From cdfb178bacd5519248c843522b3d8a013e36f623 Mon Sep 17 00:00:00 2001
From: murilo09 <78226931+murilo09@users.noreply.github.com>
Date: Tue, 31 Dec 2024 13:03:35 -0300
Subject: [PATCH 01/32] feat: cyclopedia house auction (#3022)
---
config.lua.dist | 4 +-
data-otservbr-global/migrations/10.lua | 2 +-
data-otservbr-global/migrations/11.lua | 2 +-
data-otservbr-global/migrations/12.lua | 2 +-
data-otservbr-global/migrations/13.lua | 2 +-
data-otservbr-global/migrations/14.lua | 2 +-
data-otservbr-global/migrations/15.lua | 2 +-
data-otservbr-global/migrations/16.lua | 2 +-
data-otservbr-global/migrations/17.lua | 2 +-
data-otservbr-global/migrations/18.lua | 2 +-
data-otservbr-global/migrations/19.lua | 2 +-
data-otservbr-global/migrations/2.lua | 2 +-
data-otservbr-global/migrations/20.lua | 2 +-
data-otservbr-global/migrations/21.lua | 2 +-
data-otservbr-global/migrations/22.lua | 2 +-
data-otservbr-global/migrations/23.lua | 2 +-
data-otservbr-global/migrations/24.lua | 2 +-
data-otservbr-global/migrations/25.lua | 2 +-
data-otservbr-global/migrations/26.lua | 2 +-
data-otservbr-global/migrations/27.lua | 2 +-
data-otservbr-global/migrations/28.lua | 2 +-
data-otservbr-global/migrations/29.lua | 2 +-
data-otservbr-global/migrations/3.lua | 2 +-
data-otservbr-global/migrations/30.lua | 2 +-
data-otservbr-global/migrations/31.lua | 2 +-
data-otservbr-global/migrations/32.lua | 2 +-
data-otservbr-global/migrations/33.lua | 2 +-
data-otservbr-global/migrations/34.lua | 2 +-
data-otservbr-global/migrations/35.lua | 2 +-
data-otservbr-global/migrations/36.lua | 2 +-
data-otservbr-global/migrations/37.lua | 2 +-
data-otservbr-global/migrations/38.lua | 2 +-
data-otservbr-global/migrations/39.lua | 2 +-
data-otservbr-global/migrations/4.lua | 2 +-
data-otservbr-global/migrations/40.lua | 2 +-
data-otservbr-global/migrations/41.lua | 2 +-
data-otservbr-global/migrations/42.lua | 2 +-
data-otservbr-global/migrations/43.lua | 2 +-
data-otservbr-global/migrations/44.lua | 2 +-
data-otservbr-global/migrations/45.lua | 2 +-
data-otservbr-global/migrations/46.lua | 2 +-
data-otservbr-global/migrations/47.lua | 2 +-
data-otservbr-global/migrations/48.lua | 27 +
data-otservbr-global/migrations/5.lua | 2 +-
data-otservbr-global/migrations/6.lua | 2 +-
data-otservbr-global/migrations/7.lua | 2 +-
data-otservbr-global/migrations/8.lua | 53 +-
data-otservbr-global/migrations/9.lua | 2 +-
data-otservbr-global/world/otservbr-house.xml | 1970 ++++++++---------
.../globalevents/server_initialization.lua | 24 -
data/scripts/talkactions/player/buy_house.lua | 8 +-
.../talkactions/player/leave_house.lua | 8 +-
.../scripts/talkactions/player/sell_house.lua | 8 +-
schema.sql | 14 +-
src/account/account.cpp | 7 +
src/account/account.hpp | 3 +
src/account/account_info.hpp | 1 +
src/account/account_repository_db.cpp | 3 +-
src/config/config_enums.hpp | 2 +
src/config/configmanager.cpp | 2 +
src/creatures/players/player.cpp | 122 +
src/creatures/players/player.hpp | 13 +
src/enums/player_cyclopedia.hpp | 58 +
src/game/game.cpp | 351 +++
src/game/game.hpp | 11 +
src/io/iologindata.cpp | 8 -
src/io/iologindata.hpp | 1 -
src/io/iomapserialize.cpp | 89 +-
src/lua/functions/map/house_functions.cpp | 3 +-
src/map/house/house.cpp | 103 +-
src/map/house/house.hpp | 113 +
src/server/network/protocol/protocolgame.cpp | 194 ++
src/server/network/protocol/protocolgame.hpp | 7 +
73 files changed, 2160 insertions(+), 1137 deletions(-)
create mode 100644 data-otservbr-global/migrations/48.lua
diff --git a/config.lua.dist b/config.lua.dist
index 7d0360d9360..7c891c84b5d 100644
--- a/config.lua.dist
+++ b/config.lua.dist
@@ -344,9 +344,11 @@ Setting this to false may pose risks; if a house is abandoned and contains a lar
]]
-- Periods: daily/weekly/monthly/yearly/never
-- Base: sqm,rent,sqm+rent
+toggleCyclopediaHouseAuction = true
+daysToCloseBid = 7
housePriceRentMultiplier = 0.0
housePriceEachSQM = 1000
-houseRentPeriod = "never"
+houseRentPeriod = "monthly"
houseRentRate = 1.0
houseOwnedByAccount = false
houseBuyLevel = 100
diff --git a/data-otservbr-global/migrations/10.lua b/data-otservbr-global/migrations/10.lua
index 9dfded3813d..adfa41bb535 100644
--- a/data-otservbr-global/migrations/10.lua
+++ b/data-otservbr-global/migrations/10.lua
@@ -1,4 +1,4 @@
function onUpdateDatabase()
- logger.info("Updating database to version 11 (Guilds Balance)")
+ logger.info("Updating database to version 10 (Guilds Balance)")
db.query("ALTER TABLE `guilds` ADD `balance` bigint(20) UNSIGNED NOT NULL DEFAULT '0';")
end
diff --git a/data-otservbr-global/migrations/11.lua b/data-otservbr-global/migrations/11.lua
index 08d40b66381..a10d3d2980c 100644
--- a/data-otservbr-global/migrations/11.lua
+++ b/data-otservbr-global/migrations/11.lua
@@ -1,4 +1,4 @@
function onUpdateDatabase()
- logger.info("Updating database to version 12 (Player get daily reward)")
+ logger.info("Updating database to version 11 (Player get daily reward)")
db.query("ALTER TABLE `players` ADD `isreward` tinyint(1) NOT NULL DEFAULT 1")
end
diff --git a/data-otservbr-global/migrations/12.lua b/data-otservbr-global/migrations/12.lua
index e83ca4e51f8..20a2afc4582 100644
--- a/data-otservbr-global/migrations/12.lua
+++ b/data-otservbr-global/migrations/12.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 13 (Boosted Creature Outfit)")
+ logger.info("Updating database to version 12 (Boosted Creature Outfit)")
db.query("ALTER TABLE boosted_creature ADD `looktype` int(11) NOT NULL DEFAULT 136;")
db.query("ALTER TABLE boosted_creature ADD `lookfeet` int(11) NOT NULL DEFAULT 0;")
db.query("ALTER TABLE boosted_creature ADD `looklegs` int(11) NOT NULL DEFAULT 0;")
diff --git a/data-otservbr-global/migrations/13.lua b/data-otservbr-global/migrations/13.lua
index 479b28eda79..4747efdcb96 100644
--- a/data-otservbr-global/migrations/13.lua
+++ b/data-otservbr-global/migrations/13.lua
@@ -1,4 +1,4 @@
function onUpdateDatabase()
- logger.info("Updating database to version 14 (Fixed mana spent)")
+ logger.info("Updating database to version 13 (Fixed mana spent)")
db.query("ALTER TABLE `players` CHANGE `manaspent` `manaspent` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0';")
end
diff --git a/data-otservbr-global/migrations/14.lua b/data-otservbr-global/migrations/14.lua
index 7c23d8053b5..281eb40722a 100644
--- a/data-otservbr-global/migrations/14.lua
+++ b/data-otservbr-global/migrations/14.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 15 (Magic Shield Spell)")
+ logger.info("Updating database to version 14 (Magic Shield Spell)")
db.query("ALTER TABLE `players` ADD `manashield` SMALLINT UNSIGNED NOT NULL DEFAULT '0' AFTER `skill_manaleech_amount`")
db.query("ALTER TABLE `players` ADD `max_manashield` SMALLINT UNSIGNED NOT NULL DEFAULT '0' AFTER `manashield`")
end
diff --git a/data-otservbr-global/migrations/15.lua b/data-otservbr-global/migrations/15.lua
index 73daf3c5b31..2c4a37ba315 100644
--- a/data-otservbr-global/migrations/15.lua
+++ b/data-otservbr-global/migrations/15.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 16 (Rook sample and GOD player values)")
+ logger.info("Updating database to version 15 (Rook sample and GOD player values)")
-- Rook Sample
db.query("UPDATE `players` SET `maglevel` = 2, `manaspent` = 5936, `skill_club` = 12, `skill_club_tries` = 155, `skill_sword` = 12, `skill_sword_tries` = 155, `skill_axe` = 12, `skill_axe_tries` = 155, `skill_dist` = 12, `skill_dist_tries` = 93 WHERE `id` = 1;")
-- GOD
diff --git a/data-otservbr-global/migrations/16.lua b/data-otservbr-global/migrations/16.lua
index a5766130bc3..027f2fe9822 100644
--- a/data-otservbr-global/migrations/16.lua
+++ b/data-otservbr-global/migrations/16.lua
@@ -1,4 +1,4 @@
function onUpdateDatabase()
- print("Updating database to version 17 (Tutorial support)")
+ print("Updating database to version 16 (Tutorial support)")
db.query("ALTER TABLE `players` ADD `istutorial` SMALLINT(1) NOT NULL DEFAULT '0'")
end
diff --git a/data-otservbr-global/migrations/17.lua b/data-otservbr-global/migrations/17.lua
index 9d5f0d8d624..c84d6ec8ea6 100644
--- a/data-otservbr-global/migrations/17.lua
+++ b/data-otservbr-global/migrations/17.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 18 (Fix guild creation myaac)")
+ logger.info("Updating database to version 17 (Fix guild creation myaac)")
db.query("ALTER TABLE `guilds` ADD `level` int(11) NOT NULL DEFAULT 1")
db.query("ALTER TABLE `guilds` ADD `points` int(11) NOT NULL DEFAULT 0")
end
diff --git a/data-otservbr-global/migrations/18.lua b/data-otservbr-global/migrations/18.lua
index e017b86e05b..0f5777a2678 100644
--- a/data-otservbr-global/migrations/18.lua
+++ b/data-otservbr-global/migrations/18.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 19 (Prey system rework + Task hunting system)")
+ logger.info("Updating database to version 18 (Prey system rework + Task hunting system)")
db.query([[
ALTER TABLE `players`
DROP `prey_stamina_1`,
diff --git a/data-otservbr-global/migrations/19.lua b/data-otservbr-global/migrations/19.lua
index e7d27a859ef..dd0c82d075f 100644
--- a/data-otservbr-global/migrations/19.lua
+++ b/data-otservbr-global/migrations/19.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 20 (Gamestore accepting Tournament Coins)")
+ logger.info("Updating database to version 19 (Gamestore accepting Tournament Coins)")
db.query("ALTER TABLE `accounts` ADD `tournament_coins` int(11) NOT NULL DEFAULT 0 AFTER `coins`")
db.query("ALTER TABLE `store_history` ADD `coin_type` tinyint(1) NOT NULL DEFAULT 0 AFTER `description`")
diff --git a/data-otservbr-global/migrations/2.lua b/data-otservbr-global/migrations/2.lua
index 72c797b4b0e..b5674fecacc 100644
--- a/data-otservbr-global/migrations/2.lua
+++ b/data-otservbr-global/migrations/2.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 3 (account refactor)")
+ logger.info("Updating database to version 2 (account refactor)")
db.query([[
LOCK TABLES
diff --git a/data-otservbr-global/migrations/20.lua b/data-otservbr-global/migrations/20.lua
index daaefc6f041..f232bf2a7e8 100644
--- a/data-otservbr-global/migrations/20.lua
+++ b/data-otservbr-global/migrations/20.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 21 (Fix market price size)")
+ logger.info("Updating database to version 20 (Fix market price size)")
db.query("ALTER TABLE `market_history` CHANGE `price` `price` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0';")
db.query("ALTER TABLE `market_offers` CHANGE `price` `price` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0';")
end
diff --git a/data-otservbr-global/migrations/21.lua b/data-otservbr-global/migrations/21.lua
index cec635ed937..5c9e1a0ec8e 100644
--- a/data-otservbr-global/migrations/21.lua
+++ b/data-otservbr-global/migrations/21.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 22 (forge and tier system)")
+ logger.info("Updating database to version 21 (forge and tier system)")
db.query("ALTER TABLE `market_offers` ADD `tier` tinyint UNSIGNED NOT NULL DEFAULT '0';")
db.query("ALTER TABLE `market_history` ADD `tier` tinyint UNSIGNED NOT NULL DEFAULT '0';")
db.query("ALTER TABLE `players` ADD `forge_dusts` bigint(21) NOT NULL DEFAULT '0';")
diff --git a/data-otservbr-global/migrations/22.lua b/data-otservbr-global/migrations/22.lua
index c4c5bd385bc..9a2a4475a4b 100644
--- a/data-otservbr-global/migrations/22.lua
+++ b/data-otservbr-global/migrations/22.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 23 (fix offline training skill size)")
+ logger.info("Updating database to version 22 (fix offline training skill size)")
db.query([[
ALTER TABLE `players`
MODIFY offlinetraining_skill tinyint(2) NOT NULL DEFAULT '-1';
diff --git a/data-otservbr-global/migrations/23.lua b/data-otservbr-global/migrations/23.lua
index 8edac8cef47..dbf161bb474 100644
--- a/data-otservbr-global/migrations/23.lua
+++ b/data-otservbr-global/migrations/23.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 24 (forge history)")
+ logger.info("Updating database to version 23 (forge history)")
db.query([[
CREATE TABLE IF NOT EXISTS `forge_history` (
`id` int NOT NULL AUTO_INCREMENT,
diff --git a/data-otservbr-global/migrations/24.lua b/data-otservbr-global/migrations/24.lua
index fed9f189085..2d5286e5608 100644
--- a/data-otservbr-global/migrations/24.lua
+++ b/data-otservbr-global/migrations/24.lua
@@ -1,4 +1,4 @@
function onUpdateDatabase()
- logger.info("Updating database to version 25 (random mount outfit window)")
+ logger.info("Updating database to version 24 (random mount outfit window)")
db.query("ALTER TABLE `players` ADD `randomize_mount` SMALLINT(1) NOT NULL DEFAULT '0'")
end
diff --git a/data-otservbr-global/migrations/25.lua b/data-otservbr-global/migrations/25.lua
index 4d229bb58e5..41f83e2b72b 100644
--- a/data-otservbr-global/migrations/25.lua
+++ b/data-otservbr-global/migrations/25.lua
@@ -1,4 +1,4 @@
function onUpdateDatabase()
- logger.info("Updating database to version 26 (reward bag fix)")
+ logger.info("Updating database to version 25 (reward bag fix)")
db.query("UPDATE player_rewards SET pid = 0 WHERE itemtype = 19202;")
end
diff --git a/data-otservbr-global/migrations/26.lua b/data-otservbr-global/migrations/26.lua
index ddf821ca5cd..24db966339f 100644
--- a/data-otservbr-global/migrations/26.lua
+++ b/data-otservbr-global/migrations/26.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 27 (towns)")
+ logger.info("Updating database to version 26 (towns)")
db.query([[
CREATE TABLE IF NOT EXISTS `towns` (
diff --git a/data-otservbr-global/migrations/27.lua b/data-otservbr-global/migrations/27.lua
index 73e7bf5c4f7..478e6da6207 100644
--- a/data-otservbr-global/migrations/27.lua
+++ b/data-otservbr-global/migrations/27.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 28 (bosstiary system)")
+ logger.info("Updating database to version 27 (bosstiary system)")
db.query("ALTER TABLE `players` ADD `boss_points` int NOT NULL DEFAULT '0';")
db.query([[
CREATE TABLE IF NOT EXISTS `boosted_boss` (
diff --git a/data-otservbr-global/migrations/28.lua b/data-otservbr-global/migrations/28.lua
index d7575edf3c8..06adece4d49 100644
--- a/data-otservbr-global/migrations/28.lua
+++ b/data-otservbr-global/migrations/28.lua
@@ -1,4 +1,4 @@
function onUpdateDatabase()
- logger.info("Updating database to version 29 (transfer coins)")
+ logger.info("Updating database to version 28 (transfer coins)")
db.query("ALTER TABLE `accounts` ADD `coins_transferable` int unsigned NOT NULL DEFAULT '0';")
end
diff --git a/data-otservbr-global/migrations/29.lua b/data-otservbr-global/migrations/29.lua
index 0c633e46b5d..91834f4f2f6 100644
--- a/data-otservbr-global/migrations/29.lua
+++ b/data-otservbr-global/migrations/29.lua
@@ -1,4 +1,4 @@
function onUpdateDatabase()
- logger.info("Updating database to version 30 (looktypeEx)")
+ logger.info("Updating database to version 29 (looktypeEx)")
db.query("ALTER TABLE `boosted_boss` ADD `looktypeEx` int unsigned NOT NULL DEFAULT '0';")
end
diff --git a/data-otservbr-global/migrations/3.lua b/data-otservbr-global/migrations/3.lua
index ae06343be07..0a1aec6f6df 100644
--- a/data-otservbr-global/migrations/3.lua
+++ b/data-otservbr-global/migrations/3.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 4 (prey tick)")
+ logger.info("Updating database to version 3 (prey tick)")
db.query([[
ALTER TABLE `prey_slots`
diff --git a/data-otservbr-global/migrations/30.lua b/data-otservbr-global/migrations/30.lua
index 4ee1632421d..4749f1588ac 100644
--- a/data-otservbr-global/migrations/30.lua
+++ b/data-otservbr-global/migrations/30.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 31 (loyalty)")
+ logger.info("Updating database to version 30 (loyalty)")
db.query([[
ALTER TABLE `accounts` ADD COLUMN `premdays_purchased` int(11) NOT NULL DEFAULT 0;
]])
diff --git a/data-otservbr-global/migrations/31.lua b/data-otservbr-global/migrations/31.lua
index 5ba21bbe561..9659f296ac5 100644
--- a/data-otservbr-global/migrations/31.lua
+++ b/data-otservbr-global/migrations/31.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 32 (account_sessions)")
+ logger.info("Updating database to version 31 (account_sessions)")
db.query([[
CREATE TABLE IF NOT EXISTS `account_sessions` (
`id` VARCHAR(191) NOT NULL,
diff --git a/data-otservbr-global/migrations/32.lua b/data-otservbr-global/migrations/32.lua
index 078ef407da6..c90de61886b 100644
--- a/data-otservbr-global/migrations/32.lua
+++ b/data-otservbr-global/migrations/32.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 33 (wheel of destiny)")
+ logger.info("Updating database to version 32 (wheel of destiny)")
db.query([[
CREATE TABLE IF NOT EXISTS `player_wheeldata` (
`player_id` int(11) NOT NULL,
diff --git a/data-otservbr-global/migrations/33.lua b/data-otservbr-global/migrations/33.lua
index 2c77cbb6e24..7c0852a32bc 100644
--- a/data-otservbr-global/migrations/33.lua
+++ b/data-otservbr-global/migrations/33.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 34 (add primary keys)")
+ logger.info("Updating database to version 33 (add primary keys)")
db.query([[
ALTER TABLE `player_prey`
ADD PRIMARY KEY (`player_id`, `slot`);
diff --git a/data-otservbr-global/migrations/34.lua b/data-otservbr-global/migrations/34.lua
index 7537f6e6582..c344eae6b8d 100644
--- a/data-otservbr-global/migrations/34.lua
+++ b/data-otservbr-global/migrations/34.lua
@@ -1,4 +1,4 @@
function onUpdateDatabase()
- logger.info("Updating database to version 35 (bosstiary tracker)")
+ logger.info("Updating database to version 34 (bosstiary tracker)")
db.query("ALTER TABLE `player_bosstiary` ADD `tracker` blob NOT NULL;")
end
diff --git a/data-otservbr-global/migrations/35.lua b/data-otservbr-global/migrations/35.lua
index 70c820c32fd..9e2ab4dd1ba 100644
--- a/data-otservbr-global/migrations/35.lua
+++ b/data-otservbr-global/migrations/35.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 36 (fix account premdays and lastday)")
+ logger.info("Updating database to version 35 (fix account premdays and lastday)")
local resultQuery = db.storeQuery("SELECT `id`, `premdays`, `lastday` FROM `accounts` WHERE (`premdays` > 0 OR `lastday` > 0) AND `lastday` <= " .. os.time())
if resultQuery ~= false then
diff --git a/data-otservbr-global/migrations/36.lua b/data-otservbr-global/migrations/36.lua
index 5f912763cc8..5fab551e4c0 100644
--- a/data-otservbr-global/migrations/36.lua
+++ b/data-otservbr-global/migrations/36.lua
@@ -1,4 +1,4 @@
function onUpdateDatabase()
- logger.info("Updating database to version 37 (add coin_type to accounts)")
+ logger.info("Updating database to version 36 (add coin_type to accounts)")
db.query("ALTER TABLE `coins_transactions` ADD `coin_type` tinyint(1) UNSIGNED NOT NULL DEFAULT '1';")
end
diff --git a/data-otservbr-global/migrations/37.lua b/data-otservbr-global/migrations/37.lua
index ae3dbefbc27..7219dfbbac2 100644
--- a/data-otservbr-global/migrations/37.lua
+++ b/data-otservbr-global/migrations/37.lua
@@ -1,4 +1,4 @@
function onUpdateDatabase()
- logger.info("Updating database to version 38 (add pronoun to players)")
+ logger.info("Updating database to version 37 (add pronoun to players)")
db.query("ALTER TABLE `players` ADD `pronoun` int(11) NOT NULL DEFAULT '0';")
end
diff --git a/data-otservbr-global/migrations/38.lua b/data-otservbr-global/migrations/38.lua
index 3412e4d488f..7e9e3748175 100644
--- a/data-otservbr-global/migrations/38.lua
+++ b/data-otservbr-global/migrations/38.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 39 (create kv store)")
+ logger.info("Updating database to version 38 (create kv store)")
db.query([[
CREATE TABLE IF NOT EXISTS `kv_store` (
`key_name` varchar(191) NOT NULL,
diff --git a/data-otservbr-global/migrations/39.lua b/data-otservbr-global/migrations/39.lua
index 181994882db..f660e98eb37 100644
--- a/data-otservbr-global/migrations/39.lua
+++ b/data-otservbr-global/migrations/39.lua
@@ -1,4 +1,4 @@
function onUpdateDatabase()
- logger.info("Updating database to version 40 (house transfer ownership on startup)")
+ logger.info("Updating database to version 39 (house transfer ownership on startup)")
db.query("ALTER TABLE `houses` ADD `new_owner` int(11) NOT NULL DEFAULT '-1';")
end
diff --git a/data-otservbr-global/migrations/4.lua b/data-otservbr-global/migrations/4.lua
index 891bc20915a..a7b04453374 100644
--- a/data-otservbr-global/migrations/4.lua
+++ b/data-otservbr-global/migrations/4.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 5 (boosted creature)")
+ logger.info("Updating database to version 4 (boosted creature)")
db.query([[CREATE TABLE IF NOT EXISTS `boosted_creature` (
`boostname` TEXT,
`date` varchar(250) NOT NULL DEFAULT '',
diff --git a/data-otservbr-global/migrations/40.lua b/data-otservbr-global/migrations/40.lua
index be8794e7b25..a7d3ae6afc2 100644
--- a/data-otservbr-global/migrations/40.lua
+++ b/data-otservbr-global/migrations/40.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 41 (optimize house_lists)")
+ logger.info("Updating database to version 40 (optimize house_lists)")
db.query([[
ALTER TABLE `house_lists`
diff --git a/data-otservbr-global/migrations/41.lua b/data-otservbr-global/migrations/41.lua
index 1fa9a40e36d..504bd950073 100644
--- a/data-otservbr-global/migrations/41.lua
+++ b/data-otservbr-global/migrations/41.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 42 (fix xpboost types)")
+ logger.info("Updating database to version 41 (fix xpboost types)")
db.query([[
ALTER TABLE `players`
diff --git a/data-otservbr-global/migrations/42.lua b/data-otservbr-global/migrations/42.lua
index 4b0b97b9987..6bc750efa66 100644
--- a/data-otservbr-global/migrations/42.lua
+++ b/data-otservbr-global/migrations/42.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 43 (fix guildwar_kills_unique)")
+ logger.info("Updating database to version 42 (fix guildwar_kills_unique)")
db.query([[
ALTER TABLE `guildwar_kills`
diff --git a/data-otservbr-global/migrations/43.lua b/data-otservbr-global/migrations/43.lua
index 438ba91b713..bcf1658864d 100644
--- a/data-otservbr-global/migrations/43.lua
+++ b/data-otservbr-global/migrations/43.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 44 (feat frags_limit, payment and duration_days in guild wars)")
+ logger.info("Updating database to version 43 (feat frags_limit, payment and duration_days in guild wars)")
db.query([[
ALTER TABLE `guild_wars`
diff --git a/data-otservbr-global/migrations/44.lua b/data-otservbr-global/migrations/44.lua
index 0e2140c3183..acef11ceed9 100644
--- a/data-otservbr-global/migrations/44.lua
+++ b/data-otservbr-global/migrations/44.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 45 (fix: mana shield column size for more than 65k)")
+ logger.info("Updating database to version 44 (fix: mana shield column size for more than 65k)")
db.query([[
ALTER TABLE `players`
diff --git a/data-otservbr-global/migrations/45.lua b/data-otservbr-global/migrations/45.lua
index a88de886861..abed3464072 100644
--- a/data-otservbr-global/migrations/45.lua
+++ b/data-otservbr-global/migrations/45.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 46 (feat: vip groups)")
+ logger.info("Updating database to version 45 (feat: vip groups)")
db.query([[
CREATE TABLE IF NOT EXISTS `account_vipgroups` (
diff --git a/data-otservbr-global/migrations/46.lua b/data-otservbr-global/migrations/46.lua
index da4ade8cbf3..d7f24765b8a 100644
--- a/data-otservbr-global/migrations/46.lua
+++ b/data-otservbr-global/migrations/46.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 47 (fix: creature speed and conditions)")
+ logger.info("Updating database to version 46 (fix: creature speed and conditions)")
db.query("ALTER TABLE `players` MODIFY `conditions` mediumblob NOT NULL;")
end
diff --git a/data-otservbr-global/migrations/47.lua b/data-otservbr-global/migrations/47.lua
index 3c8908b5641..6b658e4085f 100644
--- a/data-otservbr-global/migrations/47.lua
+++ b/data-otservbr-global/migrations/47.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 46 (hireling)")
+ logger.info("Updating database to version 47 (hireling)")
db.query([[
CREATE TABLE IF NOT EXISTS `player_hirelings` (
diff --git a/data-otservbr-global/migrations/48.lua b/data-otservbr-global/migrations/48.lua
new file mode 100644
index 00000000000..53d6ba3a948
--- /dev/null
+++ b/data-otservbr-global/migrations/48.lua
@@ -0,0 +1,27 @@
+function onUpdateDatabase()
+ logger.info("Updating database to version 48 (House Auction)")
+
+ db.query([[
+ ALTER TABLE `houses`
+ DROP `bid`,
+ DROP `bid_end`,
+ DROP `last_bid`,
+ DROP `highest_bidder`
+ ]])
+
+ db.query([[
+ ALTER TABLE `houses`
+ ADD `bidder` int(11) NOT NULL DEFAULT '0',
+ ADD `bidder_name` varchar(255) NOT NULL DEFAULT '',
+ ADD `highest_bid` int(11) NOT NULL DEFAULT '0',
+ ADD `internal_bid` int(11) NOT NULL DEFAULT '0',
+ ADD `bid_end_date` int(11) NOT NULL DEFAULT '0',
+ ADD `state` smallint(5) UNSIGNED NOT NULL DEFAULT '0',
+ ADD `transfer_status` tinyint(1) DEFAULT '0'
+ ]])
+
+ db.query([[
+ ALTER TABLE `accounts`
+ ADD `house_bid_id` int(11) NOT NULL DEFAULT '0'
+ ]])
+end
diff --git a/data-otservbr-global/migrations/5.lua b/data-otservbr-global/migrations/5.lua
index dbc324198dd..4b027c20085 100644
--- a/data-otservbr-global/migrations/5.lua
+++ b/data-otservbr-global/migrations/5.lua
@@ -1,4 +1,4 @@
function onUpdateDatabase()
- logger.info("Updating database to version 6 (quickloot)")
+ logger.info("Updating database to version 5 (quickloot)")
db.query("ALTER TABLE `players` ADD `quickloot_fallback` TINYINT DEFAULT 0")
end
diff --git a/data-otservbr-global/migrations/6.lua b/data-otservbr-global/migrations/6.lua
index 91766a68ca7..cc3a3f76423 100644
--- a/data-otservbr-global/migrations/6.lua
+++ b/data-otservbr-global/migrations/6.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 7 (Stash supply)")
+ logger.info("Updating database to version 6 (Stash supply)")
db.query([[CREATE TABLE IF NOT EXISTS `player_stash` (
`player_id` INT(16) NOT NULL,
`item_id` INT(16) NOT NULL,
diff --git a/data-otservbr-global/migrations/7.lua b/data-otservbr-global/migrations/7.lua
index cade1faae1f..0666d7cf9d8 100644
--- a/data-otservbr-global/migrations/7.lua
+++ b/data-otservbr-global/migrations/7.lua
@@ -1,4 +1,4 @@
function onUpdateDatabase()
- logger.info("Updating database to version 8 (recruiter system)")
+ logger.info("Updating database to version 7 (recruiter system)")
db.query("ALTER TABLE `accounts` ADD `recruiter` INT(6) DEFAULT 0")
end
diff --git a/data-otservbr-global/migrations/8.lua b/data-otservbr-global/migrations/8.lua
index 523b705c0c7..a96db82e278 100644
--- a/data-otservbr-global/migrations/8.lua
+++ b/data-otservbr-global/migrations/8.lua
@@ -1,29 +1,30 @@
function onUpdateDatabase()
- logger.info("Updating database to version 9 (Bestiary cpp)")
+ logger.info("Updating database to version 8 (Bestiary cpp)")
db.query([[CREATE TABLE IF NOT EXISTS `player_charms` (
-`player_guid` INT(250) NOT NULL ,
-`charm_points` VARCHAR(250) NULL ,
-`charm_expansion` BOOLEAN NULL ,
-`rune_wound` INT(250) NULL ,
-`rune_enflame` INT(250) NULL ,
-`rune_poison` INT(250) NULL ,
-`rune_freeze` INT(250) NULL ,
-`rune_zap` INT(250) NULL ,
-`rune_curse` INT(250) NULL ,
-`rune_cripple` INT(250) NULL ,
-`rune_parry` INT(250) NULL ,
-`rune_dodge` INT(250) NULL ,
-`rune_adrenaline` INT(250) NULL ,
-`rune_numb` INT(250) NULL,
-`rune_cleanse` INT(250) NULL ,
-`rune_bless` INT(250) NULL ,
-`rune_scavenge` INT(250) NULL ,
-`rune_gut` INT(250) NULL ,
-`rune_low_blow` INT(250) NULL ,
-`rune_divine` INT(250) NULL ,
-`rune_vamp` INT(250) NULL ,
-`rune_void` INT(250) NULL ,
-`UsedRunesBit` VARCHAR(250) NULL ,
-`UnlockedRunesBit` VARCHAR(250) NULL,
-`tracker list` BLOB NULL ) ENGINE = InnoDB DEFAULT CHARSET=utf8;]])
+ `player_guid` INT(250) NOT NULL ,
+ `charm_points` VARCHAR(250) NULL ,
+ `charm_expansion` BOOLEAN NULL ,
+ `rune_wound` INT(250) NULL ,
+ `rune_enflame` INT(250) NULL ,
+ `rune_poison` INT(250) NULL ,
+ `rune_freeze` INT(250) NULL ,
+ `rune_zap` INT(250) NULL ,
+ `rune_curse` INT(250) NULL ,
+ `rune_cripple` INT(250) NULL ,
+ `rune_parry` INT(250) NULL ,
+ `rune_dodge` INT(250) NULL ,
+ `rune_adrenaline` INT(250) NULL ,
+ `rune_numb` INT(250) NULL,
+ `rune_cleanse` INT(250) NULL ,
+ `rune_bless` INT(250) NULL ,
+ `rune_scavenge` INT(250) NULL ,
+ `rune_gut` INT(250) NULL ,
+ `rune_low_blow` INT(250) NULL ,
+ `rune_divine` INT(250) NULL ,
+ `rune_vamp` INT(250) NULL ,
+ `rune_void` INT(250) NULL ,
+ `UsedRunesBit` VARCHAR(250) NULL ,
+ `UnlockedRunesBit` VARCHAR(250) NULL,
+ `tracker list` BLOB NULL ) ENGINE = InnoDB DEFAULT CHARSET=utf8;
+ ]])
end
diff --git a/data-otservbr-global/migrations/9.lua b/data-otservbr-global/migrations/9.lua
index 7ce8e189768..23516833fbb 100644
--- a/data-otservbr-global/migrations/9.lua
+++ b/data-otservbr-global/migrations/9.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 10 (Mount Colors and familiars)")
+ logger.info("Updating database to version 9 (Mount Colors and familiars)")
db.query("ALTER TABLE `players` ADD `lookmountbody` tinyint(3) unsigned NOT NULL DEFAULT '0'")
db.query("ALTER TABLE `players` ADD `lookmountfeet` tinyint(3) unsigned NOT NULL DEFAULT '0'")
db.query("ALTER TABLE `players` ADD `lookmounthead` tinyint(3) unsigned NOT NULL DEFAULT '0'")
diff --git a/data-otservbr-global/world/otservbr-house.xml b/data-otservbr-global/world/otservbr-house.xml
index 7eff23b4606..bedef70ff1f 100644
--- a/data-otservbr-global/world/otservbr-house.xml
+++ b/data-otservbr-global/world/otservbr-house.xml
@@ -1,987 +1,987 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/data/scripts/globalevents/server_initialization.lua b/data/scripts/globalevents/server_initialization.lua
index df29660d373..a58cf01d3a2 100644
--- a/data/scripts/globalevents/server_initialization.lua
+++ b/data/scripts/globalevents/server_initialization.lua
@@ -27,29 +27,6 @@ local function moveExpiredBansToHistory()
end
end
--- Function to check and process house auctions
-local function processHouseAuctions()
- local resultId = db.storeQuery("SELECT `id`, `highest_bidder`, `last_bid`, " .. "(SELECT `balance` FROM `players` WHERE `players`.`id` = `highest_bidder`) AS `balance` " .. "FROM `houses` WHERE `owner` = 0 AND `bid_end` != 0 AND `bid_end` < " .. os.time())
- if resultId then
- repeat
- local house = House(Result.getNumber(resultId, "id"))
- if house then
- local highestBidder = Result.getNumber(resultId, "highest_bidder")
- local balance = Result.getNumber(resultId, "balance")
- local lastBid = Result.getNumber(resultId, "last_bid")
- if balance >= lastBid then
- db.query("UPDATE `players` SET `balance` = " .. (balance - lastBid) .. " WHERE `id` = " .. highestBidder)
- house:setHouseOwner(highestBidder)
- end
-
- db.asyncQuery("UPDATE `houses` SET `last_bid` = 0, `bid_end` = 0, `highest_bidder` = 0, `bid` = 0 " .. "WHERE `id` = " .. house:getId())
- end
- until not Result.next(resultId)
-
- Result.free(resultId)
- end
-end
-
-- Function to store towns in the database
local function storeTownsInDatabase()
db.query("TRUNCATE TABLE `towns`")
@@ -150,7 +127,6 @@ function serverInitialization.onStartup()
cleanupDatabase()
moveExpiredBansToHistory()
- processHouseAuctions()
storeTownsInDatabase()
checkAndLogDuplicateValues({ "Global", "GlobalStorage", "Storage" })
updateEventRates()
diff --git a/data/scripts/talkactions/player/buy_house.lua b/data/scripts/talkactions/player/buy_house.lua
index c3784d81a6b..84d3a34aad0 100644
--- a/data/scripts/talkactions/player/buy_house.lua
+++ b/data/scripts/talkactions/player/buy_house.lua
@@ -60,6 +60,8 @@ function buyHouse.onSay(player, words, param)
return true
end
-buyHouse:separator(" ")
-buyHouse:groupType("normal")
-buyHouse:register()
+if not configManager.getBoolean(configKeys.CYCLOPEDIA_HOUSE_AUCTION) then
+ buyHouse:separator(" ")
+ buyHouse:groupType("normal")
+ buyHouse:register()
+end
diff --git a/data/scripts/talkactions/player/leave_house.lua b/data/scripts/talkactions/player/leave_house.lua
index 20ad186f2d2..d954eb1dcf0 100644
--- a/data/scripts/talkactions/player/leave_house.lua
+++ b/data/scripts/talkactions/player/leave_house.lua
@@ -42,6 +42,8 @@ function leaveHouse.onSay(player, words, param)
return true
end
-leaveHouse:separator(" ")
-leaveHouse:groupType("normal")
-leaveHouse:register()
+if not configManager.getBoolean(configKeys.CYCLOPEDIA_HOUSE_AUCTION) then
+ leaveHouse:separator(" ")
+ leaveHouse:groupType("normal")
+ leaveHouse:register()
+end
diff --git a/data/scripts/talkactions/player/sell_house.lua b/data/scripts/talkactions/player/sell_house.lua
index c96cb5f71c3..dadadd066d1 100644
--- a/data/scripts/talkactions/player/sell_house.lua
+++ b/data/scripts/talkactions/player/sell_house.lua
@@ -20,6 +20,8 @@ function sellHouse.onSay(player, words, param)
return true
end
-sellHouse:separator(" ")
-sellHouse:groupType("normal")
-sellHouse:register()
+if not configManager.getBoolean(configKeys.CYCLOPEDIA_HOUSE_AUCTION) then
+ sellHouse:separator(" ")
+ sellHouse:groupType("normal")
+ sellHouse:register()
+end
diff --git a/schema.sql b/schema.sql
index 86ea9e1bf6b..6fe1f21cbb8 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', '46'), ('motd_hash', ''), ('motd_num', '0'), ('players_record', '0');
+INSERT INTO `server_config` (`config`, `value`) VALUES ('db_version', '48'), ('motd_hash', ''), ('motd_num', '0'), ('players_record', '0');
-- Table structure `accounts`
CREATE TABLE IF NOT EXISTS `accounts` (
@@ -24,6 +24,7 @@ CREATE TABLE IF NOT EXISTS `accounts` (
`tournament_coins` int(12) UNSIGNED NOT NULL DEFAULT '0',
`creation` int(11) UNSIGNED NOT NULL DEFAULT '0',
`recruiter` INT(6) DEFAULT 0,
+ `house_bid_id` int(11) NOT NULL DEFAULT '0',
CONSTRAINT `accounts_pk` PRIMARY KEY (`id`),
CONSTRAINT `accounts_unique` UNIQUE (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
@@ -451,13 +452,16 @@ CREATE TABLE IF NOT EXISTS `houses` (
`name` varchar(255) NOT NULL,
`rent` int(11) NOT NULL DEFAULT '0',
`town_id` int(11) NOT NULL DEFAULT '0',
- `bid` int(11) NOT NULL DEFAULT '0',
- `bid_end` int(11) NOT NULL DEFAULT '0',
- `last_bid` int(11) NOT NULL DEFAULT '0',
- `highest_bidder` int(11) NOT NULL DEFAULT '0',
`size` int(11) NOT NULL DEFAULT '0',
`guildid` int(11),
`beds` int(11) NOT NULL DEFAULT '0',
+ `bidder` int(11) NOT NULL DEFAULT '0',
+ `bidder_name` varchar(255) NOT NULL DEFAULT '',
+ `highest_bid` int(11) NOT NULL DEFAULT '0',
+ `internal_bid` int(11) NOT NULL DEFAULT '0',
+ `bid_end_date` int(11) NOT NULL DEFAULT '0',
+ `state` smallint(5) UNSIGNED NOT NULL DEFAULT '0',
+ `transfer_status` tinyint(1) DEFAULT '0',
INDEX `owner` (`owner`),
INDEX `town_id` (`town_id`),
CONSTRAINT `houses_pk` PRIMARY KEY (`id`)
diff --git a/src/account/account.cpp b/src/account/account.cpp
index 93596f77b15..db79f942527 100644
--- a/src/account/account.cpp
+++ b/src/account/account.cpp
@@ -300,3 +300,10 @@ uint32_t Account::getAccountAgeInDays() const {
[[nodiscard]] time_t Account::getPremiumLastDay() const {
return m_account->premiumLastDay;
}
+
+uint32_t Account::getHouseBidId() const {
+ return m_account->houseBidId;
+}
+void Account::setHouseBidId(uint32_t houseId) {
+ m_account->houseBidId = houseId;
+}
diff --git a/src/account/account.hpp b/src/account/account.hpp
index 2c6098a8dbd..0a2bcc1a2b2 100644
--- a/src/account/account.hpp
+++ b/src/account/account.hpp
@@ -119,6 +119,9 @@ class Account {
std::tuple, AccountErrors_t> getAccountPlayers() const;
+ void setHouseBidId(uint32_t houseId);
+ uint32_t getHouseBidId() const;
+
// Old protocol compat
void setProtocolCompat(bool toggle);
diff --git a/src/account/account_info.hpp b/src/account/account_info.hpp
index b9dad60dbbc..54741419ddb 100644
--- a/src/account/account_info.hpp
+++ b/src/account/account_info.hpp
@@ -28,4 +28,5 @@ struct AccountInfo {
time_t sessionExpires = 0;
uint32_t premiumDaysPurchased = 0;
uint32_t creationTime = 0;
+ uint32_t houseBidId = 0;
};
diff --git a/src/account/account_repository_db.cpp b/src/account/account_repository_db.cpp
index c3f02bfe972..b2e8fd80754 100644
--- a/src/account/account_repository_db.cpp
+++ b/src/account/account_repository_db.cpp
@@ -47,12 +47,13 @@ bool AccountRepositoryDB::loadBySession(const std::string &sessionKey, std::uniq
bool AccountRepositoryDB::save(const std::unique_ptr &accInfo) {
bool successful = g_database().executeQuery(
fmt::format(
- "UPDATE `accounts` SET `type` = {}, `premdays` = {}, `lastday` = {}, `creation` = {}, `premdays_purchased` = {} WHERE `id` = {}",
+ "UPDATE `accounts` SET `type` = {}, `premdays` = {}, `lastday` = {}, `creation` = {}, `premdays_purchased` = {}, `house_bid_id` = {} WHERE `id` = {}",
accInfo->accountType,
accInfo->premiumRemainingDays,
accInfo->premiumLastDay,
accInfo->creationTime,
accInfo->premiumDaysPurchased,
+ accInfo->houseBidId,
accInfo->id
)
);
diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp
index 559045fdb9b..65e585159e8 100644
--- a/src/config/config_enums.hpp
+++ b/src/config/config_enums.hpp
@@ -46,6 +46,7 @@ enum ConfigKey_t : uint16_t {
CONVERT_UNSAFE_SCRIPTS,
CORE_DIRECTORY,
CRITICALCHANCE,
+ CYCLOPEDIA_HOUSE_AUCTION,
DATA_DIRECTORY,
DAY_KILLS_TO_RED,
DEATH_LOSE_PERCENT,
@@ -110,6 +111,7 @@ enum ConfigKey_t : uint16_t {
HAZARD_PODS_TIME_TO_DAMAGE,
HAZARD_PODS_TIME_TO_SPAWN,
HAZARD_SPAWN_PLUNDER_MULTIPLIER,
+ DAYS_TO_CLOSE_BID,
HOUSE_BUY_LEVEL,
HOUSE_LOSE_AFTER_INACTIVITY,
HOUSE_OWNED_BY_ACCOUNT,
diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp
index 1c00df76c2f..1034a28be3e 100644
--- a/src/config/configmanager.cpp
+++ b/src/config/configmanager.cpp
@@ -157,6 +157,7 @@ bool ConfigManager::load() {
loadBoolConfig(L, VIP_SYSTEM_ENABLED, "vipSystemEnabled", false);
loadBoolConfig(L, WARN_UNSAFE_SCRIPTS, "warnUnsafeScripts", true);
loadBoolConfig(L, XP_DISPLAY_MODE, "experienceDisplayRates", true);
+ loadBoolConfig(L, CYCLOPEDIA_HOUSE_AUCTION, "toggleCyclopediaHouseAuction", true);
loadFloatConfig(L, BESTIARY_RATE_CHARM_SHOP_PRICE, "bestiaryRateCharmShopPrice", 1.0);
loadFloatConfig(L, COMBAT_CHAIN_SKILL_FORMULA_AXE, "combatChainSkillFormulaAxe", 0.9);
@@ -255,6 +256,7 @@ bool ConfigManager::load() {
loadIntConfig(L, HAZARD_PODS_TIME_TO_DAMAGE, "hazardPodsTimeToDamage", 2000);
loadIntConfig(L, HAZARD_PODS_TIME_TO_SPAWN, "hazardPodsTimeToSpawn", 4000);
loadIntConfig(L, HAZARD_SPAWN_PLUNDER_MULTIPLIER, "hazardSpawnPlunderMultiplier", 25);
+ loadIntConfig(L, DAYS_TO_CLOSE_BID, "daysToCloseBid", 7);
loadIntConfig(L, HOUSE_BUY_LEVEL, "houseBuyLevel", 0);
loadIntConfig(L, HOUSE_LOSE_AFTER_INACTIVITY, "houseLoseAfterInactivity", 0);
loadIntConfig(L, HOUSE_PRICE_PER_SQM, "housePriceEachSQM", 1000);
diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp
index 88223534497..bfbc8148d0a 100644
--- a/src/creatures/players/player.cpp
+++ b/src/creatures/players/player.cpp
@@ -37,6 +37,7 @@
#include "enums/object_category.hpp"
#include "enums/player_blessings.hpp"
#include "enums/player_icons.hpp"
+#include "enums/player_cyclopedia.hpp"
#include "game/game.hpp"
#include "game/modal_window/modal_window.hpp"
#include "game/scheduling/dispatcher.hpp"
@@ -2264,6 +2265,22 @@ void Player::sendOutfitWindow() const {
}
}
+void Player::sendCyclopediaHouseList(const HouseMap &houses) const {
+ if (client) {
+ client->sendCyclopediaHouseList(houses);
+ }
+}
+void Player::sendResourceBalance(Resource_t resourceType, uint64_t value) const {
+ if (client) {
+ client->sendResourceBalance(resourceType, value);
+ }
+}
+void Player::sendHouseAuctionMessage(uint32_t houseId, HouseAuctionType type, uint8_t index, bool bidSuccess /* = false*/) const {
+ if (client) {
+ client->sendHouseAuctionMessage(houseId, type, index, bidSuccess);
+ }
+}
+
// Imbuements
void Player::onApplyImbuement(const Imbuement* imbuement, const std::shared_ptr- &item, uint8_t slot, bool protectionCharm) {
@@ -10447,3 +10464,108 @@ uint16_t Player::getPlayerVocationEnum() const {
return Vocation_t::VOCATION_NONE;
}
+
+BidErrorMessage Player::canBidHouse(uint32_t houseId) {
+ using enum BidErrorMessage;
+ const auto house = g_game().map.houses.getHouseByClientId(houseId);
+ if (!house) {
+ return Internal;
+ }
+
+ if (getPlayerVocationEnum() == Vocation_t::VOCATION_NONE) {
+ return Rookgaard;
+ }
+
+ if (!isPremium()) {
+ return Premium;
+ }
+
+ if (getAccount()->getHouseBidId() != 0) {
+ return OnlyOneBid;
+ }
+
+ if (getBankBalance() < (house->getRent() + house->getHighestBid())) {
+ return NotEnoughMoney;
+ }
+
+ if (house->isGuildhall()) {
+ if (getGuildRank() && getGuildRank()->level != 3) {
+ return Guildhall;
+ }
+
+ if (getGuild() && getGuild()->getBankBalance() < (house->getRent() + house->getHighestBid())) {
+ return NotEnoughGuildMoney;
+ }
+ }
+
+ return NoError;
+}
+
+TransferErrorMessage Player::canTransferHouse(uint32_t houseId, uint32_t newOwnerGUID) {
+ using enum TransferErrorMessage;
+ const auto house = g_game().map.houses.getHouseByClientId(houseId);
+ if (!house) {
+ return Internal;
+ }
+
+ if (getGUID() != house->getOwner()) {
+ return NotHouseOwner;
+ }
+
+ if (getGUID() == newOwnerGUID) {
+ return AlreadyTheOwner;
+ }
+
+ const auto newOwner = g_game().getPlayerByGUID(newOwnerGUID, true);
+ if (!newOwner) {
+ return CharacterNotExist;
+ }
+
+ if (newOwner->getPlayerVocationEnum() == Vocation_t::VOCATION_NONE) {
+ return Rookgaard;
+ }
+
+ if (!newOwner->isPremium()) {
+ return Premium;
+ }
+
+ if (newOwner->getAccount()->getHouseBidId() != 0) {
+ return OnlyOneBid;
+ }
+
+ return Success;
+}
+
+AcceptTransferErrorMessage Player::canAcceptTransferHouse(uint32_t houseId) {
+ using enum AcceptTransferErrorMessage;
+ const auto house = g_game().map.houses.getHouseByClientId(houseId);
+ if (!house) {
+ return Internal;
+ }
+
+ if (getGUID() != house->getBidder()) {
+ return NotNewOwner;
+ }
+
+ if (!isPremium()) {
+ return Premium;
+ }
+
+ if (getAccount()->getHouseBidId() != 0) {
+ return AlreadyBid;
+ }
+
+ if (getPlayerVocationEnum() == Vocation_t::VOCATION_NONE) {
+ return Rookgaard;
+ }
+
+ if (getBankBalance() < (house->getRent() + house->getInternalBid())) {
+ return Frozen;
+ }
+
+ if (house->getTransferStatus()) {
+ return AlreadyAccepted;
+ }
+
+ return Success;
+}
diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp
index 6ab3be4ca7a..1b52607f2d8 100644
--- a/src/creatures/players/player.hpp
+++ b/src/creatures/players/player.hpp
@@ -64,17 +64,23 @@ struct HighscoreCharacter;
enum class PlayerIcon : uint8_t;
enum class IconBakragore : uint8_t;
+enum class HouseAuctionType : uint8_t;
+enum class BidErrorMessage : uint8_t;
+enum class TransferErrorMessage : uint8_t;
+enum class AcceptTransferErrorMessage : uint8_t;
enum ObjectCategory_t : uint8_t;
enum PreySlot_t : uint8_t;
enum SpeakClasses : uint8_t;
enum ChannelEvent_t : uint8_t;
enum SquareColor_t : uint8_t;
+enum Resource_t : uint8_t;
using GuildWarVector = std::vector;
using StashContainerList = std::vector, uint32_t>>;
using ItemVector = std::vector>;
using UsersMap = std::map>;
using InvitedMap = std::map>;
+using HouseMap = std::map>;
struct ForgeHistory {
ForgeAction_t actionType = ForgeAction_t::FUSION;
@@ -880,6 +886,13 @@ class Player final : public Creature, public Cylinder, public Bankable {
void sendOpenPrivateChannel(const std::string &receiver) const;
void sendExperienceTracker(int64_t rawExp, int64_t finalExp) const;
void sendOutfitWindow() const;
+ // House Auction
+ BidErrorMessage canBidHouse(uint32_t houseId);
+ TransferErrorMessage canTransferHouse(uint32_t houseId, uint32_t newOwnerGUID);
+ AcceptTransferErrorMessage canAcceptTransferHouse(uint32_t houseId);
+ void sendCyclopediaHouseList(const HouseMap &houses) const;
+ void sendResourceBalance(Resource_t resourceType, uint64_t value) const;
+ void sendHouseAuctionMessage(uint32_t houseId, HouseAuctionType type, uint8_t index, bool bidSuccess = false) const;
// Imbuements
void onApplyImbuement(const Imbuement* imbuement, const std::shared_ptr
- &item, uint8_t slot, bool protectionCharm);
void onClearImbuement(const std::shared_ptr
- &item, uint8_t slot);
diff --git a/src/enums/player_cyclopedia.hpp b/src/enums/player_cyclopedia.hpp
index af7ea1701ff..4d2227f8d48 100644
--- a/src/enums/player_cyclopedia.hpp
+++ b/src/enums/player_cyclopedia.hpp
@@ -60,3 +60,61 @@ enum class CyclopediaMapData_t : uint8_t {
Donations = 9,
SetCurrentArea = 10,
};
+
+enum class CyclopediaHouseState : uint8_t {
+ Available = 0,
+ Rented = 2,
+ Transfer = 3,
+ MoveOut = 4,
+};
+
+enum class HouseAuctionType : uint8_t {
+ Bid = 1,
+ MoveOut = 2,
+ Transfer = 3,
+ CancelMoveOut = 4,
+ CancelTransfer = 5,
+ AcceptTransfer = 6,
+ RejectTransfer = 7,
+};
+
+enum class BidSuccessMessage : uint8_t {
+ BidSuccess = 0,
+ LowerBid = 1,
+};
+
+enum class BidErrorMessage : uint8_t {
+ NoError = 0,
+ Rookgaard = 3,
+ Premium = 5,
+ Guildhall = 6,
+ OnlyOneBid = 7,
+ NotEnoughMoney = 17,
+ NotEnoughGuildMoney = 21,
+ Internal = 24,
+};
+
+// Bytes to:
+// Move Out, Transfer
+// Cancel Move Out/Transfer
+enum class TransferErrorMessage : uint8_t {
+ Success = 0,
+ NotHouseOwner = 2,
+ CharacterNotExist = 4,
+ Premium = 7,
+ Rookgaard = 16,
+ AlreadyTheOwner = 19,
+ OnlyOneBid = 25,
+ Internal = 32,
+};
+
+enum class AcceptTransferErrorMessage : uint8_t {
+ Success = 0,
+ NotNewOwner = 2,
+ AlreadyBid = 3,
+ AlreadyAccepted = 7,
+ Rookgaard = 8,
+ Premium = 9,
+ Frozen = 15,
+ Internal = 19,
+};
diff --git a/src/game/game.cpp b/src/game/game.cpp
index 5c7dc7aaf16..e3414f74725 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -10864,3 +10864,354 @@ void Game::updatePlayersOnline() const {
g_logger().error("[Game::updatePlayersOnline] Failed to update players online.");
}
}
+
+void Game::playerCyclopediaHousesByTown(uint32_t playerId, const std::string &townName) {
+ std::shared_ptr player = getPlayerByID(playerId);
+ if (!player) {
+ return;
+ }
+
+ HouseMap houses;
+ if (!townName.empty()) {
+ const auto &housesList = g_game().map.houses.getHouses();
+ for (const auto &it : housesList) {
+ const auto &house = it.second;
+ const auto &town = g_game().map.towns.getTown(house->getTownId());
+ if (!town) {
+ return;
+ }
+
+ const std::string &houseTown = town->getName();
+ if (houseTown == townName) {
+ houses.emplace(house->getClientId(), house);
+ }
+ }
+ } else {
+ auto playerHouses = g_game().map.houses.getAllHousesByPlayerId(player->getGUID());
+ if (playerHouses.size()) {
+ for (const auto &playerHouse : playerHouses) {
+ if (!playerHouse) {
+ continue;
+ }
+ houses.emplace(playerHouse->getClientId(), playerHouse);
+ }
+ }
+
+ const auto house = g_game().map.houses.getHouseByBidderName(player->getName());
+ if (house) {
+ houses.emplace(house->getClientId(), house);
+ }
+ }
+ player->sendCyclopediaHouseList(houses);
+}
+
+void Game::playerCyclopediaHouseBid(uint32_t playerId, uint32_t houseId, uint64_t bidValue) {
+ if (!g_configManager().getBoolean(CYCLOPEDIA_HOUSE_AUCTION)) {
+ return;
+ }
+
+ std::shared_ptr player = getPlayerByID(playerId);
+ if (!player) {
+ return;
+ }
+
+ const auto house = g_game().map.houses.getHouseByClientId(houseId);
+ if (!house) {
+ return;
+ }
+
+ auto ret = player->canBidHouse(houseId);
+ if (ret != BidErrorMessage::NoError) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::Bid, enumToValue(ret));
+ }
+ ret = BidErrorMessage::NotEnoughMoney;
+ auto retSuccess = BidSuccessMessage::BidSuccess;
+
+ if (house->getBidderName().empty()) {
+ if (!processBankAuction(player, house, bidValue)) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::Bid, enumToValue(ret));
+ return;
+ }
+ house->setHighestBid(0);
+ house->setInternalBid(bidValue);
+ house->setBidHolderLimit(bidValue);
+ house->setBidderName(player->getName());
+ house->setBidder(player->getGUID());
+ house->calculateBidEndDate(g_configManager().getNumber(DAYS_TO_CLOSE_BID));
+ } else if (house->getBidderName() == player->getName()) {
+ if (!processBankAuction(player, house, bidValue, true)) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::Bid, enumToValue(ret));
+ return;
+ }
+ house->setInternalBid(bidValue);
+ house->setBidHolderLimit(bidValue);
+ } else if (bidValue <= house->getInternalBid()) {
+ house->setHighestBid(bidValue);
+ retSuccess = BidSuccessMessage::LowerBid;
+ } else {
+ if (!processBankAuction(player, house, bidValue)) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::Bid, enumToValue(ret));
+ return;
+ }
+ house->setHighestBid(house->getInternalBid() + 1);
+ house->setInternalBid(bidValue);
+ house->setBidHolderLimit(bidValue);
+ house->setBidderName(player->getName());
+ house->setBidder(player->getGUID());
+ }
+
+ const auto &town = g_game().map.towns.getTown(house->getTownId());
+ if (!town) {
+ return;
+ }
+
+ const std::string houseTown = town->getName();
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::Bid, enumToValue(retSuccess), true);
+ playerCyclopediaHousesByTown(playerId, houseTown);
+}
+
+void Game::playerCyclopediaHouseMoveOut(uint32_t playerId, uint32_t houseId, uint32_t timestamp) {
+ if (!g_configManager().getBoolean(CYCLOPEDIA_HOUSE_AUCTION)) {
+ return;
+ }
+
+ std::shared_ptr player = getPlayerByID(playerId);
+ if (!player) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::MoveOut, enumToValue(TransferErrorMessage::Internal));
+ return;
+ }
+
+ const auto house = g_game().map.houses.getHouseByClientId(houseId);
+ if (!house || house->getState() != CyclopediaHouseState::Rented) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::MoveOut, enumToValue(TransferErrorMessage::Internal));
+ return;
+ }
+
+ if (house->getOwner() != player->getGUID()) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::MoveOut, enumToValue(TransferErrorMessage::NotHouseOwner));
+ return;
+ }
+
+ house->setBidEndDate(timestamp);
+ house->setState(CyclopediaHouseState::MoveOut);
+
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::MoveOut, enumToValue(TransferErrorMessage::Success));
+ playerCyclopediaHousesByTown(playerId, "");
+}
+
+void Game::playerCyclopediaHouseCancelMoveOut(uint32_t playerId, uint32_t houseId) {
+ if (!g_configManager().getBoolean(CYCLOPEDIA_HOUSE_AUCTION)) {
+ return;
+ }
+
+ std::shared_ptr player = getPlayerByID(playerId);
+ if (!player) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::CancelMoveOut, enumToValue(TransferErrorMessage::Internal));
+ return;
+ }
+
+ const auto house = g_game().map.houses.getHouseByClientId(houseId);
+ if (!house || house->getState() != CyclopediaHouseState::MoveOut) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::CancelMoveOut, enumToValue(TransferErrorMessage::Internal));
+ return;
+ }
+
+ if (house->getOwner() != player->getGUID()) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::CancelMoveOut, enumToValue(TransferErrorMessage::NotHouseOwner));
+ return;
+ }
+
+ house->setBidEndDate(0);
+ house->setState(CyclopediaHouseState::Rented);
+
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::CancelMoveOut, enumToValue(TransferErrorMessage::Success));
+ playerCyclopediaHousesByTown(playerId, "");
+}
+
+void Game::playerCyclopediaHouseTransfer(uint32_t playerId, uint32_t houseId, uint32_t timestamp, const std::string &newOwnerName, uint64_t bidValue) {
+ if (!g_configManager().getBoolean(CYCLOPEDIA_HOUSE_AUCTION)) {
+ return;
+ }
+
+ const std::shared_ptr &owner = getPlayerByID(playerId);
+ if (!owner) {
+ owner->sendHouseAuctionMessage(houseId, HouseAuctionType::Transfer, enumToValue(TransferErrorMessage::Internal));
+ return;
+ }
+
+ const std::shared_ptr &newOwner = getPlayerByName(newOwnerName, true);
+ if (!newOwner) {
+ owner->sendHouseAuctionMessage(houseId, HouseAuctionType::Transfer, enumToValue(TransferErrorMessage::CharacterNotExist));
+ return;
+ }
+
+ const auto house = g_game().map.houses.getHouseByClientId(houseId);
+ if (!house || house->getState() != CyclopediaHouseState::Rented) {
+ owner->sendHouseAuctionMessage(houseId, HouseAuctionType::Transfer, enumToValue(TransferErrorMessage::Internal));
+ return;
+ }
+
+ auto ret = owner->canTransferHouse(houseId, newOwner->getGUID());
+ if (ret != TransferErrorMessage::Success) {
+ owner->sendHouseAuctionMessage(houseId, HouseAuctionType::Transfer, enumToValue(ret));
+ return;
+ }
+
+ house->setBidderName(newOwnerName);
+ house->setBidder(newOwner->getGUID());
+ house->setInternalBid(bidValue);
+ house->setBidEndDate(timestamp);
+ house->setState(CyclopediaHouseState::Transfer);
+
+ owner->sendHouseAuctionMessage(houseId, HouseAuctionType::Transfer, enumToValue(ret));
+ playerCyclopediaHousesByTown(playerId, "");
+}
+
+void Game::playerCyclopediaHouseCancelTransfer(uint32_t playerId, uint32_t houseId) {
+ if (!g_configManager().getBoolean(CYCLOPEDIA_HOUSE_AUCTION)) {
+ return;
+ }
+
+ const std::shared_ptr &player = getPlayerByID(playerId);
+ if (!player) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::CancelTransfer, enumToValue(TransferErrorMessage::Internal));
+ return;
+ }
+
+ const auto house = g_game().map.houses.getHouseByClientId(houseId);
+ if (!house || house->getState() != CyclopediaHouseState::Transfer) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::CancelTransfer, enumToValue(TransferErrorMessage::Internal));
+ return;
+ }
+
+ if (house->getOwner() != player->getGUID()) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::CancelTransfer, enumToValue(TransferErrorMessage::NotHouseOwner));
+ return;
+ }
+
+ if (house->getTransferStatus()) {
+ const auto &newOwner = getPlayerByGUID(house->getBidder());
+ const auto amountPaid = house->getInternalBid() + house->getRent();
+ if (newOwner) {
+ newOwner->setBankBalance(newOwner->getBankBalance() + amountPaid);
+ newOwner->sendResourceBalance(RESOURCE_BANK, newOwner->getBankBalance());
+ } else {
+ IOLoginData::increaseBankBalance(house->getBidder(), amountPaid);
+ }
+ }
+
+ house->setBidderName("");
+ house->setBidder(0);
+ house->setInternalBid(0);
+ house->setBidEndDate(0);
+ house->setState(CyclopediaHouseState::Rented);
+ house->setTransferStatus(false);
+
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::CancelTransfer, enumToValue(TransferErrorMessage::Success));
+ playerCyclopediaHousesByTown(playerId, "");
+}
+
+void Game::playerCyclopediaHouseAcceptTransfer(uint32_t playerId, uint32_t houseId) {
+ if (!g_configManager().getBoolean(CYCLOPEDIA_HOUSE_AUCTION)) {
+ return;
+ }
+
+ const std::shared_ptr &player = getPlayerByID(playerId);
+ if (!player) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::AcceptTransfer, enumToValue(AcceptTransferErrorMessage::Internal));
+ return;
+ }
+
+ const auto house = g_game().map.houses.getHouseByClientId(houseId);
+ if (!house || house->getState() != CyclopediaHouseState::Transfer) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::AcceptTransfer, enumToValue(AcceptTransferErrorMessage::Internal));
+ return;
+ }
+
+ auto ret = player->canAcceptTransferHouse(houseId);
+ if (ret != AcceptTransferErrorMessage::Success) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::AcceptTransfer, enumToValue(ret));
+ return;
+ }
+
+ if (!processBankAuction(player, house, house->getInternalBid())) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::AcceptTransfer, enumToValue(AcceptTransferErrorMessage::Frozen));
+ return;
+ }
+
+ house->setTransferStatus(true);
+
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::AcceptTransfer, enumToValue(ret));
+ playerCyclopediaHousesByTown(playerId, "");
+}
+
+void Game::playerCyclopediaHouseRejectTransfer(uint32_t playerId, uint32_t houseId) {
+ if (!g_configManager().getBoolean(CYCLOPEDIA_HOUSE_AUCTION)) {
+ return;
+ }
+
+ const std::shared_ptr &player = getPlayerByID(playerId);
+ if (!player) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::Transfer, enumToValue(TransferErrorMessage::Internal));
+ return;
+ }
+
+ const auto house = g_game().map.houses.getHouseByClientId(houseId);
+ if (!house || house->getBidder() != player->getGUID() || house->getState() != CyclopediaHouseState::Transfer) {
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::Transfer, enumToValue(TransferErrorMessage::NotHouseOwner));
+ return;
+ }
+
+ if (house->getTransferStatus()) {
+ const auto &newOwner = getPlayerByGUID(house->getBidder());
+ const auto amountPaid = house->getInternalBid() + house->getRent();
+ if (newOwner) {
+ newOwner->setBankBalance(newOwner->getBankBalance() + amountPaid);
+ newOwner->sendResourceBalance(RESOURCE_BANK, newOwner->getBankBalance());
+ } else {
+ IOLoginData::increaseBankBalance(house->getBidder(), amountPaid);
+ }
+ }
+
+ house->setBidderName("");
+ house->setBidder(0);
+ house->setInternalBid(0);
+ house->setBidEndDate(0);
+ house->setState(CyclopediaHouseState::Rented);
+ house->setTransferStatus(false);
+
+ player->sendHouseAuctionMessage(houseId, HouseAuctionType::RejectTransfer, enumToValue(TransferErrorMessage::Success));
+ playerCyclopediaHousesByTown(playerId, "");
+}
+
+bool Game::processBankAuction(std::shared_ptr player, const std::shared_ptr &house, uint64_t bid, bool replace /* = false*/) {
+ if (!replace && player->getBankBalance() < (house->getRent() + bid)) {
+ return false;
+ }
+
+ if (player->getBankBalance() < bid) {
+ return false;
+ }
+
+ uint64_t balance = player->getBankBalance();
+ if (replace) {
+ player->setBankBalance(balance - (bid - house->getInternalBid()));
+ } else {
+ player->setBankBalance(balance - (house->getRent() + bid));
+ }
+
+ player->sendResourceBalance(RESOURCE_BANK, player->getBankBalance());
+
+ if (house->getBidderName() != player->getName()) {
+ const auto otherPlayer = g_game().getPlayerByName(house->getBidderName());
+ if (!otherPlayer) {
+ uint32_t bidderGuid = IOLoginData::getGuidByName(house->getBidderName());
+ IOLoginData::increaseBankBalance(bidderGuid, (house->getBidHolderLimit() + house->getRent()));
+ } else {
+ otherPlayer->setBankBalance(otherPlayer->getBankBalance() + (house->getBidHolderLimit() + house->getRent()));
+ otherPlayer->sendResourceBalance(RESOURCE_BANK, otherPlayer->getBankBalance());
+ }
+ }
+
+ return true;
+}
diff --git a/src/game/game.hpp b/src/game/game.hpp
index ff2e127fd25..b16d1787d69 100644
--- a/src/game/game.hpp
+++ b/src/game/game.hpp
@@ -289,6 +289,17 @@ class Game {
void playerHighscores(const std::shared_ptr &player, HighscoreType_t type, uint8_t category, uint32_t vocation, const std::string &worldName, uint16_t page, uint8_t entriesPerPage);
static std::string getSkillNameById(uint8_t &skill);
+ // House Auction
+ void playerCyclopediaHousesByTown(uint32_t playerId, const std::string &townName);
+ void playerCyclopediaHouseBid(uint32_t playerId, uint32_t houseId, uint64_t bidValue);
+ void playerCyclopediaHouseMoveOut(uint32_t playerId, uint32_t houseId, uint32_t timestamp);
+ void playerCyclopediaHouseCancelMoveOut(uint32_t playerId, uint32_t houseId);
+ void playerCyclopediaHouseTransfer(uint32_t playerId, uint32_t houseId, uint32_t timestamp, const std::string &newOwnerName, uint64_t bidValue);
+ void playerCyclopediaHouseCancelTransfer(uint32_t playerId, uint32_t houseId);
+ void playerCyclopediaHouseAcceptTransfer(uint32_t playerId, uint32_t houseId);
+ void playerCyclopediaHouseRejectTransfer(uint32_t playerId, uint32_t houseId);
+ bool processBankAuction(std::shared_ptr player, const std::shared_ptr &house, uint64_t bid, bool replace = false);
+
void updatePlayerSaleItems(uint32_t playerId);
bool internalStartTrade(const std::shared_ptr &player, const std::shared_ptr &partner, const std::shared_ptr
- &tradeItem);
diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp
index c0a6a13f363..37ec4a4dec1 100644
--- a/src/io/iologindata.cpp
+++ b/src/io/iologindata.cpp
@@ -336,14 +336,6 @@ void IOLoginData::increaseBankBalance(uint32_t guid, uint64_t bankBalance) {
Database::getInstance().executeQuery(query.str());
}
-bool IOLoginData::hasBiddedOnHouse(uint32_t guid) {
- Database &db = Database::getInstance();
-
- std::ostringstream query;
- query << "SELECT `id` FROM `houses` WHERE `highest_bidder` = " << guid << " LIMIT 1";
- return db.storeQuery(query.str()).get() != nullptr;
-}
-
std::vector IOLoginData::getVIPEntries(uint32_t accountId) {
std::string query = fmt::format("SELECT `player_id`, (SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `name`, `description`, `icon`, `notify` FROM `account_viplist` WHERE `account_id` = {}", accountId);
std::vector entries;
diff --git a/src/io/iologindata.hpp b/src/io/iologindata.hpp
index d379031cd55..378e1bd2388 100644
--- a/src/io/iologindata.hpp
+++ b/src/io/iologindata.hpp
@@ -31,7 +31,6 @@ class IOLoginData {
static std::string getNameByGuid(uint32_t guid);
static bool formatPlayerName(std::string &name);
static void increaseBankBalance(uint32_t guid, uint64_t bankBalance);
- static bool hasBiddedOnHouse(uint32_t guid);
static std::vector getVIPEntries(uint32_t accountId);
static void addVIPEntry(uint32_t accountId, uint32_t guid, const std::string &description, uint32_t icon, bool notify);
diff --git a/src/io/iomapserialize.cpp b/src/io/iomapserialize.cpp
index b1de604dd14..1479197c2c5 100644
--- a/src/io/iomapserialize.cpp
+++ b/src/io/iomapserialize.cpp
@@ -273,7 +273,7 @@ void IOMapSerialize::saveTile(PropWriteStream &stream, const std::shared_ptrgetNumber("id");
const auto house = g_game().map.houses.getHouse(houseId);
- if (house) {
- auto owner = result->getNumber("owner");
- auto newOwner = result->getNumber("new_owner");
- // Transfer house owner
- auto isTransferOnRestart = g_configManager().getBoolean(TOGGLE_HOUSE_TRANSFER_ON_SERVER_RESTART);
- if (isTransferOnRestart && newOwner >= 0) {
+ if (!house) {
+ continue;
+ }
+
+ auto owner = result->getNumber("owner");
+ auto newOwner = result->getNumber("new_owner");
+ uint32_t bidder = result->getNumber("bidder");
+ std::string bidderName = result->getString("bidder_name");
+ uint32_t highestBid = result->getNumber("highest_bid");
+ uint32_t internalBid = result->getNumber("internal_bid");
+ uint32_t bidEndDate = result->getNumber("bid_end_date");
+ auto state = static_cast(result->getNumber("state"));
+ auto transferStatus = result->getNumber("transfer_status");
+ const auto timeNow = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count();
+
+ // Transfer house owner
+ auto isTransferOnRestart = g_configManager().getBoolean(TOGGLE_HOUSE_TRANSFER_ON_SERVER_RESTART);
+ if (isTransferOnRestart && newOwner >= 0) {
+ g_game().setTransferPlayerHouseItems(houseId, owner);
+ if (newOwner == 0) {
+ g_logger().debug("Removing house id '{}' owner", houseId);
+ house->setOwner(0);
+ } else {
+ g_logger().debug("Setting house id '{}' owner to player GUID '{}'", houseId, newOwner);
+ house->setOwner(newOwner);
+ }
+ } else if (state == CyclopediaHouseState::Available && timeNow > bidEndDate && bidder > 0) {
+ g_logger().debug("[BID] - Setting house id '{}' owner to player GUID '{}'", houseId, bidder);
+ if (highestBid < internalBid) {
+ uint32_t diff = internalBid - highestBid;
+ IOLoginData::increaseBankBalance(bidder, diff);
+ }
+ house->setOwner(bidder);
+ bidder = 0;
+ bidderName = "";
+ highestBid = 0;
+ internalBid = 0;
+ bidEndDate = 0;
+ } else if (state == CyclopediaHouseState::Transfer && timeNow > bidEndDate && bidder > 0) {
+ g_logger().debug("[TRANSFER] - Removing house id '{}' from owner GUID '{}' and transfering to new owner GUID '{}'", houseId, owner, bidder);
+ if (transferStatus) {
g_game().setTransferPlayerHouseItems(houseId, owner);
- if (newOwner == 0) {
- g_logger().debug("Removing house id '{}' owner", houseId);
- house->setOwner(0);
- } else {
- g_logger().debug("Setting house id '{}' owner to player GUID '{}'", houseId, newOwner);
- house->setOwner(newOwner);
- }
+ house->setOwner(bidder);
+ IOLoginData::increaseBankBalance(owner, internalBid);
} else {
- house->setOwner(owner, false);
+ house->setOwner(owner);
}
- house->setPaidUntil(result->getNumber("paid"));
- house->setPayRentWarnings(result->getNumber("warnings"));
+ bidder = 0;
+ bidderName = "";
+ internalBid = 0;
+ bidEndDate = 0;
+ transferStatus = false;
+ } else if (state == CyclopediaHouseState::MoveOut && timeNow > bidEndDate) {
+ g_logger().debug("[MOVE OUT] - Removing house id '{}' owner", houseId);
+ g_game().setTransferPlayerHouseItems(houseId, owner);
+ house->setOwner(0);
+ bidEndDate = 0;
+ } else {
+ house->setOwner(owner, false);
+ house->setState(state);
}
+ house->setBidder(bidder);
+ house->setBidderName(bidderName);
+ house->setHighestBid(highestBid);
+ house->setInternalBid(internalBid);
+ house->setBidHolderLimit(internalBid);
+ house->setBidEndDate(bidEndDate);
+ house->setTransferStatus(transferStatus);
} while (result->next());
result = db.storeQuery("SELECT `house_id`, `listid`, `list` FROM `house_lists`");
@@ -331,11 +379,12 @@ bool IOMapSerialize::SaveHouseInfoGuard() {
Database &db = Database::getInstance();
std::ostringstream query;
- DBInsert houseUpdate("INSERT INTO `houses` (`id`, `owner`, `paid`, `warnings`, `name`, `town_id`, `rent`, `size`, `beds`) VALUES ");
- houseUpdate.upsert({ "owner", "paid", "warnings", "name", "town_id", "rent", "size", "beds" });
+ DBInsert houseUpdate("INSERT INTO `houses` (`id`, `owner`, `paid`, `warnings`, `name`, `town_id`, `rent`, `size`, `beds`, `bidder`, `bidder_name`, `highest_bid`, `internal_bid`, `bid_end_date`, `state`, `transfer_status`) VALUES ");
+ houseUpdate.upsert({ "owner", "paid", "warnings", "name", "town_id", "rent", "size", "beds", "bidder", "bidder_name", "highest_bid", "internal_bid", "bid_end_date", "state", "transfer_status" });
for (const auto &[key, house] : g_game().map.houses.getHouses()) {
- std::string values = fmt::format("{},{},{},{},{},{},{},{},{}", house->getId(), house->getOwner(), house->getPaidUntil(), house->getPayRentWarnings(), db.escapeString(house->getName()), house->getTownId(), house->getRent(), house->getSize(), house->getBedCount());
+ auto stateValue = magic_enum::enum_integer(house->getState());
+ std::string values = fmt::format("{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}", house->getId(), house->getOwner(), house->getPaidUntil(), house->getPayRentWarnings(), db.escapeString(house->getName()), house->getTownId(), house->getRent(), house->getSize(), house->getBedCount(), house->getBidder(), db.escapeString(house->getBidderName()), house->getHighestBid(), house->getInternalBid(), house->getBidEndDate(), std::to_string(stateValue), (house->getTransferStatus() ? 1 : 0));
if (!houseUpdate.addRow(values)) {
return false;
diff --git a/src/lua/functions/map/house_functions.cpp b/src/lua/functions/map/house_functions.cpp
index 10ebbc2cf0c..dd20d6fdcf6 100644
--- a/src/lua/functions/map/house_functions.cpp
+++ b/src/lua/functions/map/house_functions.cpp
@@ -9,6 +9,7 @@
#include "lua/functions/map/house_functions.hpp"
+#include "account/account.hpp"
#include "config/configmanager.hpp"
#include "items/bed.hpp"
#include "game/game.hpp"
@@ -238,7 +239,7 @@ int HouseFunctions::luaHouseStartTrade(lua_State* L) {
return 1;
}
- if (IOLoginData::hasBiddedOnHouse(tradePartner->getGUID())) {
+ if (tradePartner->getAccount()->getHouseBidId() != 0) {
lua_pushnumber(L, RETURNVALUE_TRADEPLAYERHIGHESTBIDDER);
return 1;
}
diff --git a/src/map/house/house.cpp b/src/map/house/house.cpp
index 6d93561172b..b3054616bff 100644
--- a/src/map/house/house.cpp
+++ b/src/map/house/house.cpp
@@ -95,7 +95,7 @@ void House::setOwner(uint32_t guid, bool updateDatabase /* = true*/, const std::
Database &db = Database::getInstance();
std::ostringstream query;
- query << "UPDATE `houses` SET `owner` = " << guid << ", `new_owner` = -1, `bid` = 0, `bid_end` = 0, `last_bid` = 0, `highest_bidder` = 0 WHERE `id` = " << id;
+ query << "UPDATE `houses` SET `owner` = " << guid << ", `new_owner` = -1, `paid` = 0, `bidder` = 0, `bidder_name` = '', `highest_bid` = 0, `internal_bid` = 0, `bid_end_date` = 0, `state` = " << (guid > 0 ? 2 : 0) << " WHERE `id` = " << id;
db.executeQuery(query.str());
}
@@ -107,7 +107,9 @@ void House::setOwner(uint32_t guid, bool updateDatabase /* = true*/, const std::
if (owner != 0) {
tryTransferOwnership(player, false);
- } else {
+ }
+
+ if (guid != 0) {
std::string strRentPeriod = asLowerCaseString(g_configManager().getString(HOUSE_RENT_PERIOD));
time_t currentTime = time(nullptr);
if (strRentPeriod == "yearly") {
@@ -123,6 +125,8 @@ void House::setOwner(uint32_t guid, bool updateDatabase /* = true*/, const std::
}
paidUntil = currentTime;
+ } else {
+ paidUntil = 0;
}
rentWarnings = 0;
@@ -141,6 +145,7 @@ void House::setOwner(uint32_t guid, bool updateDatabase /* = true*/, const std::
owner = guid;
ownerName = name;
ownerAccountId = result->getNumber("account_id");
+ m_state = CyclopediaHouseState::Rented;
}
}
@@ -155,15 +160,17 @@ void House::updateDoorDescription() const {
ss << "It belongs to house '" << houseName << "'. Nobody owns this house.";
}
- ss << " It is " << getSize() << " square meters.";
- const int32_t housePrice = getPrice();
- if (housePrice != -1) {
- if (g_configManager().getBoolean(HOUSE_PURSHASED_SHOW_PRICE) || owner == 0) {
- ss << " It costs " << formatNumber(getPrice()) << " gold coins.";
- }
- std::string strRentPeriod = asLowerCaseString(g_configManager().getString(HOUSE_RENT_PERIOD));
- if (strRentPeriod != "never") {
- ss << " The rent cost is " << formatNumber(getRent()) << " gold coins and it is billed " << strRentPeriod << ".";
+ if (!g_configManager().getBoolean(CYCLOPEDIA_HOUSE_AUCTION)) {
+ ss << " It is " << getSize() << " square meters.";
+ const int32_t housePrice = getPrice();
+ if (housePrice != -1) {
+ if (g_configManager().getBoolean(HOUSE_PURSHASED_SHOW_PRICE) || owner == 0) {
+ ss << " It costs " << formatNumber(getPrice()) << " gold coins.";
+ }
+ std::string strRentPeriod = asLowerCaseString(g_configManager().getString(HOUSE_RENT_PERIOD));
+ if (strRentPeriod != "never") {
+ ss << " The rent cost is " << formatNumber(getRent()) << " gold coins and it is billed " << strRentPeriod << ".";
+ }
}
}
@@ -479,6 +486,43 @@ void House::resetTransferItem() {
}
}
+void House::calculateBidEndDate(uint8_t daysToEnd) {
+ auto currentTimeMs = std::chrono::system_clock::now().time_since_epoch();
+
+ auto now = std::chrono::system_clock::time_point(
+ std::chrono::duration_cast(currentTimeMs)
+ );
+
+ // Truncate to whole days since epoch
+ days daysSinceEpoch = std::chrono::duration_cast(now.time_since_epoch());
+
+ // Get today's date at 00:00:00 UTC
+ auto todayMidnight = std::chrono::system_clock::time_point(daysSinceEpoch);
+
+ std::chrono::system_clock::time_point targetDay = todayMidnight + days(daysToEnd);
+
+ const auto serverSaveTime = g_configManager().getString(GLOBAL_SERVER_SAVE_TIME);
+
+ std::vector params = vectorAtoi(explodeString(serverSaveTime, ":"));
+ int32_t hour = params.front();
+ int32_t min = 0;
+ int32_t sec = 0;
+ if (params.size() > 1) {
+ min = params[1];
+
+ if (params.size() > 2) {
+ sec = params[2];
+ }
+ }
+ std::chrono::system_clock::time_point targetTime = targetDay + std::chrono::hours(hour) + std::chrono::minutes(min) + std::chrono::seconds(sec);
+
+ std::time_t resultTime = std::chrono::system_clock::to_time_t(targetTime);
+ std::tm* localTime = std::localtime(&resultTime);
+ auto bidEndDate = static_cast(std::mktime(localTime));
+
+ this->m_bidEndDate = bidEndDate;
+}
+
std::shared_ptr HouseTransferItem::createHouseTransferItem(const std::shared_ptr &house) {
auto transferItem = std::make_shared(house);
transferItem->setID(ITEM_DOCUMENT_RO);
@@ -725,6 +769,35 @@ std::shared_ptr Houses::getHouseByPlayerId(uint32_t playerId) const {
return nullptr;
}
+std::vector> Houses::getAllHousesByPlayerId(uint32_t playerId) {
+ std::vector> playerHouses;
+ for (const auto &[id, house] : houseMap) {
+ if (house->getOwner() == playerId) {
+ playerHouses.emplace_back(house);
+ }
+ }
+ return playerHouses;
+}
+
+std::shared_ptr Houses::getHouseByBidderName(const std::string &bidderName) {
+ for (const auto &[id, house] : houseMap) {
+ if (house->getBidderName() == bidderName) {
+ return house;
+ }
+ }
+ return nullptr;
+}
+
+uint16_t Houses::getHouseCountByAccount(uint32_t accountId) {
+ uint16_t count = 0;
+ for (const auto &[id, house] : houseMap) {
+ if (house->getOwnerAccountId() == accountId) {
+ ++count;
+ }
+ }
+ return count;
+}
+
bool Houses::loadHousesXML(const std::string &filename) {
pugi::xml_document doc;
const pugi::xml_parse_result result = doc.load_file(filename.c_str());
@@ -764,6 +837,13 @@ bool Houses::loadHousesXML(const std::string &filename) {
house->setRent(pugi::cast(houseNode.attribute("rent").value()));
house->setSize(pugi::cast(houseNode.attribute("size").value()));
house->setTownId(pugi::cast(houseNode.attribute("townid").value()));
+ house->setClientId(pugi::cast(houseNode.attribute("clientid").value()));
+
+ auto guildhallAttr = houseNode.attribute("guildhall");
+ if (!guildhallAttr.empty()) {
+ house->setGuildhall(static_cast(guildhallAttr.as_bool()));
+ }
+
auto maxBedsAttr = houseNode.attribute("beds");
int32_t maxBeds = -1;
if (!maxBedsAttr.empty()) {
@@ -772,6 +852,7 @@ bool Houses::loadHousesXML(const std::string &filename) {
house->setMaxBeds(maxBeds);
house->setOwner(0, false);
+ addHouseClientId(house->getClientId(), house);
}
return true;
}
diff --git a/src/map/house/house.hpp b/src/map/house/house.hpp
index a3dc765988f..d994fdbe3b2 100644
--- a/src/map/house/house.hpp
+++ b/src/map/house/house.hpp
@@ -13,11 +13,14 @@
#include "declarations.hpp"
#include "map/house/housetile.hpp"
#include "game/movement/position.hpp"
+#include "enums/player_cyclopedia.hpp"
class House;
class BedItem;
class Player;
+using days = std::chrono::duration>;
+
class AccessList {
public:
void parseList(const std::string &list);
@@ -233,6 +236,84 @@ class House final : public SharedObject {
bool hasNewOwnership() const;
void setNewOwnership();
+ void setClientId(uint32_t newClientId) {
+ this->m_clientId = newClientId;
+ }
+ uint32_t getClientId() const {
+ return m_clientId;
+ }
+
+ void setBidder(int32_t bidder) {
+ this->m_bidder = bidder;
+ }
+ int32_t getBidder() const {
+ return m_bidder;
+ }
+
+ void setBidderName(const std::string &bidderName) {
+ this->m_bidderName = bidderName;
+ }
+ std::string getBidderName() const {
+ return m_bidderName;
+ }
+
+ void setHighestBid(uint64_t bidValue) {
+ this->m_highestBid = bidValue;
+ }
+ uint64_t getHighestBid() const {
+ return m_highestBid;
+ }
+
+ void setInternalBid(uint64_t bidValue) {
+ this->m_internalBid = bidValue;
+ }
+ uint64_t getInternalBid() const {
+ return m_internalBid;
+ }
+
+ void setBidHolderLimit(uint64_t bidValue) {
+ this->m_bidHolderLimit = bidValue;
+ }
+ uint64_t getBidHolderLimit() const {
+ return m_bidHolderLimit;
+ }
+
+ void calculateBidEndDate(uint8_t daysToEnd);
+ void setBidEndDate(uint32_t bidEndDate) {
+ this->m_bidEndDate = bidEndDate;
+ };
+ uint32_t getBidEndDate() const {
+ return m_bidEndDate;
+ }
+
+ void setState(CyclopediaHouseState state) {
+ this->m_state = state;
+ }
+ CyclopediaHouseState getState() const {
+ return m_state;
+ }
+
+ void setTransferStatus(bool transferStatus) {
+ this->m_transferStatus = transferStatus;
+ }
+ bool getTransferStatus() const {
+ return m_transferStatus;
+ }
+
+ void setOwnerAccountId(uint32_t accountId) {
+ this->ownerAccountId = accountId;
+ }
+ uint32_t getOwnerAccountId() const {
+ return ownerAccountId;
+ }
+
+ void setGuildhall(bool isGuildHall) {
+ this->guildHall = isGuildHall;
+ }
+ bool isGuildhall() const {
+ return guildHall;
+ }
+
private:
bool transferToDepot() const;
@@ -263,9 +344,21 @@ class House final : public SharedObject {
uint32_t townId = 0;
uint32_t maxBeds = 4;
int32_t bedsCount = -1;
+ bool guildHall = false;
Position posEntry = {};
+ // House Auction
+ uint32_t m_clientId;
+ int32_t m_bidder = 0;
+ std::string m_bidderName = "";
+ uint64_t m_highestBid = 0;
+ uint64_t m_internalBid = 0;
+ uint64_t m_bidHolderLimit = 0;
+ uint32_t m_bidEndDate = 0;
+ CyclopediaHouseState m_state = CyclopediaHouseState::Available;
+ bool m_transferStatus = false;
+
bool isLoaded = false;
void handleContainer(ItemList &moveItemList, const std::shared_ptr
- &item) const;
@@ -299,7 +392,26 @@ class Houses {
return it->second;
}
+ void addHouseClientId(uint32_t clientId, std::shared_ptr house) {
+ if (auto it = houseMapClientId.find(clientId); it != houseMapClientId.end()) {
+ return;
+ }
+
+ houseMapClientId.emplace(clientId, house);
+ }
+
+ std::shared_ptr getHouseByClientId(uint32_t clientId) {
+ auto it = houseMapClientId.find(clientId);
+ if (it == houseMapClientId.end()) {
+ return nullptr;
+ }
+ return it->second;
+ }
+
std::shared_ptr getHouseByPlayerId(uint32_t playerId) const;
+ std::vector> getAllHousesByPlayerId(uint32_t playerId);
+ std::shared_ptr getHouseByBidderName(const std::string &bidderName);
+ uint16_t getHouseCountByAccount(uint32_t accountId);
bool loadHousesXML(const std::string &filename);
@@ -311,4 +423,5 @@ class Houses {
private:
HouseMap houseMap;
+ HouseMap houseMapClientId;
};
diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp
index dc02e316f1f..60e29847477 100644
--- a/src/server/network/protocol/protocolgame.cpp
+++ b/src/server/network/protocol/protocolgame.cpp
@@ -55,6 +55,7 @@
#include "enums/account_type.hpp"
#include "enums/object_category.hpp"
#include "enums/player_blessings.hpp"
+#include "enums/player_cyclopedia.hpp"
/*
* NOTE: This namespace is used so that we can add functions without having to declare them in the ".hpp/.hpp" file
@@ -1227,6 +1228,9 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage &msg, uint8_t recvby
case 0xAC:
parseChannelExclude(msg);
break;
+ case 0xAD:
+ parseCyclopediaHouseAuction(msg);
+ break;
case 0xAE:
parseSendBosstiary();
break;
@@ -6905,6 +6909,7 @@ void ProtocolGame::sendAddCreature(const std::shared_ptr &creature, co
sendLootContainers();
sendBasicData();
+ sendHousesInfo();
// Wheel of destiny cooldown
if (!oldProtocol && g_configManager().getBoolean(TOGGLE_WHEELSYSTEM)) {
player->wheel()->sendGiftOfLifeCooldown();
@@ -9341,3 +9346,192 @@ void ProtocolGame::sendTakeScreenshot(Screenshot_t screenshotType) {
msg.addByte(screenshotType);
writeToOutputBuffer(msg);
}
+
+void ProtocolGame::parseCyclopediaHouseAuction(NetworkMessage &msg) {
+ if (oldProtocol) {
+ return;
+ }
+
+ uint8_t houseActionType = msg.getByte();
+ switch (houseActionType) {
+ case 0: {
+ const auto townName = msg.getString();
+ g_game().playerCyclopediaHousesByTown(player->getID(), townName);
+ break;
+ }
+ case 1: {
+ const uint32_t houseId = msg.get();
+ const uint64_t bidValue = msg.get();
+ g_game().playerCyclopediaHouseBid(player->getID(), houseId, bidValue);
+ break;
+ }
+ case 2: {
+ const uint32_t houseId = msg.get();
+ const uint32_t timestamp = msg.get();
+ g_game().playerCyclopediaHouseMoveOut(player->getID(), houseId, timestamp);
+ break;
+ }
+ case 3: {
+ const uint32_t houseId = msg.get();
+ const uint32_t timestamp = msg.get();
+ const std::string &newOwner = msg.getString();
+ const uint64_t bidValue = msg.get();
+ g_game().playerCyclopediaHouseTransfer(player->getID(), houseId, timestamp, newOwner, bidValue);
+ break;
+ }
+ case 4: {
+ const uint32_t houseId = msg.get();
+ g_game().playerCyclopediaHouseCancelMoveOut(player->getID(), houseId);
+ break;
+ }
+ case 5: {
+ const uint32_t houseId = msg.get();
+ g_game().playerCyclopediaHouseCancelTransfer(player->getID(), houseId);
+ break;
+ }
+ case 6: {
+ const uint32_t houseId = msg.get();
+ g_game().playerCyclopediaHouseAcceptTransfer(player->getID(), houseId);
+ break;
+ }
+ case 7: {
+ const uint32_t houseId = msg.get();
+ g_game().playerCyclopediaHouseRejectTransfer(player->getID(), houseId);
+ break;
+ }
+ }
+}
+
+void ProtocolGame::sendCyclopediaHouseList(HouseMap houses) {
+ NetworkMessage msg;
+ msg.addByte(0xC7);
+ msg.add(houses.size());
+ for (const auto &[clientId, houseData] : houses) {
+ msg.add(clientId);
+ msg.addByte(0x01); // 0x00 = Renovation; 0x01 = Available
+
+ auto houseState = houseData->getState();
+ auto stateValue = magic_enum::enum_integer(houseState);
+ msg.addByte(stateValue);
+ if (houseState == CyclopediaHouseState::Available) {
+ bool bidder = houseData->getBidderName() == player->getName();
+ msg.addString(houseData->getBidderName());
+ msg.addByte(bidder);
+ uint8_t disableIndex = enumToValue(player->canBidHouse(clientId));
+ msg.addByte(disableIndex);
+
+ if (!houseData->getBidderName().empty()) {
+ msg.add(houseData->getBidEndDate());
+ msg.add(houseData->getHighestBid());
+ if (bidder) {
+ msg.add(houseData->getBidHolderLimit());
+ }
+ }
+ } else if (houseState == CyclopediaHouseState::Rented) {
+ auto ownerName = IOLoginData::getNameByGuid(houseData->getOwner());
+ msg.addString(ownerName);
+ msg.add(houseData->getPaidUntil());
+
+ bool rented = ownerName.compare(player->getName()) == 0;
+ msg.addByte(rented);
+ if (rented) {
+ msg.addByte(0);
+ msg.addByte(0);
+ }
+ } else if (houseState == CyclopediaHouseState::Transfer) {
+ auto ownerName = IOLoginData::getNameByGuid(houseData->getOwner());
+ msg.addString(ownerName);
+ msg.add(houseData->getPaidUntil());
+
+ bool isOwner = ownerName.compare(player->getName()) == 0;
+ msg.addByte(isOwner);
+ if (isOwner) {
+ msg.addByte(0); // ?
+ msg.addByte(0); // ?
+ }
+ msg.add(houseData->getBidEndDate());
+ msg.addString(houseData->getBidderName());
+ msg.addByte(0); // ?
+ msg.add(houseData->getInternalBid());
+
+ bool isNewOwner = player->getName() == houseData->getBidderName();
+ msg.addByte(isNewOwner);
+ if (isNewOwner) {
+ uint8_t disableIndex = enumToValue(player->canAcceptTransferHouse(clientId));
+ msg.addByte(disableIndex); // Accept Transfer Error
+ msg.addByte(0); // Reject Transfer Error
+ }
+
+ if (isOwner) {
+ msg.addByte(0); // Cancel Transfer Error
+ }
+ } else if (houseState == CyclopediaHouseState::MoveOut) {
+ auto ownerName = IOLoginData::getNameByGuid(houseData->getOwner());
+ msg.addString(ownerName);
+ msg.add(houseData->getPaidUntil());
+
+ bool isOwner = ownerName.compare(player->getName()) == 0;
+ msg.addByte(isOwner);
+ if (isOwner) {
+ msg.addByte(0); // ?
+ msg.addByte(0); // ?
+ msg.add(houseData->getBidEndDate());
+ msg.addByte(0);
+ } else {
+ msg.add(houseData->getBidEndDate());
+ }
+ }
+ }
+
+ writeToOutputBuffer(msg);
+}
+
+void ProtocolGame::sendHouseAuctionMessage(uint32_t houseId, HouseAuctionType type, uint8_t index, bool bidSuccess /* = false*/) {
+ NetworkMessage msg;
+ const auto typeValue = enumToValue(type);
+
+ msg.addByte(0xC3);
+ msg.add(houseId);
+ msg.addByte(typeValue);
+ if (bidSuccess && typeValue == 1) {
+ msg.addByte(0x00);
+ }
+ msg.addByte(index);
+
+ writeToOutputBuffer(msg);
+}
+
+void ProtocolGame::sendHousesInfo() {
+ NetworkMessage msg;
+
+ uint32_t houseClientId = 0;
+ const auto accountHouseCount = g_game().map.houses.getHouseCountByAccount(player->getAccountId());
+ const auto house = g_game().map.houses.getHouseByPlayerId(player->getGUID());
+ if (house) {
+ houseClientId = house->getClientId();
+ }
+
+ msg.addByte(0xC6);
+ msg.add(houseClientId);
+ msg.addByte(0x00);
+
+ msg.addByte(accountHouseCount); // Houses Account
+
+ msg.addByte(0x00);
+
+ msg.addByte(3);
+ msg.addByte(3);
+
+ msg.addByte(0x01);
+
+ msg.addByte(0x01);
+ msg.add(houseClientId);
+
+ const auto &housesList = g_game().map.houses.getHouses();
+ msg.add(housesList.size());
+ for (const auto &it : housesList) {
+ msg.add(it.second->getClientId());
+ }
+
+ writeToOutputBuffer(msg);
+}
diff --git a/src/server/network/protocol/protocolgame.hpp b/src/server/network/protocol/protocolgame.hpp
index 2048bea7c16..7b7e0aacee3 100644
--- a/src/server/network/protocol/protocolgame.hpp
+++ b/src/server/network/protocol/protocolgame.hpp
@@ -29,6 +29,7 @@ enum Slots_t : uint8_t;
enum CombatType_t : uint8_t;
enum SoundEffect_t : uint16_t;
enum class SourceEffect_t : uint8_t;
+enum class HouseAuctionType : uint8_t;
class NetworkMessage;
class Player;
@@ -68,6 +69,7 @@ using MarketOfferList = std::list;
using HistoryMarketOfferList = std::list;
using ItemsTierCountList = std::map>;
using StashItemList = std::map;
+using HouseMap = std::map>;
struct TextMessage {
TextMessage() = default;
@@ -353,6 +355,11 @@ class ProtocolGame final : public Protocol {
void sendCyclopediaCharacterBadges();
void sendCyclopediaCharacterTitles();
+ void sendHousesInfo();
+ void parseCyclopediaHouseAuction(NetworkMessage &msg);
+ void sendCyclopediaHouseList(HouseMap houses);
+ void sendHouseAuctionMessage(uint32_t houseId, HouseAuctionType type, uint8_t index, bool bidSuccess);
+
void sendCreatureWalkthrough(const std::shared_ptr &creature, bool walkthrough);
void sendCreatureShield(const std::shared_ptr &creature);
void sendCreatureEmblem(const std::shared_ptr &creature);
From 55de86ee4f475925b2d3098b2d6ac6da19e17a68 Mon Sep 17 00:00:00 2001
From: murilo09 <78226931+murilo09@users.noreply.github.com>
Date: Tue, 31 Dec 2024 13:27:34 -0300
Subject: [PATCH 02/32] fix: damage console (#3203)
---
src/game/game.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/game/game.cpp b/src/game/game.cpp
index e3414f74725..8b19f8bb94f 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -7418,7 +7418,7 @@ bool Game::combatChangeHealth(const std::shared_ptr &attacker, const s
}
auto targetHealth = target->getHealth();
- realDamage = damage.primary.value + damage.secondary.value;
+ realDamage = std::min(targetHealth, damage.primary.value + damage.secondary.value);
if (realDamage == 0) {
return true;
} else if (realDamage >= targetHealth) {
From 4023def3800a8cd7523ba4c64896a9781c4a42c6 Mon Sep 17 00:00:00 2001
From: pudimtibia
Date: Tue, 31 Dec 2024 13:31:56 -0300
Subject: [PATCH 03/32] fix: adjust character limit for player names (#3174)
---
data/modules/scripts/gamestore/init.lua | 41 +++++++++++++++----------
src/game/game.cpp | 2 +-
2 files changed, 25 insertions(+), 18 deletions(-)
diff --git a/data/modules/scripts/gamestore/init.lua b/data/modules/scripts/gamestore/init.lua
index 433abf34492..abd125e79ef 100644
--- a/data/modules/scripts/gamestore/init.lua
+++ b/data/modules/scripts/gamestore/init.lua
@@ -1487,67 +1487,74 @@ GameStore.canChangeToName = function(name)
local result = {
ability = false,
}
- if name:len() < 3 or name:len() > 18 then
- result.reason = "The length of your new name must be between 3 and 18 characters."
+
+ if name:len() < 3 or name:len() > 29 then
+ result.reason = "The length of your new name must be between 3 and 29 characters."
return result
end
local match = name:gmatch("%s+")
local count = 0
- for v in match do
+ for _ in match do
count = count + 1
end
local matchtwo = name:match("^%s+")
if matchtwo then
- result.reason = "Your new name can't have whitespace at begin."
+ result.reason = "Your new name can't have whitespace at the beginning."
return result
end
- if count > 1 then
- result.reason = "Your new name have more than 1 whitespace."
+ if count > 2 then
+ result.reason = "Your new name can't have more than 2 spaces."
+ return result
+ end
+
+ if name:match("%s%s") then
+ result.reason = "Your new name can't have consecutive spaces."
return result
end
-- just copied from znote aac.
local words = { "owner", "gamemaster", "hoster", "admin", "staff", "tibia", "account", "god", "anal", "ass", "fuck", "sex", "hitler", "pussy", "dick", "rape", "adm", "cm", "gm", "tutor", "counsellor" }
local split = name:split(" ")
- for k, word in ipairs(words) do
- for k, nameWord in ipairs(split) do
+ for _, word in ipairs(words) do
+ for _, nameWord in ipairs(split) do
if nameWord:lower() == word then
- result.reason = "You can't use word \"" .. word .. '" in your new name.'
+ result.reason = "You can't use the word '" .. word .. "' in your new name."
return result
end
end
end
local tmpName = name:gsub("%s+", "")
- for i = 1, #words do
- if tmpName:lower():find(words[i]) then
- result.reason = "You can't use word \"" .. words[i] .. '" with whitespace in your new name.'
+ for _, word in ipairs(words) do
+ if tmpName:lower():find(word) then
+ result.reason = "You can't use the word '" .. word .. "' even with spaces in your new name."
return result
end
end
if MonsterType(name) then
- result.reason = 'Your new name "' .. name .. "\" can't be a monster's name."
+ result.reason = "Your new name '" .. name .. "' can't be a monster's name."
return result
elseif Npc(name) then
- result.reason = 'Your new name "' .. name .. "\" can't be a npc's name."
+ result.reason = "Your new name '" .. name .. "' can't be an NPC's name."
return result
end
local letters = "{}|_*+-=<>0123456789@#%^&()/*'\\.,:;~!\"$"
for i = 1, letters:len() do
local c = letters:sub(i, i)
- for i = 1, name:len() do
- local m = name:sub(i, i)
+ for j = 1, name:len() do
+ local m = name:sub(j, j)
if m == c then
- result.reason = "You can't use this letter \"" .. c .. '" in your new name.'
+ result.reason = "You can't use this character '" .. c .. "' in your new name."
return result
end
end
end
+
result.ability = true
return result
end
diff --git a/src/game/game.cpp b/src/game/game.cpp
index 8b19f8bb94f..a4a53738b9b 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -1092,7 +1092,7 @@ std::string Game::getPlayerNameByGUID(const uint32_t &guid) {
ReturnValue Game::getPlayerByNameWildcard(const std::string &s, std::shared_ptr &player) {
size_t strlen = s.length();
- if (strlen == 0 || strlen > 20) {
+ if (strlen == 0 || strlen > 29) {
return RETURNVALUE_PLAYERWITHTHISNAMEISNOTONLINE;
}
From 444ad6cf640c3308c16f212d53e60e8b142cfd9c Mon Sep 17 00:00:00 2001
From: Felipe Pessoa
Date: Tue, 31 Dec 2024 13:34:17 -0300
Subject: [PATCH 04/32] fix: lava lurker heals on fire damage (#3176)
---
data-otservbr-global/monster/elementals/lava_lurker.lua | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/data-otservbr-global/monster/elementals/lava_lurker.lua b/data-otservbr-global/monster/elementals/lava_lurker.lua
index 6a6e236f8c8..f7a88445c56 100644
--- a/data-otservbr-global/monster/elementals/lava_lurker.lua
+++ b/data-otservbr-global/monster/elementals/lava_lurker.lua
@@ -95,7 +95,7 @@ monster.elements = {
{ type = COMBAT_PHYSICALDAMAGE, percent = 0 },
{ type = COMBAT_ENERGYDAMAGE, percent = 0 },
{ type = COMBAT_EARTHDAMAGE, percent = 0 },
- { type = COMBAT_FIREDAMAGE, percent = 0 },
+ { type = COMBAT_FIREDAMAGE, percent = 100 },
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
{ type = COMBAT_DROWNDAMAGE, percent = 0 },
@@ -111,4 +111,8 @@ monster.immunities = {
{ type = "bleed", condition = false },
}
+monster.heals = {
+ { type = COMBAT_FIREDAMAGE, percent = 100 },
+}
+
mType:register(monster)
From c61b1b4b7a805dac60ac19037c0d785dec0cede4 Mon Sep 17 00:00:00 2001
From: Filipe Arruda <66535966+arrudaqs@users.noreply.github.com>
Date: Tue, 31 Dec 2024 13:35:35 -0300
Subject: [PATCH 05/32] fix: add missing storage breaking the ice quest (#3201)
---
data-otservbr-global/lib/core/storages.lua | 1 +
.../scripts/creaturescripts/customs/freequests.lua | 1 +
data-otservbr-global/scripts/lib/register_actions.lua | 1 +
3 files changed, 3 insertions(+)
diff --git a/data-otservbr-global/lib/core/storages.lua b/data-otservbr-global/lib/core/storages.lua
index 4529c64f583..da86c8b9bb9 100644
--- a/data-otservbr-global/lib/core/storages.lua
+++ b/data-otservbr-global/lib/core/storages.lua
@@ -923,6 +923,7 @@ Storage = {
NoblemanSecondAddon = 41308,
FormorgarMinesHoistSkeleton = 41309,
FormorgarMinesHoistChest = 41310,
+ PickAmount = 41311,
},
},
U8_1 = { -- update 8.1 - Reserved Storages 41351 - 41650
diff --git a/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua b/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua
index bfeac7acad0..9c318264b11 100644
--- a/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua
+++ b/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua
@@ -173,6 +173,7 @@ local questTable = {
{ storageName = "TheIceIslands.Mission10", storage = Storage.Quest.U8_0.TheIceIslands.Mission10, storageValue = 2 },
{ storageName = "TheIceIslands.Mission11", storage = Storage.Quest.U8_0.TheIceIslands.Mission11, storageValue = 2 },
{ storageName = "TheIceIslands.Mission12", storage = Storage.Quest.U8_0.TheIceIslands.Mission12, storageValue = 6 },
+ { storageName = "TheIceIslands.PickAmount", storage = Storage.Quest.U8_0.TheIceIslands.PickAmount, storageValue = 3 },
{ storageName = "TheIceIslands.yakchalDoor", storage = Storage.Quest.U8_0.TheIceIslands.yakchalDoor, storageValue = 1 },
{ storageName = "TheInquisitionQuest.Questline", storage = Storage.Quest.U8_2.TheInquisitionQuest.Questline, storageValue = 25 },
{ storageName = "TheInquisitionQuest.Mission01", storage = Storage.Quest.U8_2.TheInquisitionQuest.Mission01, storageValue = 7 },
diff --git a/data-otservbr-global/scripts/lib/register_actions.lua b/data-otservbr-global/scripts/lib/register_actions.lua
index ec68c90e27c..d89772f7ce9 100644
--- a/data-otservbr-global/scripts/lib/register_actions.lua
+++ b/data-otservbr-global/scripts/lib/register_actions.lua
@@ -651,6 +651,7 @@ function onUsePick(player, item, fromPosition, target, toPosition, isHotkey)
--The Ice Islands Quest, Nibelor 1: Breaking the Ice
local missionProgress = player:getStorageValue(Storage.Quest.U8_0.TheIceIslands.Mission02)
local pickAmount = player:getStorageValue(Storage.Quest.U8_0.TheIceIslands.PickAmount)
+
if missionProgress < 1 or pickAmount >= 3 or player:getStorageValue(Storage.Quest.U8_0.TheIceIslands.Questline) ~= 3 then
return false
end
From 0091ce144f0b2e4e3456904418d4be5d93b4ac68 Mon Sep 17 00:00:00 2001
From: Elson Costa
Date: Tue, 31 Dec 2024 14:03:18 -0300
Subject: [PATCH 06/32] feat: monsters and npcs rotten blood quest (#2686)
---
data-otservbr-global/lib/core/storages.lua | 5 +
.../monster/bosses/bakragore.lua | 159 ++
.../monster/bosses/chagorz.lua | 147 +
.../monster/bosses/ichgahal.lua | 154 +
.../monster/bosses/murcion.lua | 140 +
.../monster/bosses/vemiath.lua | 151 +
.../rotten_blood/bloated_man-maggot.lua | 122 +
.../monster/quests/rotten_blood/converter.lua | 119 +
.../rotten_blood/darklight_construct.lua | 120 +
.../quests/rotten_blood/darklight_emitter.lua | 118 +
.../quests/rotten_blood/darklight_matter.lua | 127 +
.../quests/rotten_blood/darklight_source.lua | 117 +
.../quests/rotten_blood/darklight_striker.lua | 120 +
.../quests/rotten_blood/echo_of_chagorz.lua | 101 +
.../quests/rotten_blood/echo_of_ichgahal.lua | 101 +
.../quests/rotten_blood/echo_of_murcion.lua | 101 +
.../quests/rotten_blood/echo_of_vemiath.lua | 101 +
.../quests/rotten_blood/elder_bloodjaw.lua | 102 +
.../rotten_blood/meandering_mushroom.lua | 121 +
.../monster/quests/rotten_blood/mushroom.lua | 94 +
.../rotten_blood/mycobiontic_beetle.lua | 129 +
.../quests/rotten_blood/oozing_carcass.lua | 123 +
.../quests/rotten_blood/oozing_corpus.lua | 120 +
.../quests/rotten_blood/rotten_man-maggot.lua | 124 +
.../quests/rotten_blood/sopping_carcass.lua | 110 +
.../quests/rotten_blood/sopping_corpus.lua | 130 +
.../quests/rotten_blood/walking_pillar.lua | 130 +
.../quests/rotten_blood/wandering_pillar.lua | 133 +
.../npc/bloodshade_rotten.lua | 79 +
.../npc/bloodshade_sacrifice.lua | 100 +
data-otservbr-global/npc/torkada.lua | 103 +
.../bosses_levers/rotten_bakragore.lua | 24 +
.../actions/bosses_levers/rotten_chagorz.lua | 23 +
.../actions/bosses_levers/rotten_ichgahal.lua | 23 +
.../actions/bosses_levers/rotten_murcion.lua | 23 +
.../actions/bosses_levers/rotten_vemiath.lua | 23 +
.../quests/rotten_blood/blood_entrance.lua | 61 +
.../quests/rotten_blood/bosses_killed.lua | 50 +
.../actions/quests/rotten_blood/entrances.lua | 104 +
.../actions/quests/rotten_blood/sacrifice.lua | 34 +
.../spells/monster/large_black_ring.lua | 35 +
.../spells/monster/large_blackportal_ring.lua | 35 +
.../spells/monster/large_energy_ring.lua | 35 +
.../spells/monster/large_fire_ring.lua | 35 +
.../spells/monster/large_holy_ring.lua | 35 +
.../scripts/spells/monster/large_ice_ring.lua | 35 +
.../spells/monster/large_pink_ring.lua | 35 +
.../spells/monster/large_poison_ring.lua | 35 +
.../monster/large_purple_ring_smoke.lua | 35 +
.../scripts/spells/monster/large_red_ring.lua | 35 +
.../world/otservbr-monster.xml | 2494 ++++++++++++++++-
data-otservbr-global/world/otservbr-npc.xml | 9 +
data/items/items.xml | 412 ++-
data/libs/tables/doors.lua | 1 +
54 files changed, 7201 insertions(+), 31 deletions(-)
create mode 100644 data-otservbr-global/monster/bosses/bakragore.lua
create mode 100644 data-otservbr-global/monster/bosses/chagorz.lua
create mode 100644 data-otservbr-global/monster/bosses/ichgahal.lua
create mode 100644 data-otservbr-global/monster/bosses/murcion.lua
create mode 100644 data-otservbr-global/monster/bosses/vemiath.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/bloated_man-maggot.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/converter.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/darklight_construct.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/darklight_emitter.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/darklight_matter.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/darklight_source.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/darklight_striker.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/echo_of_chagorz.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/echo_of_ichgahal.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/echo_of_murcion.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/echo_of_vemiath.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/elder_bloodjaw.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/meandering_mushroom.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/mushroom.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/mycobiontic_beetle.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/oozing_carcass.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/oozing_corpus.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/rotten_man-maggot.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/sopping_carcass.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/sopping_corpus.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/walking_pillar.lua
create mode 100644 data-otservbr-global/monster/quests/rotten_blood/wandering_pillar.lua
create mode 100644 data-otservbr-global/npc/bloodshade_rotten.lua
create mode 100644 data-otservbr-global/npc/bloodshade_sacrifice.lua
create mode 100644 data-otservbr-global/npc/torkada.lua
create mode 100644 data-otservbr-global/scripts/actions/bosses_levers/rotten_bakragore.lua
create mode 100644 data-otservbr-global/scripts/actions/bosses_levers/rotten_chagorz.lua
create mode 100644 data-otservbr-global/scripts/actions/bosses_levers/rotten_ichgahal.lua
create mode 100644 data-otservbr-global/scripts/actions/bosses_levers/rotten_murcion.lua
create mode 100644 data-otservbr-global/scripts/actions/bosses_levers/rotten_vemiath.lua
create mode 100644 data-otservbr-global/scripts/actions/quests/rotten_blood/blood_entrance.lua
create mode 100644 data-otservbr-global/scripts/actions/quests/rotten_blood/bosses_killed.lua
create mode 100644 data-otservbr-global/scripts/actions/quests/rotten_blood/entrances.lua
create mode 100644 data-otservbr-global/scripts/actions/quests/rotten_blood/sacrifice.lua
create mode 100644 data-otservbr-global/scripts/spells/monster/large_black_ring.lua
create mode 100644 data-otservbr-global/scripts/spells/monster/large_blackportal_ring.lua
create mode 100644 data-otservbr-global/scripts/spells/monster/large_energy_ring.lua
create mode 100644 data-otservbr-global/scripts/spells/monster/large_fire_ring.lua
create mode 100644 data-otservbr-global/scripts/spells/monster/large_holy_ring.lua
create mode 100644 data-otservbr-global/scripts/spells/monster/large_ice_ring.lua
create mode 100644 data-otservbr-global/scripts/spells/monster/large_pink_ring.lua
create mode 100644 data-otservbr-global/scripts/spells/monster/large_poison_ring.lua
create mode 100644 data-otservbr-global/scripts/spells/monster/large_purple_ring_smoke.lua
create mode 100644 data-otservbr-global/scripts/spells/monster/large_red_ring.lua
diff --git a/data-otservbr-global/lib/core/storages.lua b/data-otservbr-global/lib/core/storages.lua
index da86c8b9bb9..ba71dfedb6f 100644
--- a/data-otservbr-global/lib/core/storages.lua
+++ b/data-otservbr-global/lib/core/storages.lua
@@ -2844,6 +2844,11 @@ Storage = {
},
},
},
+ U13_20 = { -- update 13.20 - Reserved Storages 47952 - 47970
+ RottenBlood = {
+ AccessDoor = 47952,
+ },
+ },
},
-- Reserved storage from 63951 - 63999
ThaisExhibition = {
diff --git a/data-otservbr-global/monster/bosses/bakragore.lua b/data-otservbr-global/monster/bosses/bakragore.lua
new file mode 100644
index 00000000000..2a0ca5c60fc
--- /dev/null
+++ b/data-otservbr-global/monster/bosses/bakragore.lua
@@ -0,0 +1,159 @@
+local mType = Game.createMonsterType("Bakragore")
+local monster = {}
+
+monster.description = "Bakragore"
+monster.experience = 15000000
+monster.outfit = {
+ lookType = 1671,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.events = {
+ "RottenBloodBakragoreDeath",
+}
+
+monster.bosstiary = {
+ bossRaceId = 2367,
+ bossRace = RARITY_NEMESIS,
+}
+
+monster.health = 660000
+monster.maxHealth = 660000
+monster.race = "undead"
+monster.corpse = 44012
+monster.speed = 250
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 10000,
+ chance = 20,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = true,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 98,
+ targetDistance = 1,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.summon = {
+ maxSummons = 2,
+ summons = {
+ { name = "Elder Bloodjaw", chance = 20, interval = 2000, count = 2 },
+ },
+}
+
+monster.voices = {
+ interval = 5000,
+ chance = 10,
+ { text = "Light ... darkens!", yell = false },
+ { text = "Light .. the ... darkness!", yell = false },
+ { text = "Darkness ... is ... light!", yell = false },
+ { text = "WILL ... PUNISH ... YOU!", yell = false },
+ { text = "RAAAR!", yell = false },
+}
+
+monster.loot = {
+ { name = "crystal coin", chance = 8938, maxCount = 200 },
+ { name = "supreme health potion", chance = 8938, maxCount = 300 },
+ { name = "ultimate mana potion", chance = 11433, maxCount = 300 },
+ { name = "ultimate spirit potion", chance = 11433, maxCount = 300 },
+ { name = "berserk potion", chance = 10938, maxCount = 90 },
+ { name = "bullseye potion", chance = 10938, maxCount = 90 },
+ { name = "mastermind potion", chance = 10938, maxCount = 30 },
+ { name = "blue gem", chance = 10570, maxCount = 10 },
+ { name = "giant amethyst", chance = 10570, maxCount = 10 },
+ { name = "giant emerald", chance = 10570, maxCount = 10 },
+ { name = "giant ruby", chance = 10570, maxCount = 10 },
+ { name = "red gem", chance = 10570, maxCount = 10 },
+ { name = "giant sapphire", chance = 10570, maxCount = 10 },
+ { name = "giant topaz", chance = 10570, maxCount = 10 },
+ { name = "violet gem", chance = 10970, maxCount = 10 },
+ { name = "yellow gem", chance = 10970, maxCount = 10 },
+ { name = "figurine of bakragore", chance = 10970 },
+ { name = "bakragore's amalgamation", chance = 570 },
+ { name = "spiritual horseshoe", chance = 470 },
+ { id = 43895, chance = 360 }, -- Bag you covet
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -3000 },
+ { name = "combat", interval = 3000, chance = 35, type = COMBAT_ICEDAMAGE, minDamage = -900, maxDamage = -1100, range = 7, radius = 7, shootEffect = CONST_ANI_ICE, effect = 243, target = true },
+ { name = "combat", interval = 2000, chance = 13, type = COMBAT_DEATHDAMAGE, minDamage = -100, maxDamage = -1000, length = 8, spread = 0, effect = 252, target = false },
+ { name = "combat", interval = 3000, chance = 30, type = COMBAT_FIREDAMAGE, minDamage = -1000, maxDamage = -2000, length = 8, spread = 0, effect = 249, target = false },
+ { name = "combat", interval = 2000, chance = 30, type = COMBAT_ICEDAMAGE, minDamage = -950, maxDamage = -2400, range = 7, radius = 3, shootEffect = 37, effect = 240, target = true },
+ { name = "combat", interval = 2000, chance = 10, type = COMBAT_DEATHDAMAGE, minDamage = -1000, maxDamage = -2500, length = 8, spread = 0, effect = 244, target = false },
+}
+
+monster.defenses = {
+ defense = 135,
+ armor = 135,
+ { name = "combat", interval = 3000, chance = 15, type = COMBAT_HEALING, minDamage = 2500, maxDamage = 3500, effect = 236, target = false },
+ { name = "speed", interval = 4000, chance = 80, speedChange = 700, effect = CONST_ME_MAGIC_RED, target = false, duration = 6000 },
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 15 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 15 },
+ { type = COMBAT_EARTHDAMAGE, percent = 15 },
+ { type = COMBAT_FIREDAMAGE, percent = 15 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 15 },
+ { type = COMBAT_HOLYDAMAGE, percent = 15 },
+ { type = COMBAT_DEATHDAMAGE, percent = 15 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType.onThink = function(monster, interval) end
+
+mType.onAppear = function(monster, creature)
+ if monster:getType():isRewardBoss() then
+ monster:setReward(true)
+ end
+end
+
+mType.onDisappear = function(monster, creature) end
+
+mType.onMove = function(monster, creature, fromPosition, toPosition) end
+
+mType.onSay = function(monster, creature, type, message) end
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/bosses/chagorz.lua b/data-otservbr-global/monster/bosses/chagorz.lua
new file mode 100644
index 00000000000..bec5b74e9e8
--- /dev/null
+++ b/data-otservbr-global/monster/bosses/chagorz.lua
@@ -0,0 +1,147 @@
+local mType = Game.createMonsterType("Chagorz")
+local monster = {}
+
+monster.description = "Chagorz"
+monster.experience = 3250000
+monster.outfit = {
+ lookType = 1665,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.events = {
+ "RottenBloodBossDeath",
+}
+
+monster.bosstiary = {
+ bossRaceId = 2366,
+ bossRace = RARITY_ARCHFOE,
+}
+
+monster.health = 350000
+monster.maxHealth = 350000
+monster.race = "undead"
+monster.corpse = 44024
+monster.speed = 250
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 10000,
+ chance = 20,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = true,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 98,
+ targetDistance = 1,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.summon = {}
+
+monster.voices = {
+ interval = 5000,
+ chance = 10,
+ { text = "The light... that... drains!", yell = false },
+ { text = "RAAAR!", yell = false },
+ { text = "WILL ... PUNISH ... YOU!", yell = false },
+ { text = "Darkness ... devours!", yell = false },
+}
+
+monster.loot = {
+ { name = "crystal coin", chance = 5441, maxCount = 108 },
+ { name = "mastermind potion", chance = 5530, maxCount = 28 },
+ { name = "supreme health potion", chance = 5044, maxCount = 154 },
+ { name = "giant sapphire", chance = 10546, maxCount = 1 },
+ { name = "ultimate mana potion", chance = 5752, maxCount = 107 },
+ { name = "violet gem", chance = 13217, maxCount = 4 },
+ { id = 3039, chance = 13465, maxCount = 1 }, -- red gem
+ { name = "yellow gem", chance = 14071, maxCount = 1 },
+ { name = "blue gem", chance = 11156, maxCount = 3 },
+ { name = "bullseye potion", chance = 6792, maxCount = 21 },
+ { name = "giant amethyst", chance = 11603, maxCount = 1 },
+ { name = "giant topaz", chance = 12280, maxCount = 1 },
+ { name = "green gem", chance = 8348, maxCount = 1 },
+ { name = "ultimate spirit potion", chance = 10934, maxCount = 18 },
+ { name = "white gem", chance = 9600, maxCount = 3 },
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = -1300, maxDamage = -2250 },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_EARTHDAMAGE, minDamage = -500, maxDamage = -900, radius = 4, effect = CONST_ME_GREEN_RINGS, target = false },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_DEATHDAMAGE, minDamage = -500, maxDamage = -900, range = 4, radius = 4, effect = 241, target = true },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_EARTHDAMAGE, minDamage = -1000, maxDamage = -1200, length = 10, spread = 0, effect = CONST_ME_POFF, target = false },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_LIFEDRAIN, minDamage = -1500, maxDamage = -1900, length = 10, spread = 0, effect = 225, target = false },
+ { name = "speed", interval = 2000, chance = 20, speedChange = -600, radius = 7, effect = CONST_ME_MAGIC_GREEN, target = false, duration = 20000 },
+}
+
+monster.defenses = {
+ defense = 105,
+ armor = 105,
+ { name = "combat", interval = 3000, chance = 10, type = COMBAT_HEALING, minDamage = 700, maxDamage = 1500, effect = 236, target = false },
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 15 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 15 },
+ { type = COMBAT_EARTHDAMAGE, percent = 15 },
+ { type = COMBAT_FIREDAMAGE, percent = 15 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 15 },
+ { type = COMBAT_HOLYDAMAGE, percent = 15 },
+ { type = COMBAT_DEATHDAMAGE, percent = 15 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType.onThink = function(monster, interval) end
+
+mType.onAppear = function(monster, creature)
+ if monster:getType():isRewardBoss() then
+ monster:setReward(true)
+ end
+end
+
+mType.onDisappear = function(monster, creature) end
+
+mType.onMove = function(monster, creature, fromPosition, toPosition) end
+
+mType.onSay = function(monster, creature, type, message) end
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/bosses/ichgahal.lua b/data-otservbr-global/monster/bosses/ichgahal.lua
new file mode 100644
index 00000000000..7d0d21924e4
--- /dev/null
+++ b/data-otservbr-global/monster/bosses/ichgahal.lua
@@ -0,0 +1,154 @@
+local mType = Game.createMonsterType("Ichgahal")
+local monster = {}
+
+monster.description = "Ichgahal"
+monster.experience = 3250000
+monster.outfit = {
+ lookType = 1665,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.events = {
+ "RottenBloodBossDeath",
+}
+
+monster.bosstiary = {
+ bossRaceId = 2364,
+ bossRace = RARITY_NEMESIS,
+}
+
+monster.health = 350000
+monster.maxHealth = 350000
+monster.race = "undead"
+monster.corpse = 44018
+monster.speed = 250
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 10000,
+ chance = 20,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = true,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 98,
+ targetDistance = 1,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.summon = {
+ maxSummons = 8,
+ summons = {
+ { name = "Mushroom", chance = 30, interval = 5000, count = 8 },
+ },
+}
+
+monster.voices = {
+ interval = 5000,
+ chance = 10,
+ { text = "Rott!!", yell = false },
+ { text = "Putrefy!", yell = false },
+ { text = "Decay!", yell = false },
+}
+
+monster.loot = {
+ { name = "crystal coin", chance = 14615, maxCount = 115 },
+ { name = "ultimate spirit potion", chance = 7169, maxCount = 153 },
+ { name = "mastermind potion", chance = 14651, maxCount = 45 },
+ { name = "yellow gem", chance = 9243, maxCount = 5 },
+ { name = "amber with a bug", chance = 7224, maxCount = 2 },
+ { name = "ultimate mana potion", chance = 13137, maxCount = 179 },
+ { name = "violet gem", chance = 14447, maxCount = 4 },
+ { name = "raw watermelon tourmaline", chance = 6788, maxCount = 2 },
+ { id = 3039, chance = 9047, maxCount = 1 }, -- red gem
+ { name = "supreme health potion", chance = 14635, maxCount = 37 },
+ { name = "berserk potion", chance = 14973, maxCount = 45 },
+ { name = "amber with a dragonfly", chance = 6470, maxCount = 1 },
+ { name = "gold ingot", chance = 11421, maxCount = 1 },
+ { name = "blue gem", chance = 8394, maxCount = 1 },
+ { name = "bullseye potion", chance = 13783, maxCount = 36 },
+ { name = "putrefactive figurine", chance = 11416, maxCount = 1 },
+ { name = "ichgahal's fungal infestation", chance = 7902, maxCount = 1 },
+ { name = "white gem", chance = 13559, maxCount = 3 },
+ { id = 43895, chance = 360 }, -- Bag you covet
+}
+
+monster.attacks = {
+ { name = "melee", interval = 3000, chance = 100, minDamage = -1500, maxDamage = -2300 },
+ { name = "combat", interval = 1000, chance = 10, type = COMBAT_PHYSICALDAMAGE, minDamage = -700, maxDamage = -1000, length = 12, spread = 0, effect = 249, target = false },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_MANADRAIN, minDamage = -2600, maxDamage = -2300, length = 12, spread = 0, effect = 193, target = false },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_FIREDAMAGE, minDamage = -900, maxDamage = -1500, length = 6, spread = 0, effect = CONST_ME_FIREAREA, target = false },
+ { name = "speed", interval = 2000, chance = 35, speedChange = -600, radius = 8, effect = CONST_ME_MAGIC_RED, target = false, duration = 15000 },
+}
+
+monster.defenses = {
+ defense = 105,
+ armor = 105,
+ { name = "combat", interval = 3000, chance = 10, type = COMBAT_HEALING, minDamage = 800, maxDamage = 1200, effect = 236, target = false },
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 15 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 15 },
+ { type = COMBAT_EARTHDAMAGE, percent = 15 },
+ { type = COMBAT_FIREDAMAGE, percent = 15 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 15 },
+ { type = COMBAT_HOLYDAMAGE, percent = 15 },
+ { type = COMBAT_DEATHDAMAGE, percent = 15 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType.onThink = function(monster, interval) end
+
+mType.onAppear = function(monster, creature)
+ if monster:getType():isRewardBoss() then
+ monster:setReward(true)
+ end
+end
+
+mType.onDisappear = function(monster, creature) end
+
+mType.onMove = function(monster, creature, fromPosition, toPosition) end
+
+mType.onSay = function(monster, creature, type, message) end
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/bosses/murcion.lua b/data-otservbr-global/monster/bosses/murcion.lua
new file mode 100644
index 00000000000..ad091f74758
--- /dev/null
+++ b/data-otservbr-global/monster/bosses/murcion.lua
@@ -0,0 +1,140 @@
+local mType = Game.createMonsterType("Murcion")
+local monster = {}
+
+monster.description = "Murcion"
+monster.experience = 3250000
+monster.outfit = {
+ lookType = 1664,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.events = {
+ "RottenBloodBossDeath",
+}
+
+monster.bosstiary = {
+ bossRaceId = 2362,
+ bossRace = RARITY_NEMESIS,
+}
+
+monster.health = 350000
+monster.maxHealth = 350000
+monster.race = "undead"
+monster.corpse = 44015
+monster.speed = 250
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 10000,
+ chance = 20,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = true,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 98,
+ targetDistance = 1,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.summon = {
+ maxSummons = 8,
+ summons = {
+ { name = "Mushroom", chance = 30, interval = 5000, count = 8 },
+ },
+}
+
+monster.voices = {}
+
+monster.loot = {
+ { name = "crystal coin", chance = 12317, maxCount = 91 },
+ { id = 3039, chance = 10896, maxCount = 2 }, -- red gem
+ { name = "amber with a bug", chance = 14590, maxCount = 1 },
+ { name = "amber with a dragonfly", chance = 5405, maxCount = 1 },
+ { name = "bullseye potion", chance = 10821, maxCount = 44 },
+ { name = "green gem", chance = 7763, maxCount = 4 },
+ { name = "mastermind potion", chance = 9534, maxCount = 15 },
+ { name = "supreme health potion", chance = 6212, maxCount = 102 },
+ { name = "ultimate mana potion", chance = 8785, maxCount = 29 },
+ { name = "ultimate spirit potion", chance = 8783, maxCount = 161 },
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = -1400, maxDamage = -2300 },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_DEATHDAMAGE, minDamage = -500, maxDamage = -900, radius = 4, effect = CONST_ME_SMALLCLOUDS, target = false },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_HOLYDAMAGE, minDamage = -500, maxDamage = -900, range = 4, radius = 4, shootEffect = 31, effect = 248, target = true },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_ICEDAMAGE, minDamage = -1000, maxDamage = -1200, length = 10, spread = 0, effect = 53, target = false },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_PHYSICALDAMAGE, minDamage = -1500, maxDamage = -1900, length = 10, spread = 0, effect = 158, target = false },
+ { name = "speed", interval = 2000, chance = 20, speedChange = -600, radius = 7, effect = CONST_ME_POFF, target = false, duration = 20000 },
+}
+
+monster.defenses = {
+ defense = 105,
+ armor = 105,
+ { name = "combat", interval = 3000, chance = 10, type = COMBAT_HEALING, minDamage = 800, maxDamage = 1500, effect = 236, target = false },
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 15 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 15 },
+ { type = COMBAT_EARTHDAMAGE, percent = 15 },
+ { type = COMBAT_FIREDAMAGE, percent = 15 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 15 },
+ { type = COMBAT_HOLYDAMAGE, percent = 15 },
+ { type = COMBAT_DEATHDAMAGE, percent = 15 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType.onThink = function(monster, interval) end
+
+mType.onAppear = function(monster, creature)
+ if monster:getType():isRewardBoss() then
+ monster:setReward(true)
+ end
+end
+
+mType.onDisappear = function(monster, creature) end
+
+mType.onMove = function(monster, creature, fromPosition, toPosition) end
+
+mType.onSay = function(monster, creature, type, message) end
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/bosses/vemiath.lua b/data-otservbr-global/monster/bosses/vemiath.lua
new file mode 100644
index 00000000000..97be142a621
--- /dev/null
+++ b/data-otservbr-global/monster/bosses/vemiath.lua
@@ -0,0 +1,151 @@
+local mType = Game.createMonsterType("Vemiath")
+local monster = {}
+
+monster.description = "Vemiath"
+monster.experience = 3250000
+monster.outfit = {
+ lookType = 1665,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.events = {
+ "RottenBloodBossDeath",
+}
+
+monster.bosstiary = {
+ bossRaceId = 2365,
+ bossRace = RARITY_ARCHFOE,
+}
+
+monster.health = 350000
+monster.maxHealth = 350000
+monster.race = "undead"
+monster.corpse = 44021
+monster.speed = 250
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 10000,
+ chance = 20,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = true,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 98,
+ targetDistance = 1,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.summon = {}
+
+monster.voices = {
+ interval = 5000,
+ chance = 10,
+ { text = "The light... that... drains!", yell = false },
+ { text = "RAAAR!", yell = false },
+ { text = "WILL ... PUNISH ... YOU!", yell = false },
+ { text = "Darkness ... devours!", yell = false },
+}
+
+monster.loot = {
+ { name = "crystal coin", chance = 8852, maxCount = 125 },
+ { name = "ultimate mana potion", chance = 11337, maxCount = 211 },
+ { name = "giant emerald", chance = 6423, maxCount = 1 },
+ { name = "supreme health potion", chance = 8385, maxCount = 179 },
+ { name = "yellow gem", chance = 8604, maxCount = 5 },
+ { name = "berserk potion", chance = 9395, maxCount = 45 },
+ { name = "blue gem", chance = 14144, maxCount = 5 },
+ { name = "green gem", chance = 6221, maxCount = 4 },
+ { name = "bullseye potion", chance = 6530, maxCount = 26 },
+ { name = "mastermind potion", chance = 5700, maxCount = 44 },
+ { name = "ultimate spirit potion", chance = 9216, maxCount = 25 },
+ { name = "giant topaz", chance = 11191, maxCount = 1 },
+ { name = "giant amethyst", chance = 8527, maxCount = 1 },
+ { name = "gold ingot", chance = 10866, maxCount = 1 },
+ { id = 3039, chance = 8945, maxCount = 1 }, -- red gem
+ { name = "dragon figurine", chance = 11502, maxCount = 1 },
+ { name = "raw watermelon tourmaline", chance = 9302, maxCount = 1 },
+ { name = "vemiath's infused basalt", chance = 7914, maxCount = 1 },
+ { name = "violet gem", chance = 7210, maxCount = 1 },
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = -1500, maxDamage = -2500 },
+ { name = "combat", interval = 3000, chance = 20, type = COMBAT_FIREDAMAGE, minDamage = -500, maxDamage = -1000, length = 10, spread = 3, effect = 244, target = false },
+ { name = "speed", interval = 2000, chance = 25, speedChange = -600, radius = 7, effect = CONST_ME_MAGIC_RED, target = false, duration = 15000 },
+ { name = "combat", interval = 2000, chance = 15, type = COMBAT_ICEDAMAGE, minDamage = -300, maxDamage = -700, radius = 5, effect = 243, target = false },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_DEATHDAMAGE, minDamage = -500, maxDamage = -800, length = 10, spread = 3, effect = CONST_ME_EXPLOSIONHIT, target = false },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_FIREDAMAGE, minDamage = -500, maxDamage = -800, length = 8, spread = 3, effect = CONST_ME_FIREATTACK, target = false },
+}
+
+monster.defenses = {
+ defense = 105,
+ armor = 105,
+ { name = "combat", interval = 3000, chance = 10, type = COMBAT_HEALING, minDamage = 800, maxDamage = 1500, effect = 236, target = false },
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 15 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 15 },
+ { type = COMBAT_EARTHDAMAGE, percent = 15 },
+ { type = COMBAT_FIREDAMAGE, percent = 15 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 15 },
+ { type = COMBAT_HOLYDAMAGE, percent = 15 },
+ { type = COMBAT_DEATHDAMAGE, percent = 15 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType.onThink = function(monster, interval) end
+
+mType.onAppear = function(monster, creature)
+ if monster:getType():isRewardBoss() then
+ monster:setReward(true)
+ end
+end
+
+mType.onDisappear = function(monster, creature) end
+
+mType.onMove = function(monster, creature, fromPosition, toPosition) end
+
+mType.onSay = function(monster, creature, type, message) end
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/bloated_man-maggot.lua b/data-otservbr-global/monster/quests/rotten_blood/bloated_man-maggot.lua
new file mode 100644
index 00000000000..76c2ecb9fb3
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/bloated_man-maggot.lua
@@ -0,0 +1,122 @@
+local mType = Game.createMonsterType("Bloated Man-Maggot")
+local monster = {}
+
+monster.description = "a bloated man-maggot"
+monster.experience = 21570
+monster.outfit = {
+ lookType = 1654,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.raceId = 2392
+monster.Bestiary = {
+ class = "Vermin",
+ race = BESTY_RACE_VERMIN,
+ toKill = 5000,
+ FirstUnlock = 200,
+ SecondUnlock = 2000,
+ CharmsPoints = 100,
+ Stars = 5,
+ Occurrence = 0,
+ Locations = "Jaded Roots",
+}
+
+monster.health = 31700
+monster.maxHealth = 31700
+monster.race = "undead"
+monster.corpse = 43816
+monster.speed = 195
+monster.manaCost = 305
+
+monster.changeTarget = {
+ interval = 5000,
+ chance = 10,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = true,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 90,
+ targetDistance = 1,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.voices = {}
+
+monster.loot = {
+ { name = "crystal coin", chance = 12961, maxCount = 1 },
+ { name = "organic acid", chance = 11678, maxCount = 1 },
+ { name = "might ring", chance = 10020, maxCount = 1 },
+ { name = "small emerald", chance = 9133, maxCount = 5 },
+ { name = "rotten roots", chance = 8637, maxCount = 1 },
+ { name = "bloated maggot", chance = 8133, maxCount = 1 },
+ { name = "terra rod", chance = 8078, maxCount = 1 },
+ { name = "butcher's axe", chance = 7967, maxCount = 1 },
+ { name = "blue gem", chance = 7808, maxCount = 1 },
+ { name = "violet gem", chance = 7084, maxCount = 1 },
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -1500 },
+ { name = "combat", interval = 2500, chance = 25, type = COMBAT_PHYSICALDAMAGE, minDamage = -1400, maxDamage = -1700, radius = 5, effect = CONST_ME_GHOSTLY_BITE, target = true },
+ { name = "combat", interval = 2500, chance = 20, type = COMBAT_EARTHDAMAGE, minDamage = -1400, maxDamage = -1900, radius = 5, effect = CONST_ME_BIGPLANTS, target = false },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_PHYSICALDAMAGE, minDamage = -1400, maxDamage = -1550, length = 8, spread = 3, effect = CONST_ME_GROUNDSHAKER, target = false },
+ { name = "largefirering", interval = 2500, chance = 15, minDamage = -1400, maxDamage = -1800, target = false },
+}
+
+monster.defenses = {
+ defense = 104,
+ armor = 104,
+ mitigation = 3.16,
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 45 },
+ { type = COMBAT_ENERGYDAMAGE, percent = -15 },
+ { type = COMBAT_EARTHDAMAGE, percent = 40 },
+ { type = COMBAT_FIREDAMAGE, percent = 15 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = -15 },
+ { type = COMBAT_HOLYDAMAGE, percent = -5 },
+ { type = COMBAT_DEATHDAMAGE, percent = 5 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = false },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/converter.lua b/data-otservbr-global/monster/quests/rotten_blood/converter.lua
new file mode 100644
index 00000000000..65ebab91985
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/converter.lua
@@ -0,0 +1,119 @@
+local mType = Game.createMonsterType("Converter")
+local monster = {}
+
+monster.description = "a converter"
+monster.experience = 21425
+monster.outfit = {
+ lookType = 1623,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.raceId = 2379
+monster.Bestiary = {
+ class = "Elemental",
+ race = BESTY_RACE_ELEMENTAL,
+ toKill = 5000,
+ FirstUnlock = 200,
+ SecondUnlock = 2000,
+ CharmsPoints = 100,
+ Stars = 5,
+ Occurrence = 0,
+ Locations = "Gloom Pillars.",
+}
+
+monster.health = 29600
+monster.maxHealth = 29600
+monster.race = "undead"
+monster.corpse = 43567
+monster.speed = 250
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 4000,
+ chance = 10,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = true,
+ canPushItems = true,
+ canPushCreatures = false,
+ staticAttackChance = 80,
+ targetDistance = 1,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = false,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.loot = {
+ { name = "crystal coin", chance = 5230, maxCount = 1 },
+ { name = "darklight obsidian axe", chance = 6963, maxCount = 1 },
+ { name = "darklight matter", chance = 6927, maxCount = 1 },
+ { name = "darklight core", chance = 10715, maxCount = 1 },
+ { name = "wand of starstorm", chance = 8797, maxCount = 1 },
+ { name = "blue gem", chance = 9372, maxCount = 1 },
+ { name = "ultimate health potion", chance = 9851, maxCount = 5 },
+ { name = "focus cape", chance = 6945, maxCount = 1 },
+ { name = "white gem", chance = 14533, maxCount = 1 },
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -900 },
+ { name = "combat", interval = 2000, chance = 10, type = COMBAT_ENERGYDAMAGE, minDamage = -1400, maxDamage = -1900, length = 7, spread = 0, effect = CONST_ME_PINK_ENERGY_SPARK, target = false },
+ { name = "combat", interval = 2500, chance = 20, type = COMBAT_HOLYDAMAGE, minDamage = -1500, maxDamage = -1600, radius = 5, effect = CONST_ME_GHOSTLY_BITE, target = true },
+ { name = "largeholyring", interval = 2000, chance = 15, minDamage = -1400, maxDamage = -1900 },
+ { name = "energy chain", interval = 3200, chance = 20, minDamage = -800, maxDamage = -1200 },
+}
+
+monster.defenses = {
+ defense = 100,
+ armor = 100,
+ mitigation = 3.31,
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = -20 },
+ { type = COMBAT_ENERGYDAMAGE, percent = -10 },
+ { type = COMBAT_EARTHDAMAGE, percent = 10 },
+ { type = COMBAT_FIREDAMAGE, percent = 25 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 0 },
+ { type = COMBAT_HOLYDAMAGE, percent = 35 },
+ { type = COMBAT_DEATHDAMAGE, percent = -15 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/darklight_construct.lua b/data-otservbr-global/monster/quests/rotten_blood/darklight_construct.lua
new file mode 100644
index 00000000000..c4b2c5e8568
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/darklight_construct.lua
@@ -0,0 +1,120 @@
+local mType = Game.createMonsterType("Darklight Construct")
+local monster = {}
+
+monster.description = "a darklight construct"
+monster.experience = 22050
+monster.outfit = {
+ lookType = 1622,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.raceId = 2378
+monster.Bestiary = {
+ class = "Magical",
+ race = BESTY_RACE_MAGICAL,
+ toKill = 5000,
+ FirstUnlock = 200,
+ SecondUnlock = 2000,
+ CharmsPoints = 100,
+ Stars = 5,
+ Occurrence = 0,
+ Locations = "Darklight Core",
+}
+
+monster.health = 32200
+monster.maxHealth = 32200
+monster.race = "undead"
+monster.corpse = 43840
+monster.speed = 220
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 4000,
+ chance = 10,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 90,
+ targetDistance = 0,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.loot = {
+ { name = "crystal coin", chance = 11290, maxCount = 1 },
+ { name = "dark obsidian splinter", chance = 12735, maxCount = 1 },
+ { id = 3039, chance = 8781, maxCount = 1 }, -- red gem
+ { name = "small emerald", chance = 6646, maxCount = 3 },
+ { name = "zaoan shoes", chance = 8614, maxCount = 1 },
+ { name = "darklight core", chance = 5659, maxCount = 1 },
+ { name = "darklight obsidian axe", chance = 11129, maxCount = 1 },
+ { name = "magma amulet", chance = 13240, maxCount = 1 },
+ { name = "small ruby", chance = 12458, maxCount = 3 },
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -1050 },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_FIREDAMAGE, minDamage = -1300, maxDamage = -1500, length = 8, spread = 3, effect = CONST_ME_HITBYFIRE, target = false },
+ { name = "combat", interval = 2000, chance = 25, type = COMBAT_FIREDAMAGE, minDamage = -1100, maxDamage = -1400, radius = 5, effect = CONST_ME_HITBYFIRE, target = false },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_HOLYDAMAGE, minDamage = -1500, maxDamage = -1600, radius = 5, effect = CONST_ME_HOLYAREA, target = true },
+ { name = "extended fire chain", interval = 2000, chance = 15, minDamage = -800, maxDamage = -1200, target = true },
+ { name = "largefirering", interval = 2800, chance = 20, minDamage = -1000, maxDamage = -1300, target = false },
+}
+
+monster.defenses = {
+ defense = 117,
+ armor = 117,
+ mitigation = 2.98,
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = -15 },
+ { type = COMBAT_ENERGYDAMAGE, percent = -5 },
+ { type = COMBAT_EARTHDAMAGE, percent = 10 },
+ { type = COMBAT_FIREDAMAGE, percent = 55 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = -5 },
+ { type = COMBAT_HOLYDAMAGE, percent = 40 },
+ { type = COMBAT_DEATHDAMAGE, percent = -20 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/darklight_emitter.lua b/data-otservbr-global/monster/quests/rotten_blood/darklight_emitter.lua
new file mode 100644
index 00000000000..dc211745db4
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/darklight_emitter.lua
@@ -0,0 +1,118 @@
+local mType = Game.createMonsterType("Darklight Emitter")
+local monster = {}
+
+monster.description = "a darklight emitter"
+monster.experience = 20600
+monster.outfit = {
+ lookType = 1627,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.raceId = 2382
+monster.Bestiary = {
+ class = "Magical",
+ race = BESTY_RACE_MAGICAL,
+ toKill = 5000,
+ FirstUnlock = 200,
+ SecondUnlock = 2000,
+ CharmsPoints = 100,
+ Stars = 5,
+ Occurrence = 0,
+ Locations = "Darklight Core",
+}
+
+monster.health = 27500
+monster.maxHealth = 27500
+monster.race = "undead"
+monster.corpse = 43583
+monster.speed = 210
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 4000,
+ chance = 10,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 90,
+ targetDistance = 0,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.loot = {
+ { name = "crystal coin", chance = 12516, maxCount = 2 },
+ { name = "darklight core", chance = 13367, maxCount = 1 },
+ { name = "darklight obsidian axe", chance = 10433, maxCount = 1 },
+ { name = "zaoan armor", chance = 8574, maxCount = 1 },
+ { name = "basalt crumbs", chance = 5794, maxCount = 1 },
+ { name = "small topaz", chance = 5784, maxCount = 3 },
+ { name = "amber staff", chance = 6240, maxCount = 1 },
+ { id = 3039, chance = 8459, maxCount = 1 }, -- red gem
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -1050 },
+ { name = "combat", interval = 2600, chance = 20, type = COMBAT_FIREDAMAGE, minDamage = -1400, maxDamage = -1750, length = 8, spread = 3, effect = CONST_ME_HITBYFIRE, target = false },
+ { name = "combat", interval = 3100, chance = 20, type = COMBAT_HOLYDAMAGE, minDamage = -1000, maxDamage = -1600, length = 8, spread = 3, effect = CONST_ME_HOLYAREA, target = false },
+ { name = "combat", interval = 2600, chance = 20, type = COMBAT_FIREDAMAGE, minDamage = -1200, maxDamage = -1650, radius = 5, effect = CONST_ME_HITBYFIRE, target = true },
+ { name = "largefirering", interval = 2000, chance = 10, minDamage = -800, maxDamage = -1400, target = false },
+}
+
+monster.defenses = {
+ defense = 120,
+ armor = 120,
+ mitigation = 3.04,
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = -15 },
+ { type = COMBAT_ENERGYDAMAGE, percent = -10 },
+ { type = COMBAT_EARTHDAMAGE, percent = 5 },
+ { type = COMBAT_FIREDAMAGE, percent = 40 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 0 },
+ { type = COMBAT_HOLYDAMAGE, percent = 25 },
+ { type = COMBAT_DEATHDAMAGE, percent = -20 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/darklight_matter.lua b/data-otservbr-global/monster/quests/rotten_blood/darklight_matter.lua
new file mode 100644
index 00000000000..10d4ff36d84
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/darklight_matter.lua
@@ -0,0 +1,127 @@
+local mType = Game.createMonsterType("Darklight Matter")
+local monster = {}
+
+monster.description = "a darklight matter"
+monster.experience = 22250
+monster.outfit = {
+ lookType = 1624,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.raceId = 2380
+monster.Bestiary = {
+ class = "Slime",
+ race = BESTY_RACE_SLIME,
+ toKill = 5000,
+ FirstUnlock = 200,
+ SecondUnlock = 2000,
+ CharmsPoints = 100,
+ Stars = 5,
+ Occurrence = 0,
+ Locations = "Darklight Core.",
+}
+
+monster.health = 30150
+monster.maxHealth = 30150
+monster.race = "venom"
+monster.corpse = 43571
+monster.speed = 230
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 5000,
+ chance = 0,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = false,
+ staticAttackChance = 85,
+ targetDistance = 1,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = false,
+ canWalkOnFire = false,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.voices = {
+ interval = 5000,
+ chance = 10,
+ { text = "*twiggle*", yell = false },
+ { text = "SSSSHRRR...", yell = false },
+}
+
+monster.loot = {
+ { name = "crystal coin", chance = 11755, maxCount = 1 },
+ { name = "unstable darklight matter", chance = 9060, maxCount = 1 },
+ { name = "darklight core", chance = 12887, maxCount = 1 },
+ { name = "ultimate health potion", chance = 6553, maxCount = 6 },
+ { id = 3039, chance = 5852, maxCount = 1 }, -- red gem
+ { name = "darklight matter", chance = 8849, maxCount = 1 },
+ { name = "rubber cap", chance = 7180, maxCount = 1 },
+ { id = 23544, chance = 3500, maxCount = 1 }, -- collar of red plasma
+ { name = "green gem", chance = 3500, maxCount = 1 },
+ { name = "shadow sceptre", chance = 3500, maxCount = 1 },
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -1100 },
+ { name = "combat", interval = 2500, chance = 15, type = COMBAT_ENERGYDAMAGE, minDamage = -1400, maxDamage = -1800, radius = 5, effect = CONST_ME_PURPLESMOKE, target = true },
+ { name = "combat", interval = 2500, chance = 20, type = COMBAT_PHYSICALDAMAGE, minDamage = -1500, maxDamage = -1600, radius = 5, effect = CONST_ME_GHOSTLY_BITE, target = true },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_ENERGYDAMAGE, minDamage = -1400, maxDamage = -1650, length = 8, spread = 3, effect = CONST_ME_ELECTRICALSPARK, target = false },
+ { name = "largeredring", interval = 2000, chance = 15, minDamage = -800, maxDamage = -1500, target = false },
+}
+
+monster.defenses = {
+ defense = 98,
+ armor = 98,
+ mitigation = 3.28,
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = -10 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 40 },
+ { type = COMBAT_EARTHDAMAGE, percent = -10 },
+ { type = COMBAT_FIREDAMAGE, percent = -25 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 20 },
+ { type = COMBAT_HOLYDAMAGE, percent = 0 },
+ { type = COMBAT_DEATHDAMAGE, percent = 0 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = false },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = false },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/darklight_source.lua b/data-otservbr-global/monster/quests/rotten_blood/darklight_source.lua
new file mode 100644
index 00000000000..b6e9c065399
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/darklight_source.lua
@@ -0,0 +1,117 @@
+local mType = Game.createMonsterType("Darklight Source")
+local monster = {}
+
+monster.description = "a darklight source"
+monster.experience = 22465
+monster.outfit = {
+ lookType = 1660,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.raceId = 2398
+monster.Bestiary = {
+ class = "Magical",
+ race = BESTY_RACE_MAGICAL,
+ toKill = 5000,
+ FirstUnlock = 200,
+ SecondUnlock = 2000,
+ CharmsPoints = 100,
+ Stars = 5,
+ Occurrence = 0,
+ Locations = "Darklight Core",
+}
+
+monster.health = 31550
+monster.maxHealth = 31550
+monster.race = "undead"
+monster.corpse = 43840
+monster.speed = 220
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 4000,
+ chance = 10,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 90,
+ targetDistance = 1,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.loot = {
+ { name = "crystal coin", chance = 5214, maxCount = 1 },
+ { name = "yellow darklight matter", chance = 9397, maxCount = 1 },
+ { name = "dark obsidian splinter", chance = 13215, maxCount = 1 },
+ { name = "darklight core", chance = 7570, maxCount = 1 },
+ { name = "small sapphire", chance = 5644, maxCount = 2 },
+ { name = "blue gem", chance = 12909, maxCount = 1 },
+ { name = "twiceslicer", chance = 11596, maxCount = 1 },
+ { name = "white gem", chance = 13964, maxCount = 1 },
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -1200 },
+ { name = "combat", interval = 2500, chance = 20, type = COMBAT_ENERGYDAMAGE, minDamage = -1400, maxDamage = -1650, length = 8, spread = 3, effect = CONST_ME_BLUE_ENERGY_SPARK, target = false },
+ { name = "combat", interval = 2500, chance = 20, type = COMBAT_HOLYDAMAGE, minDamage = -1500, maxDamage = -1600, radius = 5, effect = CONST_ME_GHOSTLY_BITE, target = false },
+ { name = "combat", interval = 2500, chance = 15, type = COMBAT_ENERGYDAMAGE, minDamage = -1300, maxDamage = -1500, radius = 5, effect = CONST_ME_ELECTRICALSPARK, target = false },
+}
+
+monster.defenses = {
+ defense = 115,
+ armor = 115,
+ mitigation = 3.19,
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = -15 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 55 },
+ { type = COMBAT_EARTHDAMAGE, percent = -10 },
+ { type = COMBAT_FIREDAMAGE, percent = -15 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 40 },
+ { type = COMBAT_HOLYDAMAGE, percent = 0 },
+ { type = COMBAT_DEATHDAMAGE, percent = 0 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/darklight_striker.lua b/data-otservbr-global/monster/quests/rotten_blood/darklight_striker.lua
new file mode 100644
index 00000000000..4d6a19d6dc9
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/darklight_striker.lua
@@ -0,0 +1,120 @@
+local mType = Game.createMonsterType("Darklight Striker")
+local monster = {}
+
+monster.description = "a darklight striker"
+monster.experience = 22200
+monster.outfit = {
+ lookType = 1661,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.raceId = 2399
+monster.Bestiary = {
+ class = "Magical",
+ race = BESTY_RACE_MAGICAL,
+ toKill = 5000,
+ FirstUnlock = 200,
+ SecondUnlock = 2000,
+ CharmsPoints = 100,
+ Stars = 5,
+ Occurrence = 0,
+ Locations = "Darklight Core",
+}
+
+monster.health = 29700
+monster.maxHealth = 29700
+monster.race = "undead"
+monster.corpse = 43844
+monster.speed = 210
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 4000,
+ chance = 10,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 90,
+ targetDistance = 1,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.loot = {
+ { name = "crystal coin", chance = 14380, maxCount = 1 },
+ { name = "unstable darklight matter", chance = 14492, maxCount = 1 },
+ { name = "darklight core", chance = 9783, maxCount = 1 },
+ { name = "small topaz", chance = 11140, maxCount = 3 },
+ { name = "ice rapier", chance = 5104, maxCount = 1 },
+ { name = "dark obsidian splinter", chance = 14185, maxCount = 1 },
+ { name = "blue gem", chance = 7355, maxCount = 1 },
+ { name = "crystal mace", chance = 8812, maxCount = 1 },
+ { name = "zaoan helmet", chance = 5572, maxCount = 1 },
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2500, chance = 100, minDamage = 0, maxDamage = -1100 },
+ { name = "combat", interval = 2500, chance = 15, type = COMBAT_ENERGYDAMAGE, minDamage = -1400, maxDamage = -1650, length = 8, spread = 3, effect = CONST_ME_ELECTRICALSPARK, target = false },
+ { name = "combat", interval = 2500, chance = 15, type = COMBAT_HOLYDAMAGE, minDamage = -1100, maxDamage = -1600, radius = 5, effect = CONST_ME_GHOSTLY_BITE, target = false },
+ { name = "combat", interval = 2500, chance = 10, type = COMBAT_ENERGYDAMAGE, minDamage = -1300, maxDamage = -1500, radius = 5, effect = CONST_ME_ELECTRICALSPARK, target = false },
+ { name = "extended holy chain", interval = 2000, chance = 15, minDamage = -800, maxDamage = -1200 },
+ { name = "largepinkring", interval = 2500, chance = 10, minDamage = -1500, maxDamage = -1900, target = false },
+}
+
+monster.defenses = {
+ defense = 112,
+ armor = 112,
+ mitigation = 3.10,
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 10 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 35 },
+ { type = COMBAT_EARTHDAMAGE, percent = -15 },
+ { type = COMBAT_FIREDAMAGE, percent = -25 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 30 },
+ { type = COMBAT_HOLYDAMAGE, percent = 10 },
+ { type = COMBAT_DEATHDAMAGE, percent = 0 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/echo_of_chagorz.lua b/data-otservbr-global/monster/quests/rotten_blood/echo_of_chagorz.lua
new file mode 100644
index 00000000000..3627ffe75be
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/echo_of_chagorz.lua
@@ -0,0 +1,101 @@
+local mType = Game.createMonsterType("Echo Of Chagorz")
+local monster = {}
+
+monster.description = "an echo of Chagorz"
+monster.experience = 0
+monster.outfit = {
+ lookType = 1670,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.health = 90000
+monster.maxHealth = 90000
+monster.race = "undead"
+monster.corpse = 0
+monster.speed = 100
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 2500,
+ chance = 40,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 98,
+ targetDistance = 1,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.voices = {
+ interval = 5000,
+ chance = 10,
+}
+
+monster.loot = {}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = -750, maxDamage = -1750 },
+ { name = "combat", interval = 2700, chance = 37, type = COMBAT_FIREDAMAGE, minDamage = -950, maxDamage = -2000, length = 8, spread = 3, effect = CONST_ME_HITBYFIRE, target = false },
+ { name = "combat", interval = 3300, chance = 37, type = COMBAT_PHYSICALDAMAGE, minDamage = -1100, maxDamage = -1600, length = 8, spread = 0, effect = CONST_ME_EXPLOSIONHIT, target = false },
+}
+
+monster.defenses = {
+ defense = 65,
+ armor = 0,
+ mitigation = 2.0,
+ { name = "combat", interval = 3000, chance = 35, type = COMBAT_HEALING, minDamage = 400, maxDamage = 500, effect = CONST_ME_MAGIC_BLUE, target = false },
+ { name = "speed", interval = 2000, chance = 15, speedChange = 320, effect = CONST_ME_MAGIC_RED, target = false, duration = 5000 },
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 0 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 0 },
+ { type = COMBAT_EARTHDAMAGE, percent = 0 },
+ { type = COMBAT_FIREDAMAGE, percent = 0 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 0 },
+ { type = COMBAT_HOLYDAMAGE, percent = 0 },
+ { type = COMBAT_DEATHDAMAGE, percent = 0 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/echo_of_ichgahal.lua b/data-otservbr-global/monster/quests/rotten_blood/echo_of_ichgahal.lua
new file mode 100644
index 00000000000..f35d2e13997
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/echo_of_ichgahal.lua
@@ -0,0 +1,101 @@
+local mType = Game.createMonsterType("Echo Of Ichgahal")
+local monster = {}
+
+monster.description = "an echo of Ichgahal"
+monster.experience = 0
+monster.outfit = {
+ lookType = 1669,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.health = 90000
+monster.maxHealth = 90000
+monster.race = "undead"
+monster.corpse = 0
+monster.speed = 100
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 2500,
+ chance = 40,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 98,
+ targetDistance = 1,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.voices = {
+ interval = 5000,
+ chance = 10,
+}
+
+monster.loot = {}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = -750, maxDamage = -1750 },
+ { name = "combat", interval = 2700, chance = 37, type = COMBAT_FIREDAMAGE, minDamage = -950, maxDamage = -2000, length = 8, spread = 3, effect = CONST_ME_HITBYFIRE, target = false },
+ { name = "combat", interval = 3300, chance = 37, type = COMBAT_PHYSICALDAMAGE, minDamage = -1100, maxDamage = -1600, length = 8, spread = 0, effect = CONST_ME_EXPLOSIONHIT, target = false },
+}
+
+monster.defenses = {
+ defense = 65,
+ armor = 0,
+ mitigation = 2.0,
+ { name = "combat", interval = 3000, chance = 35, type = COMBAT_HEALING, minDamage = 400, maxDamage = 500, effect = CONST_ME_MAGIC_BLUE, target = false },
+ { name = "speed", interval = 2000, chance = 15, speedChange = 320, effect = CONST_ME_MAGIC_RED, target = false, duration = 5000 },
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 0 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 0 },
+ { type = COMBAT_EARTHDAMAGE, percent = 0 },
+ { type = COMBAT_FIREDAMAGE, percent = 0 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 0 },
+ { type = COMBAT_HOLYDAMAGE, percent = 0 },
+ { type = COMBAT_DEATHDAMAGE, percent = 0 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/echo_of_murcion.lua b/data-otservbr-global/monster/quests/rotten_blood/echo_of_murcion.lua
new file mode 100644
index 00000000000..83a1b002d6b
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/echo_of_murcion.lua
@@ -0,0 +1,101 @@
+local mType = Game.createMonsterType("Echo Of Murcion")
+local monster = {}
+
+monster.description = "an echo of Murcion"
+monster.experience = 0
+monster.outfit = {
+ lookType = 1669,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.health = 90000
+monster.maxHealth = 90000
+monster.race = "undead"
+monster.corpse = 0
+monster.speed = 100
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 2500,
+ chance = 40,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 98,
+ targetDistance = 1,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.voices = {
+ interval = 5000,
+ chance = 10,
+}
+
+monster.loot = {}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = -750, maxDamage = -1750 },
+ { name = "combat", interval = 2700, chance = 37, type = COMBAT_FIREDAMAGE, minDamage = -950, maxDamage = -2000, length = 8, spread = 0, effect = CONST_ME_HITBYFIRE, target = false },
+ { name = "combat", interval = 3300, chance = 37, type = COMBAT_PHYSICALDAMAGE, minDamage = -1100, maxDamage = -1600, length = 8, spread = 0, effect = CONST_ME_EXPLOSIONHIT, target = false },
+}
+
+monster.defenses = {
+ defense = 65,
+ armor = 0,
+ mitigation = 2.0,
+ { name = "combat", interval = 3000, chance = 35, type = COMBAT_HEALING, minDamage = 400, maxDamage = 500, effect = CONST_ME_MAGIC_BLUE, target = false },
+ { name = "speed", interval = 2000, chance = 15, speedChange = 320, effect = CONST_ME_MAGIC_RED, target = false, duration = 5000 },
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 0 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 0 },
+ { type = COMBAT_EARTHDAMAGE, percent = 0 },
+ { type = COMBAT_FIREDAMAGE, percent = 0 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 0 },
+ { type = COMBAT_HOLYDAMAGE, percent = 0 },
+ { type = COMBAT_DEATHDAMAGE, percent = 0 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/echo_of_vemiath.lua b/data-otservbr-global/monster/quests/rotten_blood/echo_of_vemiath.lua
new file mode 100644
index 00000000000..d865ba9ae76
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/echo_of_vemiath.lua
@@ -0,0 +1,101 @@
+local mType = Game.createMonsterType("Echo Of Vemiath")
+local monster = {}
+
+monster.description = "an echo of Vemiath"
+monster.experience = 0
+monster.outfit = {
+ lookType = 1670,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.health = 90000
+monster.maxHealth = 90000
+monster.race = "undead"
+monster.corpse = 0
+monster.speed = 100
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 2500,
+ chance = 40,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 98,
+ targetDistance = 1,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.voices = {
+ interval = 5000,
+ chance = 10,
+}
+
+monster.loot = {}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = -750, maxDamage = -1750 },
+ { name = "combat", interval = 2700, chance = 37, type = COMBAT_FIREDAMAGE, minDamage = -950, maxDamage = -2000, length = 8, spread = 3, effect = CONST_ME_HITBYFIRE, target = false },
+ { name = "combat", interval = 3300, chance = 37, type = COMBAT_PHYSICALDAMAGE, minDamage = -1100, maxDamage = -1600, length = 8, spread = 0, effect = CONST_ME_EXPLOSIONHIT, target = false },
+}
+
+monster.defenses = {
+ defense = 65,
+ armor = 0,
+ mitigation = 2.0,
+ { name = "combat", interval = 3000, chance = 35, type = COMBAT_HEALING, minDamage = 400, maxDamage = 500, effect = CONST_ME_MAGIC_BLUE, target = false },
+ { name = "speed", interval = 2000, chance = 15, speedChange = 320, effect = CONST_ME_MAGIC_RED, target = false, duration = 5000 },
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 0 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 0 },
+ { type = COMBAT_EARTHDAMAGE, percent = 0 },
+ { type = COMBAT_FIREDAMAGE, percent = 0 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 0 },
+ { type = COMBAT_HOLYDAMAGE, percent = 0 },
+ { type = COMBAT_DEATHDAMAGE, percent = 0 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/elder_bloodjaw.lua b/data-otservbr-global/monster/quests/rotten_blood/elder_bloodjaw.lua
new file mode 100644
index 00000000000..37b63816566
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/elder_bloodjaw.lua
@@ -0,0 +1,102 @@
+local mType = Game.createMonsterType("Elder Bloodjaw")
+local monster = {}
+
+monster.description = "an elder bloodjaw"
+monster.experience = 0
+monster.outfit = {
+ lookType = 1628,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.health = 86000
+monster.maxHealth = 86000
+monster.race = "undead"
+monster.corpse = 43669
+monster.speed = 210
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 4000,
+ chance = 10,
+}
+
+monster.strategiesTarget = {
+ nearest = 80,
+ health = 10,
+ damage = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 90,
+ targetDistance = 1,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.voices = {
+ interval = 5000,
+ chance = 10,
+ { text = "SHWAARR!", yell = false },
+ { text = "SHWAARP!", yell = false },
+}
+
+monster.loot = {}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -490 },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_EARTHDAMAGE, minDamage = -220, maxDamage = -405, range = 7, radius = 1, shootEffect = CONST_ANI_POISON, target = true },
+ { name = "combat", interval = 2000, chance = 15, type = COMBAT_LIFEDRAIN, minDamage = -65, maxDamage = -135, radius = 4, effect = CONST_ME_MAGIC_GREEN, target = false },
+ { name = "drunk", interval = 2000, chance = 10, radius = 3, effect = CONST_ME_HITBYPOISON, target = false, duration = 5000 },
+ { name = "blightwalker curse", interval = 2000, chance = 15, target = false },
+ { name = "speed", interval = 2000, chance = 15, speedChange = -300, range = 7, shootEffect = CONST_ANI_POISON, target = true, duration = 30000 },
+}
+
+monster.defenses = {
+ defense = 100,
+ armor = 100,
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = -15 },
+ { type = COMBAT_ENERGYDAMAGE, percent = -10 },
+ { type = COMBAT_EARTHDAMAGE, percent = 5 },
+ { type = COMBAT_FIREDAMAGE, percent = 40 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 0 },
+ { type = COMBAT_HOLYDAMAGE, percent = 25 },
+ { type = COMBAT_DEATHDAMAGE, percent = -20 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/meandering_mushroom.lua b/data-otservbr-global/monster/quests/rotten_blood/meandering_mushroom.lua
new file mode 100644
index 00000000000..7dc04b95375
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/meandering_mushroom.lua
@@ -0,0 +1,121 @@
+local mType = Game.createMonsterType("Meandering Mushroom")
+local monster = {}
+
+monster.description = "a meandering mushroom"
+monster.experience = 21980
+monster.outfit = {
+ lookType = 1621,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.raceId = 2376
+monster.Bestiary = {
+ class = "Slime",
+ race = BESTY_RACE_SLIME,
+ toKill = 5000,
+ FirstUnlock = 200,
+ SecondUnlock = 2000,
+ CharmsPoints = 100,
+ Stars = 5,
+ Occurrence = 0,
+ Locations = "Putrefactory.",
+}
+
+monster.health = 29100
+monster.maxHealth = 29100
+monster.race = "undead"
+monster.corpse = 43559
+monster.speed = 205
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 5000,
+ chance = 0,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = false,
+ staticAttackChance = 85,
+ targetDistance = 0,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = false,
+ canWalkOnFire = false,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.loot = {
+ { name = "crystal coin", chance = 11755, maxCount = 1 },
+ { name = "lichen gobbler", chance = 9121, maxCount = 1 },
+ { name = "white mushroom", chance = 12998, maxCount = 3 },
+ { name = "rotten roots", chance = 9791, maxCount = 1 },
+ { name = "wand of decay", chance = 14668, maxCount = 1 },
+ { id = 3039, chance = 10406, maxCount = 1 }, -- red gem
+ { name = "worm sponge", chance = 10697, maxCount = 1 },
+ { name = "dark mushroom", chance = 12313, maxCount = 3 },
+ { name = "yellow gem", chance = 13520, maxCount = 1 },
+ { name = "brown mushroom", chance = 6422, maxCount = 3 },
+ { name = "terra amulet", chance = 13122, maxCount = 1 },
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -1150 },
+ { name = "combat", interval = 2500, chance = 15, type = COMBAT_DEATHDAMAGE, minDamage = -1800, maxDamage = -1900, radius = 5, effect = CONST_ME_MORTAREA, target = true },
+ { name = "combat", interval = 2500, chance = 15, type = COMBAT_DEATHDAMAGE, minDamage = -1700, maxDamage = -1700, radius = 5, effect = CONST_ME_INSECTS, target = true },
+ { name = "combat", interval = 2500, chance = 20, type = COMBAT_ICEDAMAGE, minDamage = -1100, maxDamage = -1300, length = 8, spread = 5, effect = CONST_ME_BLACKSMOKE, target = false },
+ { name = "largeblackring", interval = 2000, chance = 10, minDamage = -900, maxDamage = -1500, target = false },
+}
+
+monster.defenses = {
+ defense = 115,
+ armor = 115,
+ mitigation = 3.19,
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 0 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 25 },
+ { type = COMBAT_EARTHDAMAGE, percent = -20 },
+ { type = COMBAT_FIREDAMAGE, percent = -10 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 40 },
+ { type = COMBAT_HOLYDAMAGE, percent = -15 },
+ { type = COMBAT_DEATHDAMAGE, percent = 50 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = false },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = false },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/mushroom.lua b/data-otservbr-global/monster/quests/rotten_blood/mushroom.lua
new file mode 100644
index 00000000000..d5611b8ea9c
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/mushroom.lua
@@ -0,0 +1,94 @@
+local mType = Game.createMonsterType("Mushroom")
+local monster = {}
+
+monster.description = "a Mushroom"
+monster.experience = 0
+monster.outfit = {
+ lookType = 1669, --todo get correct lookType
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.health = 10000
+monster.maxHealth = 10000
+monster.race = "undead"
+monster.corpse = 0
+monster.speed = 0
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 2500,
+ chance = 40,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 98,
+ targetDistance = 1,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.voices = {}
+
+monster.loot = {}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -400 },
+ { name = "combat", interval = 3000, chance = 100, type = COMBAT_LIFEDRAIN, minDamage = -2500, maxDamage = -3000, radius = 3, effect = CONST_ME_POISONAREA, target = false }, -- life drain bomb
+}
+
+monster.defenses = {
+ defense = 65,
+ armor = 0,
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 0 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 0 },
+ { type = COMBAT_EARTHDAMAGE, percent = 0 },
+ { type = COMBAT_FIREDAMAGE, percent = 0 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 0 },
+ { type = COMBAT_HOLYDAMAGE, percent = 0 },
+ { type = COMBAT_DEATHDAMAGE, percent = 0 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = true },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = true },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/mycobiontic_beetle.lua b/data-otservbr-global/monster/quests/rotten_blood/mycobiontic_beetle.lua
new file mode 100644
index 00000000000..6a8267c9bc8
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/mycobiontic_beetle.lua
@@ -0,0 +1,129 @@
+local mType = Game.createMonsterType("Mycobiontic Beetle")
+local monster = {}
+
+monster.description = "a mycobiontic beetle"
+monster.experience = 21175
+monster.outfit = {
+ lookType = 1620,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.raceId = 2375
+monster.Bestiary = {
+ class = "Vermin",
+ race = BESTY_RACE_VERMIN,
+ toKill = 5000,
+ FirstUnlock = 200,
+ SecondUnlock = 2000,
+ CharmsPoints = 100,
+ Stars = 5,
+ Occurrence = 0,
+ Locations = "Jaded roots",
+}
+
+monster.health = 30200
+monster.maxHealth = 30200
+monster.race = "undead"
+monster.corpse = 43555
+monster.speed = 230
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 5000,
+ chance = 10,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 90,
+ targetDistance = 0,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.voices = {
+ interval = 5000,
+ chance = 10,
+}
+
+monster.loot = {
+ { name = "crystal coin", chance = 15540 },
+ { name = "ultimate health potion", chance = 43253, maxCount = 5 },
+ { name = "serpent sword", chance = 32253 },
+ { name = "glacier mask", chance = 21920 },
+ { name = "small sapphire", chance = 34560, maxCount = 3 },
+ { name = "organic acid", chance = 11678, maxCount = 1 },
+ { name = "rotten roots", chance = 25920, maxCount = 1 },
+ { name = "scarab coin", chance = 22920, maxCount = 3 },
+ { name = "buckle", chance = 22920, maxCount = 1 },
+ { name = "rotten vermin ichor", chance = 22920, maxCount = 1 },
+ { name = "violet gem", chance = 18920 },
+ { name = "blue gem", chance = 15920 },
+ { name = "small ruby", chance = 24560, maxCount = 3 },
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -1600 },
+ { name = "combat", interval = 2500, chance = 20, type = COMBAT_EARTHDAMAGE, minDamage = -1100, maxDamage = -1400, radius = 5, effect = CONST_ME_GREEN_RINGS, target = true },
+ { name = "combat", intervall = 2000, chance = 15, type = COMBAT_EARTHDAMAGE, minDamage = -1200, maxDamage = -1600, length = 8, spread = 3, effect = CONST_ME_GREEN_RINGS, target = false },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_PHYSICALDAMAGE, minDamage = -1400, maxDamage = -1700, radius = 5, effect = CONST_ME_EXPLOSIONAREA, target = false },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_EARTHDAMAGE, minDamage = -1200, maxDamage = -1400, radius = 5, effect = CONST_ME_GROUNDSHAKER, target = false },
+ { name = "largepoisonring", interval = 2000, chance = 10, minDamage = -900, maxDamage = -1300 },
+}
+
+monster.defenses = {
+ defense = 116,
+ armor = 116,
+ mitigation = 2.92,
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 25 },
+ { type = COMBAT_ENERGYDAMAGE, percent = -15 },
+ { type = COMBAT_EARTHDAMAGE, percent = 60 },
+ { type = COMBAT_FIREDAMAGE, percent = 35 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = -25 },
+ { type = COMBAT_HOLYDAMAGE, percent = -5 },
+ { type = COMBAT_DEATHDAMAGE, percent = 0 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = false },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/oozing_carcass.lua b/data-otservbr-global/monster/quests/rotten_blood/oozing_carcass.lua
new file mode 100644
index 00000000000..bc44ba198fb
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/oozing_carcass.lua
@@ -0,0 +1,123 @@
+local mType = Game.createMonsterType("Oozing Carcass")
+local monster = {}
+
+monster.description = "an oozing carcass"
+monster.experience = 20980
+monster.outfit = {
+ lookType = 1626,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.raceId = 2377
+monster.Bestiary = {
+ class = "Undead",
+ race = BESTY_RACE_UNDEAD,
+ toKill = 5000,
+ FirstUnlock = 200,
+ SecondUnlock = 2000,
+ CharmsPoints = 100,
+ Stars = 5,
+ Occurrence = 0,
+ Locations = "Putrefactory.",
+}
+
+monster.health = 27500
+monster.maxHealth = 27500
+monster.race = "undead"
+monster.corpse = 43579
+monster.speed = 215
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 4000,
+ chance = 0,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = false,
+ staticAttackChance = 90,
+ targetDistance = 1,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 4,
+ color = 143,
+}
+
+monster.loot = {
+ { name = "crystal coin", chance = 9000, maxCount = 1 },
+ { name = "lichen gobbler", chance = 12369, maxCount = 1 },
+ { name = "small emerald", chance = 12859, maxCount = 1 },
+ { id = 3039, chance = 9808, maxCount = 1 }, -- red gem
+ { name = "skull staff", chance = 12316, maxCount = 1 },
+ { name = "bone shield", chance = 6752, maxCount = 1 },
+ { name = "yellow gem", chance = 8634, maxCount = 1 },
+ { name = "rotten roots", chance = 13133, maxCount = 1 },
+ { name = "decayed finger bone", chance = 6964, maxCount = 1 },
+ { name = "ultimate health potion", chance = 10285, maxCount = 2 },
+ { name = "bloody edge", chance = 12270, maxCount = 1 },
+ { name = "spellbook of warding", chance = 5084, maxCount = 1 },
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -600 },
+ { name = "combat", interval = 2000, chance = 15, type = COMBAT_DEATHDAMAGE, minDamage = -1500, maxDamage = -1600, radius = 5, effect = CONST_ME_BLACKSMOKE, target = false },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_ICEDAMAGE, minDamage = -1400, maxDamage = -1500, radius = 5, effect = CONST_ME_ICEAREA, target = false },
+ { name = "combat", interval = 2000, chance = 25, type = COMBAT_ICEDAMAGE, minDamage = -1400, maxDamage = -1550, length = 8, spread = 5, effect = CONST_ME_ICEAREA, target = false },
+ { name = "largedeathring", interval = 2000, chance = 20, minDamage = -850, maxDamage = -1400, target = false },
+ { name = "energy chain", interval = 3000, chance = 20, minDamage = -1050, maxDamage = -1400, target = false },
+}
+
+monster.defenses = {
+ defense = 102,
+ armor = 102,
+ mitigation = 3.10,
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 0 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 25 },
+ { type = COMBAT_EARTHDAMAGE, percent = -20 },
+ { type = COMBAT_FIREDAMAGE, percent = -10 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 35 },
+ { type = COMBAT_HOLYDAMAGE, percent = -25 },
+ { type = COMBAT_DEATHDAMAGE, percent = 40 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = true },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/oozing_corpus.lua b/data-otservbr-global/monster/quests/rotten_blood/oozing_corpus.lua
new file mode 100644
index 00000000000..7408ed46ef0
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/oozing_corpus.lua
@@ -0,0 +1,120 @@
+local mType = Game.createMonsterType("Oozing Corpus")
+local monster = {}
+
+monster.description = "an oozing corpus"
+monster.experience = 20600
+monster.outfit = {
+ lookType = 1625,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.raceId = 2381
+monster.Bestiary = {
+ class = "Undead",
+ race = BESTY_RACE_UNDEAD,
+ toKill = 5000,
+ FirstUnlock = 200,
+ SecondUnlock = 2000,
+ CharmsPoints = 100,
+ Stars = 5,
+ Occurrence = 0,
+ Locations = "Jaded Roots.",
+}
+
+monster.health = 28700
+monster.maxHealth = 28700
+monster.race = "undead"
+monster.corpse = 43575
+monster.speed = 220
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 4000,
+ chance = 0,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = false,
+ staticAttackChance = 90,
+ targetDistance = 0,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 4,
+ color = 143,
+}
+
+monster.loot = {
+ { name = "crystal coin", chance = 9000, maxCount = 1 },
+ { name = "organic acid", chance = 7678, maxCount = 1 },
+ { name = "terra boots", chance = 12369, maxCount = 1 },
+ { name = "small amethyst", chance = 12859, maxCount = 1 },
+ { name = "rotten roots", chance = 13133, maxCount = 1 },
+ { name = "blue gem", chance = 9808, maxCount = 1 },
+ { name = "dragonbone staff", chance = 6964, maxCount = 1 },
+ { name = "worm sponge", chance = 7270, maxCount = 1 },
+ { name = "violet gem", chance = 5084, maxCount = 1 },
+ { name = "jade hammer", chance = 3073, maxCount = 1 },
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -1600 },
+ { name = "combat", interval = 2500, chance = 20, type = COMBAT_PHYSICALDAMAGE, minDamage = -1300, maxDamage = -1700, radius = 5, effect = CONST_ME_GHOSTLY_BITE, target = true },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_PHYSICALDAMAGE, minDamage = -1400, maxDamage = -1550, length = 8, spread = 3, effect = CONST_ME_GROUNDSHAKER, target = false },
+ { name = "combat", interval = 2000, chance = 15, type = COMBAT_EARTHDAMAGE, minDamage = -1100, maxDamage = -1550, length = 8, spread = 3, effect = CONST_ME_GREEN_RINGS, target = false },
+ { name = "death chain", interval = 3000, chance = 15, minDamage = -900, maxDamage = -1300, target = true },
+}
+
+monster.defenses = {
+ defense = 100,
+ armor = 107,
+ mitigation = 3.25,
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 30 },
+ { type = COMBAT_ENERGYDAMAGE, percent = -25 },
+ { type = COMBAT_EARTHDAMAGE, percent = 40 },
+ { type = COMBAT_FIREDAMAGE, percent = 25 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = -10 },
+ { type = COMBAT_HOLYDAMAGE, percent = -10 },
+ { type = COMBAT_DEATHDAMAGE, percent = 0 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = true },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/rotten_man-maggot.lua b/data-otservbr-global/monster/quests/rotten_blood/rotten_man-maggot.lua
new file mode 100644
index 00000000000..19eb6d03b27
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/rotten_man-maggot.lua
@@ -0,0 +1,124 @@
+local mType = Game.createMonsterType("Rotten Man-Maggot")
+local monster = {}
+
+monster.description = "a rotten man-maggot"
+monster.experience = 22625
+monster.outfit = {
+ lookType = 1655,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.raceId = 2393
+monster.Bestiary = {
+ class = "Vermin",
+ race = BESTY_RACE_VERMIN,
+ toKill = 5000,
+ FirstUnlock = 200,
+ SecondUnlock = 2000,
+ CharmsPoints = 100,
+ Stars = 5,
+ Occurrence = 0,
+ Locations = "Putrefactory.",
+}
+
+monster.health = 31100
+monster.maxHealth = 31100
+monster.race = "undead"
+monster.corpse = 43820
+monster.speed = 195
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 5000,
+ chance = 10,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = true,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 90,
+ targetDistance = 0,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.voices = {
+ interval = 5000,
+ chance = 10,
+}
+
+monster.loot = {
+ { name = "crystal coin", chance = 10340, maxCount = 1 },
+ { name = "small amethyst", chance = 7364, maxCount = 2 },
+ { name = "lichen gobbler", chance = 8391, maxCount = 1 },
+ { name = "rotten roots", chance = 11619, maxCount = 1 },
+ { id = 6299, chance = 12591, maxCount = 1 }, -- death ring
+ { name = "wood cape", chance = 14371, maxCount = 1 },
+ { id = 3039, chance = 5155, maxCount = 1 }, -- red gem
+ { name = "yellow gem", chance = 9564, maxCount = 1 },
+ { name = "blooded worm", chance = 5096, maxCount = 1 },
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -900 },
+ { name = "combat", interval = 2000, chance = 15, type = COMBAT_DEATHDAMAGE, minDamage = -1100, maxDamage = -1400, radius = 5, effect = CONST_ME_MORTAREA, target = true },
+ { name = "combat", interval = 2000, chance = 15, type = COMBAT_ICEDAMAGE, minDamage = -1300, maxDamage = -1800, radius = 5, effect = CONST_ME_GHOSTLY_BITE, target = true },
+ { name = "combat", interval = 2000, chance = 10, type = COMBAT_ICEDAMAGE, minDamage = -1200, maxDamage = -1700, length = 8, spread = 5, effect = CONST_ME_ICEAREA, target = false },
+ { name = "largeicering", interval = 2000, chance = 15, minDamage = -800, maxDamage = -1200, target = false },
+}
+
+monster.defenses = {
+ defense = 110,
+ armor = 110,
+ mitigation = 2.75,
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 0 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 55 },
+ { type = COMBAT_EARTHDAMAGE, percent = -15 },
+ { type = COMBAT_FIREDAMAGE, percent = -10 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 40 },
+ { type = COMBAT_HOLYDAMAGE, percent = -15 },
+ { type = COMBAT_DEATHDAMAGE, percent = 30 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = false },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/sopping_carcass.lua b/data-otservbr-global/monster/quests/rotten_blood/sopping_carcass.lua
new file mode 100644
index 00000000000..26458dfe282
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/sopping_carcass.lua
@@ -0,0 +1,110 @@
+local mType = Game.createMonsterType("Sopping Carcass")
+local monster = {}
+
+monster.description = "a sopping carcass"
+monster.experience = 23425
+monster.outfit = {
+ lookType = 1658,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.raceId = 2396
+monster.Bestiary = {
+ class = "Undead",
+ race = BESTY_RACE_UNDEAD,
+ toKill = 5000,
+ FirstUnlock = 200,
+ SecondUnlock = 2000,
+ CharmsPoints = 100,
+ Stars = 5,
+ Occurrence = 0,
+ Locations = "Putrefactory.",
+}
+
+monster.health = 32700
+monster.maxHealth = 32700
+monster.race = "undead"
+monster.corpse = 43832
+monster.speed = 210
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 4000,
+ chance = 0,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = false,
+ staticAttackChance = 90,
+ targetDistance = 0,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.loot = {}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -1100 },
+ { name = "combat", interval = 2000, chance = 24, type = COMBAT_DEATHDAMAGE, minDamage = -1400, maxDamage = -1500, radius = 5, effect = CONST_ME_MORTAREA, target = false },
+ { name = "combat", interval = 2500, chance = 15, type = COMBAT_ICEDAMAGE, minDamage = -1200, maxDamage = -1400, radius = 5, effect = CONST_ME_GHOSTLY_BITE, target = false },
+ { name = "combat", interval = 2000, chance = 25, type = COMBAT_EARTHDAMAGE, minDamage = -900, maxDamage = -1400, radius = 5, effect = CONST_ME_BIGPLANTS, target = false },
+ { name = "combat", interval = 2000, chance = 25, type = COMBAT_DEATHDAMAGE, minDamage = -1100, maxDamage = -1550, length = 8, spread = 5, effect = CONST_ME_BLACKSMOKE, target = false },
+ { name = "ice chain", interval = 3000, chance = 15, minDamage = -1200, maxDamage = -1500, target = false },
+}
+
+monster.defenses = {
+ defense = 109,
+ armor = 109,
+ mitigation = 3.28,
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 0 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 35 },
+ { type = COMBAT_EARTHDAMAGE, percent = -15 },
+ { type = COMBAT_FIREDAMAGE, percent = -5 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 50 },
+ { type = COMBAT_HOLYDAMAGE, percent = -20 },
+ { type = COMBAT_DEATHDAMAGE, percent = 60 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = true },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/sopping_corpus.lua b/data-otservbr-global/monster/quests/rotten_blood/sopping_corpus.lua
new file mode 100644
index 00000000000..24d7a5f87b1
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/sopping_corpus.lua
@@ -0,0 +1,130 @@
+local mType = Game.createMonsterType("Sopping Corpus")
+local monster = {}
+
+monster.description = "a sopping corpus"
+monster.experience = 22465
+monster.outfit = {
+ lookType = 1659,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.raceId = 2397
+monster.Bestiary = {
+ class = "Undead",
+ race = BESTY_RACE_UNDEAD,
+ toKill = 5000,
+ FirstUnlock = 200,
+ SecondUnlock = 2000,
+ CharmsPoints = 100,
+ Stars = 5,
+ Occurrence = 0,
+ Locations = "Jaded Roots.",
+}
+
+monster.health = 33400
+monster.maxHealth = 33400
+monster.race = "undead"
+monster.corpse = 43836
+monster.speed = 210
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 4000,
+ chance = 0,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = false,
+ canPushItems = true,
+ canPushCreatures = false,
+ staticAttackChance = 90,
+ targetDistance = 0,
+ runHealth = 0,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = true,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.voices = {
+ interval = 5000,
+ chance = 10,
+ { text = "*Lessshhh!*", yell = false },
+}
+
+monster.loot = {
+ { name = "crystal coin", chance = 6961, maxCount = 1 },
+ { name = "ultimate mana potion", chance = 10285, maxCount = 2 },
+ { name = "crimson sword", chance = 10090, maxCount = 3 },
+ { name = "ultimate health potion", chance = 10285, maxCount = 2 },
+ { name = "organic acid", chance = 7678, maxCount = 1 },
+ { name = "rotten roots", chance = 13133, maxCount = 1 },
+ { name = "emerald bangle", chance = 8558, maxCount = 1 },
+ { name = "underworld rod", chance = 8380, maxCount = 1 },
+ { name = "violet gem", chance = 5084, maxCount = 1 },
+ { name = "blue gem", chance = 9808, maxCount = 1 },
+ { name = "relic sword", chance = 6964, maxCount = 1 },
+ { name = "skullcracker armor", chance = 7270, maxCount = 1 },
+ { name = "ring of green plasma", chance = 3073, maxCount = 1 },
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -1600 },
+ { name = "combat", interval = 2000, chance = 15, type = COMBAT_PHYSICALDAMAGE, minDamage = -1300, maxDamage = -1600, length = 8, spread = 3, effect = CONST_ME_GROUNDSHAKER, target = false },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_PHYSICALDAMAGE, minDamage = -1200, maxDamage = -1500, effect = CONST_ME_BIG_SCRATCH, target = true },
+ { name = "combat", interval = 2000, chance = 15, type = COMBAT_PHYSICALDAMAGE, minDamage = -1400, maxDamage = -1600, radius = 5, effect = CONST_ME_GROUNDSHAKER, target = true },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_EARTHDAMAGE, minDamage = -1200, maxDamage = -1500, length = 8, spread = 3, effect = CONST_ME_GREEN_RINGS, target = false },
+ { name = "largepoisonring", interval = 2000, chance = 10, minDamage = -1000, maxDamage = -1200, target = false },
+}
+
+monster.defenses = {
+ defense = 112,
+ armor = 112,
+ mitigation = 3.25,
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = 40 },
+ { type = COMBAT_ENERGYDAMAGE, percent = -20 },
+ { type = COMBAT_EARTHDAMAGE, percent = 50 },
+ { type = COMBAT_FIREDAMAGE, percent = 30 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = -10 },
+ { type = COMBAT_HOLYDAMAGE, percent = 5 },
+ { type = COMBAT_DEATHDAMAGE, percent = 10 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = true },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/walking_pillar.lua b/data-otservbr-global/monster/quests/rotten_blood/walking_pillar.lua
new file mode 100644
index 00000000000..89e1e62a109
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/walking_pillar.lua
@@ -0,0 +1,130 @@
+local mType = Game.createMonsterType("Walking Pillar")
+local monster = {}
+
+monster.description = "a walking pillar"
+monster.experience = 24300
+monster.outfit = {
+ lookType = 1656,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.raceId = 2394
+monster.Bestiary = {
+ class = "Construct",
+ race = BESTY_RACE_CONSTRUCT,
+ toKill = 5000,
+ FirstUnlock = 200,
+ SecondUnlock = 2000,
+ CharmsPoints = 100,
+ Stars = 5,
+ Occurrence = 0,
+ Locations = "Darklight Core",
+}
+
+monster.health = 38000
+monster.maxHealth = 38000
+monster.race = "undead"
+monster.corpse = 43824
+monster.speed = 190
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 4000,
+ chance = 10,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = true,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 90,
+ targetDistance = 1,
+ runHealth = 50,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = false,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.voices = {
+ interval = 5000,
+ chance = 10,
+ { text = "TREEMBLE", yell = false },
+}
+
+monster.loot = {
+ { name = "crystal coin", chance = 12186, maxCount = 1 },
+ { name = "yellow darklight matter", chance = 5354, maxCount = 1 },
+ { name = "magma clump", chance = 11440, maxCount = 1 },
+ { name = "darklight core", chance = 10276, maxCount = 1 },
+ { id = 12600, chance = 8489, maxCount = 4 }, -- coal
+ { name = "darklight basalt chunk", chance = 12855, maxCount = 1 },
+ { name = "onyx chip", chance = 12831, maxCount = 2 },
+ { name = "strange helmet", chance = 11001, maxCount = 1 },
+ { name = "fire sword", chance = 8347, maxCount = 1 },
+ { name = "ultimate mana potion", chance = 9687, maxCount = 3 },
+ { name = "blue gem", chance = 5868, maxCount = 1 },
+ { name = "magma legs", chance = 14497, maxCount = 1 },
+ { name = "white gem", chance = 9936, maxCount = 1 },
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -1500 },
+ { name = "combat", interval = 2000, chance = 10, type = COMBAT_ENERGYDAMAGE, minDamage = -1400, maxDamage = -1650, length = 8, spread = 3, effect = CONST_ME_BLUE_ENERGY_SPARK, target = false },
+ { name = "combat", intervall = 2000, chance = 20, type = COMBAT_HOLYDAMAGE, minDamage = -1500, maxDamage = -1800, radius = 5, effect = CONST_ME_PURPLESMOKE, target = true },
+ { name = "combat", interval = 2000, chance = 10, type = COMBAT_HOLYDAMAGE, minDamage = -1200, maxDamage = -1200, radius = 5, effect = CONST_ME_GHOSTLY_BITE, target = true },
+ { name = "extended energy chain", interval = 2000, chance = 5, minDamage = -800, maxDamage = 1200, target = true },
+ { name = "largepinkring", interval = 3500, chance = 10, minDamage = -1100, maxDamage = -1600, target = false },
+}
+
+monster.defenses = {
+ defense = 120,
+ armor = 120,
+ mitigation = 2.75,
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = -10 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 60 },
+ { type = COMBAT_EARTHDAMAGE, percent = -15 },
+ { type = COMBAT_FIREDAMAGE, percent = -15 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 45 },
+ { type = COMBAT_HOLYDAMAGE, percent = 0 },
+ { type = COMBAT_DEATHDAMAGE, percent = 10 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/monster/quests/rotten_blood/wandering_pillar.lua b/data-otservbr-global/monster/quests/rotten_blood/wandering_pillar.lua
new file mode 100644
index 00000000000..e6cfb42a355
--- /dev/null
+++ b/data-otservbr-global/monster/quests/rotten_blood/wandering_pillar.lua
@@ -0,0 +1,133 @@
+local mType = Game.createMonsterType("Wandering Pillar")
+local monster = {}
+
+monster.description = "a wandering pillar"
+monster.experience = 23200
+monster.outfit = {
+ lookType = 1657,
+ lookHead = 0,
+ lookBody = 0,
+ lookLegs = 0,
+ lookFeet = 0,
+ lookAddons = 0,
+ lookMount = 0,
+}
+
+monster.raceId = 2395
+monster.Bestiary = {
+ class = "Construct",
+ race = BESTY_RACE_CONSTRUCT,
+ toKill = 5000,
+ FirstUnlock = 200,
+ SecondUnlock = 2000,
+ CharmsPoints = 100,
+ Stars = 5,
+ Occurrence = 0,
+ Locations = "Darklight Core",
+}
+
+monster.health = 37000
+monster.maxHealth = 37000
+monster.race = "undead"
+monster.corpse = 43828
+monster.speed = 190
+monster.manaCost = 0
+
+monster.changeTarget = {
+ interval = 4000,
+ chance = 10,
+}
+
+monster.strategiesTarget = {
+ nearest = 70,
+ health = 10,
+ damage = 10,
+ random = 10,
+}
+
+monster.flags = {
+ summonable = false,
+ attackable = true,
+ hostile = true,
+ convinceable = false,
+ pushable = false,
+ rewardBoss = false,
+ illusionable = true,
+ canPushItems = true,
+ canPushCreatures = true,
+ staticAttackChance = 90,
+ targetDistance = 1,
+ runHealth = 50,
+ healthHidden = false,
+ isBlockable = false,
+ canWalkOnEnergy = true,
+ canWalkOnFire = false,
+ canWalkOnPoison = true,
+}
+
+monster.light = {
+ level = 0,
+ color = 0,
+}
+
+monster.voices = {
+ interval = 5000,
+ chance = 10,
+ { text = "POWERRR!!", yell = false },
+ { text = "DARKNESS. DEATH. ENERGIES.", yell = false },
+ { text = "TREMMMBLE!", yell = false },
+}
+
+monster.loot = {
+ { name = "crystal coin", chance = 6629, maxCount = 1 },
+ { name = "darklight obsidian axe", chance = 14652, maxCount = 1 },
+ { name = "basalt crumbs", chance = 8184, maxCount = 1 },
+ { name = "sulphurous stone", chance = 5873, maxCount = 1 },
+ { name = "magma boots", chance = 5080, maxCount = 1 },
+ { id = 12600, chance = 9802, maxCount = 4 }, -- coal
+ { name = "dark helmet", chance = 7490, maxCount = 1 },
+ { name = "magma coat", chance = 11753, maxCount = 1 },
+ { name = "onyx chip", chance = 9311, maxCount = 2 },
+ { name = "darklight core", chance = 5957, maxCount = 1 },
+ { name = "fire sword", chance = 8319, maxCount = 1 },
+ { name = "magma clump", chance = 6260, maxCount = 1 },
+ { id = 3039, chance = 9915, maxCount = 1 }, -- red gem
+ { name = "green gem", chance = 12864, maxCount = 1 },
+ { name = "basalt core", chance = 9037, maxCount = 1 },
+}
+
+monster.attacks = {
+ { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -1200 },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_ENERGYDAMAGE, minDamage = -1400, maxDamage = -1650, length = 8, spread = 3, effect = CONST_ME_BLUE_ENERGY_SPARK, target = false },
+ { name = "combat", intervall = 2000, chance = 20, type = COMBAT_ENERGYDAMAGE, minDamage = -1100, maxDamage = -1500, radius = 5, effect = CONST_ME_PINK_BEAM, target = false },
+ { name = "combat", interval = 2000, chance = 20, type = COMBAT_FIREDAMAGE, minDamage = -1400, maxDamage = -1700, radius = 5, effect = CONST_ME_HITBYFIRE, target = true },
+ { name = "largeholyring", interval = 3000, chance = 15, minDamage = -900, maxDamage = -1250 },
+}
+
+monster.defenses = {
+ defense = 120,
+ armor = 120,
+ mitigation = 2.75,
+}
+
+monster.elements = {
+ { type = COMBAT_PHYSICALDAMAGE, percent = -10 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 60 },
+ { type = COMBAT_EARTHDAMAGE, percent = -15 },
+ { type = COMBAT_FIREDAMAGE, percent = -15 },
+ { type = COMBAT_LIFEDRAIN, percent = 0 },
+ { type = COMBAT_MANADRAIN, percent = 0 },
+ { type = COMBAT_DROWNDAMAGE, percent = 0 },
+ { type = COMBAT_ICEDAMAGE, percent = 45 },
+ { type = COMBAT_HOLYDAMAGE, percent = 0 },
+ { type = COMBAT_DEATHDAMAGE, percent = 10 },
+}
+
+monster.immunities = {
+ { type = "paralyze", condition = true },
+ { type = "outfit", condition = false },
+ { type = "invisible", condition = true },
+ { type = "bleed", condition = false },
+}
+
+mType:register(monster)
diff --git a/data-otservbr-global/npc/bloodshade_rotten.lua b/data-otservbr-global/npc/bloodshade_rotten.lua
new file mode 100644
index 00000000000..5d46776a40d
--- /dev/null
+++ b/data-otservbr-global/npc/bloodshade_rotten.lua
@@ -0,0 +1,79 @@
+local npcType = Game.createNpcType("Bloodshade Rotten")
+local npcConfig = {}
+
+local npcName = "A Bloodshade"
+npcConfig.name = npcName
+npcConfig.description = npcName
+
+npcConfig.health = 100
+npcConfig.maxHealth = npcConfig.health
+npcConfig.walkInterval = 0
+npcConfig.walkRadius = 0
+
+npcConfig.outfit = {
+ lookType = 1414,
+}
+
+npcConfig.flags = {
+ floorchange = false,
+}
+
+local keywordHandler = KeywordHandler:new()
+local npcHandler = NpcHandler:new(keywordHandler)
+
+npcType.onThink = function(npc, interval)
+ npcHandler:onThink(npc, interval)
+end
+
+npcType.onAppear = function(npc, creature)
+ npcHandler:onAppear(npc, creature)
+end
+
+npcType.onDisappear = function(npc, creature)
+ npcHandler:onDisappear(npc, creature)
+end
+
+npcType.onMove = function(npc, creature, fromPosition, toPosition)
+ npcHandler:onMove(npc, creature, fromPosition, toPosition)
+end
+
+npcType.onSay = function(npc, creature, type, message)
+ npcHandler:onSay(npc, creature, type, message)
+end
+
+local function creatureSayCallback(npc, creature, type, message)
+ local player = Player(creature)
+ local playerId = player:getId()
+
+ if not npcHandler:checkInteraction(npc, creature) then
+ return false
+ end
+
+ local access = player:kv():scoped("rotten-blood-quest"):get("access") or 0
+ if access == 5 then
+ npcHandler:say("You already have started this quest.", npc, creature)
+ npcHandler:setTopic(playerId, 0)
+ return true
+ end
+
+ message = message:lower()
+ if MsgContains(message, "quest") then
+ npcHandler:say({
+ "First you must fight the two pairs of evil twins that lurk in the realm beyond here. ...",
+ "Only when you are victorious over all four of them, your path to the source of vileness, the path to Bakragore will be opened. ...",
+ "And even this victory will only be the beginning.",
+ }, npc, creature)
+ player:kv():scoped("rotten-blood-quest"):set("access", 5)
+ end
+
+ return true
+end
+
+npcHandler:setMessage(MESSAGE_GREET, "Mortal! If you are on a {quest} to serve the blood god, my master - be greeted!")
+npcHandler:setMessage(MESSAGE_WALKAWAY, "Bye.")
+npcHandler:setMessage(MESSAGE_FAREWELL, "Bye, |PLAYERNAME|.")
+
+npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
+npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
+
+npcType:register(npcConfig)
diff --git a/data-otservbr-global/npc/bloodshade_sacrifice.lua b/data-otservbr-global/npc/bloodshade_sacrifice.lua
new file mode 100644
index 00000000000..37e1cf0fe7b
--- /dev/null
+++ b/data-otservbr-global/npc/bloodshade_sacrifice.lua
@@ -0,0 +1,100 @@
+local npcType = Game.createNpcType("Bloodshade Sacrifice")
+local npcConfig = {}
+
+local npcName = "A Bloodshade"
+npcConfig.name = npcName
+npcConfig.description = npcName
+
+npcConfig.health = 100
+npcConfig.maxHealth = npcConfig.health
+npcConfig.walkInterval = 0
+npcConfig.walkRadius = 0
+
+npcConfig.outfit = {
+ lookType = 1414,
+}
+
+npcConfig.flags = {
+ floorchange = false,
+}
+
+local keywordHandler = KeywordHandler:new()
+local npcHandler = NpcHandler:new(keywordHandler)
+
+npcType.onThink = function(npc, interval)
+ npcHandler:onThink(npc, interval)
+end
+
+npcType.onAppear = function(npc, creature)
+ npcHandler:onAppear(npc, creature)
+end
+
+npcType.onDisappear = function(npc, creature)
+ npcHandler:onDisappear(npc, creature)
+end
+
+npcType.onMove = function(npc, creature, fromPosition, toPosition)
+ npcHandler:onMove(npc, creature, fromPosition, toPosition)
+end
+
+npcType.onSay = function(npc, creature, type, message)
+ npcHandler:onSay(npc, creature, type, message)
+end
+
+local function creatureSayCallback(npc, creature, type, message)
+ local player = Player(creature)
+ local playerId = player:getId()
+
+ if not npcHandler:checkInteraction(npc, creature) then
+ return false
+ end
+
+ local access = player:kv():scoped("rotten-blood-quest"):get("access") or 0
+ if access > 2 then
+ npcHandler:say("You already have access.", npc, creature)
+ npcHandler:setTopic(playerId, 0)
+ return true
+ end
+
+ message = message:lower()
+ if MsgContains(message, "quest") then
+ npcHandler:say({
+ "To enter the realm of the sanguine master and destroy its spawn, a sufficient sacrifice is imperative. ...",
+ "Find and slay the keeper of blooded tears and bring the nectar of his eyes before the blood god. Present your gift on the sacrificial altar. ...",
+ "After - and under no circumstances before - you have completed this procedure, you can enter the sacred fluid. You can, of course also take a slightly faster... {detour}.",
+ }, npc, creature)
+ npcHandler:setTopic(playerId, 1)
+ elseif MsgContains(message, "detour") and npcHandler:getTopic(playerId) == 1 then
+ npcHandler:say({
+ "Hm. I see. Well, I will be frank. Every blood sacrifice has its price. Blood money will please the blood god... just as well. ...",
+ "The sum would be five million gold pieces and I... my master will be pleased. Are you prepared for a sacrifice such as this?",
+ }, npc, creature)
+ npcHandler:setTopic(playerId, 2)
+ elseif MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 2 then
+ npcHandler:say("You are willing to pay 5000000 gold pieces, then? There is no turning back after our... transaction is complete. Are you sure?", npc, creature)
+ npcHandler:setTopic(playerId, 3)
+ elseif MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 3 then
+ if not player:removeMoneyBank(5000000) then
+ npcHandler:say("Sorry, you don't have enough gold.", npc, creature)
+ npcHandler:setTopic(playerId, 0)
+ return true
+ end
+ npcHandler:say("The bargain has been made, the business is done.", npc, creature)
+ npcHandler:setTopic(playerId, 0)
+ player:kv():scoped("rotten-blood-quest"):set("access", 4)
+ elseif MsgContains(message, "no") and npcHandler:getTopic(playerId) == 1 then
+ npcHandler:setTopic(playerId, 0)
+ npcHandler:say("Ok then not.", npc, creature)
+ end
+
+ return true
+end
+
+npcHandler:setMessage(MESSAGE_GREET, "Mortal! If you are on a {quest} to serve the blood god, my master - be greeted!")
+npcHandler:setMessage(MESSAGE_WALKAWAY, "Bye.")
+npcHandler:setMessage(MESSAGE_FAREWELL, "Bye, |PLAYERNAME|.")
+
+npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
+npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
+
+npcType:register(npcConfig)
diff --git a/data-otservbr-global/npc/torkada.lua b/data-otservbr-global/npc/torkada.lua
new file mode 100644
index 00000000000..ddbe4d0326e
--- /dev/null
+++ b/data-otservbr-global/npc/torkada.lua
@@ -0,0 +1,103 @@
+local internalNpcName = "Torkada"
+local npcType = Game.createNpcType(internalNpcName)
+local npcConfig = {}
+
+npcConfig.name = internalNpcName
+npcConfig.description = internalNpcName
+
+npcConfig.health = 100
+npcConfig.maxHealth = npcConfig.health
+npcConfig.walkInterval = 0
+npcConfig.walkRadius = 0
+
+npcConfig.outfit = {
+ lookType = 1243,
+ lookHead = 59,
+ lookBody = 78,
+ lookLegs = 78,
+ lookFeet = 57,
+ lookAddons = 0,
+}
+
+npcConfig.flags = {
+ floorchange = false,
+}
+
+local keywordHandler = KeywordHandler:new()
+local npcHandler = NpcHandler:new(keywordHandler)
+
+npcType.onThink = function(npc, interval)
+ npcHandler:onThink(npc, interval)
+end
+
+npcType.onAppear = function(npc, creature)
+ npcHandler:onAppear(npc, creature)
+end
+
+npcType.onDisappear = function(npc, creature)
+ npcHandler:onDisappear(npc, creature)
+end
+
+npcType.onMove = function(npc, creature, fromPosition, toPosition)
+ npcHandler:onMove(npc, creature, fromPosition, toPosition)
+end
+
+npcType.onSay = function(npc, creature, type, message)
+ npcHandler:onSay(npc, creature, type, message)
+end
+
+local function creatureSayCallback(npc, creature, type, message)
+ local player = Player(creature)
+ local playerId = player:getId()
+
+ if not npcHandler:checkInteraction(npc, creature) then
+ return false
+ end
+
+ if player:getLevel() < 250 then
+ npcHandler:say("You need at least level 250 to start our mission.", npc, creature)
+ return false
+ end
+
+ local access = player:kv():scoped("rotten-blood-quest"):get("access") or 0
+ if access > 0 then
+ if player:getStorageValue(Storage.Quest.U13_20.RottenBlood.AccessDoor) ~= 1 then
+ player:setStorageValue(Storage.Quest.U13_20.RottenBlood.AccessDoor, 1)
+ end
+ npcHandler:say("You already have accepted this mission.", npc, creature)
+ npcHandler:setTopic(playerId, 0)
+ return true
+ end
+
+ message = message:lower()
+ if MsgContains(message, "time") then
+ npcHandler:say("This expedition is here on an important {mission} for the inquisition", npc, creature)
+ npcHandler:setTopic(playerId, 1)
+ elseif MsgContains(message, "mission") and npcHandler:getTopic(playerId) == 1 then
+ npcHandler:say("Are you willing, to bring the fury of the inquisition to that foul place and eradicate all evil you find? Speak, {yes} or {no}?", npc, creature)
+ npcHandler:setTopic(playerId, 2)
+ elseif MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 2 then
+ npcHandler:setTopic(playerId, 0)
+ npcHandler:say({
+ "So hereby receive the blessings of the gods, provided by me as the voice of the inquisition! ...",
+ "Go now and search the ancient temple in the north-west part of the drefian ruins. Slay the evil that lurks there and cleanse the foul place from its taint!",
+ }, npc, creature)
+ player:kv():scoped("rotten-blood-quest"):set("access", 1)
+ player:setStorageValue(Storage.Quest.U13_20.RottenBlood.AccessDoor, 1)
+ player:getPosition():sendMagicEffect(CONST_ME_HOLYAREA)
+ elseif MsgContains(message, "no") and npcHandler:getTopic(playerId) == 1 then
+ npcHandler:setTopic(playerId, 0)
+ npcHandler:say("Ok then not.", npc, creature)
+ end
+
+ return true
+end
+
+npcHandler:setMessage(MESSAGE_GREET, "Greetings! This isn't the {time} to chitchat though.")
+npcHandler:setMessage(MESSAGE_WALKAWAY, "Bye.")
+npcHandler:setMessage(MESSAGE_FAREWELL, "Bye, |PLAYERNAME|.")
+
+npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
+npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
+
+npcType:register(npcConfig)
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/rotten_bakragore.lua b/data-otservbr-global/scripts/actions/bosses_levers/rotten_bakragore.lua
new file mode 100644
index 00000000000..09cf6f78378
--- /dev/null
+++ b/data-otservbr-global/scripts/actions/bosses_levers/rotten_bakragore.lua
@@ -0,0 +1,24 @@
+local config = {
+ boss = {
+ name = "Bakragore",
+ position = Position(33044, 32394, 15),
+ },
+ requiredLevel = 250,
+ timeToFightAgain = ParseDuration("68h") / ParseDuration("1s"),
+ playerPositions = {
+ { pos = Position(33078, 32398, 15), teleport = Position(33044, 32407, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(33077, 32398, 15), teleport = Position(33044, 32407, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(33076, 32398, 15), teleport = Position(33044, 32407, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(33075, 32398, 15), teleport = Position(33044, 32407, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(33074, 32398, 15), teleport = Position(33044, 32407, 15), effect = CONST_ME_TELEPORT },
+ },
+ specPos = {
+ from = Position(33034, 32389, 15),
+ to = Position(33053, 32410, 15),
+ },
+ exit = Position(33044, 32409, 15),
+}
+
+local lever = BossLever(config)
+lever:position(Position(33079, 32398, 15))
+lever:register()
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/rotten_chagorz.lua b/data-otservbr-global/scripts/actions/bosses_levers/rotten_chagorz.lua
new file mode 100644
index 00000000000..2e21f18f3ac
--- /dev/null
+++ b/data-otservbr-global/scripts/actions/bosses_levers/rotten_chagorz.lua
@@ -0,0 +1,23 @@
+local config = {
+ boss = {
+ name = "Chagorz",
+ position = Position(33044, 32361, 15),
+ },
+ requiredLevel = 250,
+ playerPositions = {
+ { pos = Position(33078, 32367, 15), teleport = Position(33044, 32373, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(33077, 32367, 15), teleport = Position(33044, 32373, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(33076, 32367, 15), teleport = Position(33044, 32373, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(33075, 32367, 15), teleport = Position(33044, 32373, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(33074, 32367, 15), teleport = Position(33044, 32373, 15), effect = CONST_ME_TELEPORT },
+ },
+ specPos = {
+ from = Position(33035, 32327, 15),
+ to = Position(33053, 32345, 15),
+ },
+ exit = Position(33043, 32344, 15),
+}
+
+local lever = BossLever(config)
+lever:position(Position(33079, 32367, 15))
+lever:register()
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/rotten_ichgahal.lua b/data-otservbr-global/scripts/actions/bosses_levers/rotten_ichgahal.lua
new file mode 100644
index 00000000000..32f44d9acc5
--- /dev/null
+++ b/data-otservbr-global/scripts/actions/bosses_levers/rotten_ichgahal.lua
@@ -0,0 +1,23 @@
+local config = {
+ boss = {
+ name = "Ichgahal",
+ position = Position(33008, 32333, 15),
+ },
+ requiredLevel = 250,
+ playerPositions = {
+ { pos = Position(32978, 32333, 15), teleport = Position(33004, 32341, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(32977, 32333, 15), teleport = Position(33004, 32341, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(32976, 32333, 15), teleport = Position(33004, 32341, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(32975, 32333, 15), teleport = Position(33004, 32341, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(32974, 32333, 15), teleport = Position(33004, 32341, 15), effect = CONST_ME_TELEPORT },
+ },
+ specPos = {
+ from = Position(32998, 32326, 15),
+ to = Position(33018, 32343, 15),
+ },
+ exit = Position(33004, 32343, 15),
+}
+
+local lever = BossLever(config)
+lever:position(Position(32979, 32333, 15))
+lever:register()
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/rotten_murcion.lua b/data-otservbr-global/scripts/actions/bosses_levers/rotten_murcion.lua
new file mode 100644
index 00000000000..0c163f8db3d
--- /dev/null
+++ b/data-otservbr-global/scripts/actions/bosses_levers/rotten_murcion.lua
@@ -0,0 +1,23 @@
+local config = {
+ boss = {
+ name = "Murcion",
+ position = Position(33009, 32362, 15),
+ },
+ requiredLevel = 250,
+ playerPositions = {
+ { pos = Position(32978, 32365, 15), teleport = Position(33009, 32371, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(32977, 32365, 15), teleport = Position(33009, 32371, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(32976, 32365, 15), teleport = Position(33009, 32371, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(32975, 32365, 15), teleport = Position(33009, 32371, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(32974, 32365, 15), teleport = Position(33009, 32371, 15), effect = CONST_ME_TELEPORT },
+ },
+ specPos = {
+ from = Position(32999, 32359, 15),
+ to = Position(33019, 32376, 15),
+ },
+ exit = Position(33009, 32374, 15),
+}
+
+local lever = BossLever(config)
+lever:position(Position(32979, 32365, 15))
+lever:register()
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/rotten_vemiath.lua b/data-otservbr-global/scripts/actions/bosses_levers/rotten_vemiath.lua
new file mode 100644
index 00000000000..a018c622eff
--- /dev/null
+++ b/data-otservbr-global/scripts/actions/bosses_levers/rotten_vemiath.lua
@@ -0,0 +1,23 @@
+local config = {
+ boss = {
+ name = "Vemiath",
+ position = Position(33044, 32332, 15),
+ },
+ requiredLevel = 250,
+ playerPositions = {
+ { pos = Position(33078, 32333, 15), teleport = Position(33043, 32341, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(33077, 32333, 15), teleport = Position(33043, 32341, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(33076, 32333, 15), teleport = Position(33043, 32341, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(33075, 32333, 15), teleport = Position(33043, 32341, 15), effect = CONST_ME_TELEPORT },
+ { pos = Position(33074, 32333, 15), teleport = Position(33043, 32341, 15), effect = CONST_ME_TELEPORT },
+ },
+ specPos = {
+ from = Position(33034, 32357, 15),
+ to = Position(33052, 32376, 15),
+ },
+ exit = Position(33043, 32344, 15),
+}
+
+local lever = BossLever(config)
+lever:position(Position(33079, 32333, 15))
+lever:register()
diff --git a/data-otservbr-global/scripts/actions/quests/rotten_blood/blood_entrance.lua b/data-otservbr-global/scripts/actions/quests/rotten_blood/blood_entrance.lua
new file mode 100644
index 00000000000..4830adfa615
--- /dev/null
+++ b/data-otservbr-global/scripts/actions/quests/rotten_blood/blood_entrance.lua
@@ -0,0 +1,61 @@
+local config = {
+ entrance = Position(32953, 32398, 9),
+ noAccess = Position(32955, 32398, 9),
+ destination = Position(34070, 31975, 14),
+}
+
+local function getDamage(currentHealth)
+ local damage = math.max(math.floor(currentHealth * 0.20), 1)
+ local newHealth = currentHealth - damage
+ return newHealth > 10 and damage or 0
+end
+
+local accessBlood = MoveEvent()
+function accessBlood.onStepIn(creature, item, position, fromPosition)
+ local player = creature:getPlayer()
+ if not player then
+ return false
+ end
+
+ if player:getLevel() < 250 then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need at least level 250 to enter.")
+ player:teleportTo(fromPosition, true)
+ return false
+ end
+
+ local access = player:kv():scoped("rotten-blood-quest"):get("access") or 0
+ if access < 4 then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You offerings to this sanguine master of this realm have been insufficient. You can not pass.")
+ player:teleportTo(config.noAccess, true)
+ player:addHealth(-getDamage(player:getHealth()), COMBAT_PHYSICALDAMAGE)
+ return false
+ end
+
+ if config.entrance == position then
+ player:teleportTo(config.destination)
+ player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
+ end
+
+ return true
+end
+
+accessBlood:type("stepin")
+accessBlood:position(config.entrance)
+accessBlood:register()
+
+----------- Leave from Rotten -----------
+local leaveBlood = MoveEvent()
+function leaveBlood.onStepIn(creature, item, position, fromPosition)
+ local player = creature:getPlayer()
+ if not player then
+ return false
+ end
+
+ player:teleportTo(Position(32955, 32398, 9))
+ player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
+ return true
+end
+
+leaveBlood:type("stepin")
+leaveBlood:position(Position(34070, 31974, 14))
+leaveBlood:register()
diff --git a/data-otservbr-global/scripts/actions/quests/rotten_blood/bosses_killed.lua b/data-otservbr-global/scripts/actions/quests/rotten_blood/bosses_killed.lua
new file mode 100644
index 00000000000..2bb1fa3d53c
--- /dev/null
+++ b/data-otservbr-global/scripts/actions/quests/rotten_blood/bosses_killed.lua
@@ -0,0 +1,50 @@
+local bossesRottenBlood = CreatureEvent("RottenBloodBossDeath")
+function bossesRottenBlood.onDeath(creature)
+ local bossName = creature:getName():lower()
+ if not table.contains({ "murcion", "chagorz", "ichgahal", "vemiath" }, bossName) then
+ return false
+ end
+
+ onDeathForDamagingPlayers(creature, function(creature, player)
+ local now = os.time()
+ local kv = player:kv():scoped("rotten-blood-quest")
+ local cooldown = kv:scoped(bossName):get("cooldown") or 0
+ if cooldown <= now then
+ kv:scoped(bossName):set("cooldown", now + 20 * 60 * 60)
+ kv:set("taints", math.min(((kv:get("taints") or 0) + 1), 4))
+ logger.info("taints: {}", kv:get("taints"))
+ end
+ end)
+
+ return true
+end
+
+bossesRottenBlood:register()
+
+-------------- Bakragore OnDeath --------------
+local bakragoreOnDeath = CreatureEvent("RottenBloodBakragoreDeath")
+function bakragoreOnDeath.onDeath(creature)
+ local bossName = creature:getName():lower()
+ if bossName ~= "bakragore" then
+ return false
+ end
+
+ onDeathForDamagingPlayers(creature, function(creature, player)
+ local kv = player:kv():scoped("rotten-blood-quest")
+ local checkBoss = kv:get(bossName) or false
+ if not checkBoss then
+ kv:set(bossName, true)
+ if not player:hasOutfit("1663") or not player:hasOutfit("1662") then
+ player:addOutfitAddon("1663", 1)
+ player:addOutfitAddon("1662", 1)
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Congratulations! You have won a Decaying Defender Outfit.")
+ end
+ end
+ kv:set("taints", 0)
+ logger.info("taints after: {}", kv:get("taints"))
+ end)
+
+ return true
+end
+
+bakragoreOnDeath:register()
diff --git a/data-otservbr-global/scripts/actions/quests/rotten_blood/entrances.lua b/data-otservbr-global/scripts/actions/quests/rotten_blood/entrances.lua
new file mode 100644
index 00000000000..8434c3fdd8f
--- /dev/null
+++ b/data-otservbr-global/scripts/actions/quests/rotten_blood/entrances.lua
@@ -0,0 +1,104 @@
+local entrances = {
+ { position = Position(34092, 31978, 14), destination = Position(33842, 31651, 13) }, -- Murcion hunt area
+ { position = Position(33842, 31650, 13), destination = Position(34092, 31979, 14) }, -- leave Murcion hunt area
+ { position = Position(33905, 31693, 15), destination = Position(32971, 32368, 15) }, -- Murcion lever room
+
+ { position = Position(34093, 32009, 14), destination = Position(33809, 31815, 13) }, -- Chagorz hunt area
+ { position = Position(33809, 31814, 13), destination = Position(34092, 32009, 14) }, -- leave Chagorz hunt area
+ { position = Position(33902, 31881, 15), destination = Position(33071, 32370, 15) }, -- Chagorz lever room
+
+ { position = Position(34120, 31978, 14), destination = Position(34101, 31678, 13) }, -- Ichgahal hunt area
+ { position = Position(34101, 31677, 13), destination = Position(34119, 31979, 14) }, -- leave Ichgahal hunt area
+ { position = Position(34065, 31716, 15), destination = Position(32971, 32336, 15) }, -- Ichgahal lever room
+
+ { position = Position(34117, 32010, 15), destination = Position(34119, 31876, 14) }, -- Vemiath hunt area
+ { position = Position(34119, 31875, 14), destination = Position(34118, 32010, 15) }, -- leave Vemiath hunt area
+ { position = Position(34002, 31820, 15), destination = Position(33071, 32336, 15) }, -- Vemiath lever room
+
+ { position = Position(33069, 32405, 15), destination = Position(34106, 32052, 13) }, -- Bakragore leave room
+}
+
+local teleportEvent = MoveEvent()
+
+function teleportEvent.onStepIn(creature, item, position, fromPosition)
+ local player = creature:getPlayer()
+ if not player then
+ return false
+ end
+
+ local access = player:kv():scoped("rotten-blood-quest"):get("access") or 0
+ if access < 5 then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You should pay respect to the Bloodshade guarding this realm before entering.")
+ player:teleportTo(fromPosition, true)
+ return false
+ end
+
+ for _, entrance in pairs(entrances) do
+ if entrance.position == position then
+ player:teleportTo(entrance.destination)
+ player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
+ break
+ end
+ end
+
+ return true
+end
+
+teleportEvent:type("stepin")
+for _, entrance in pairs(entrances) do
+ teleportEvent:position(entrance.position)
+end
+teleportEvent:register()
+
+----------- Bakragore Entrance Check -----------
+local entrance = {
+ position = Position(34106, 32054, 13),
+ destination = Position(33071, 32403, 15), -- Bakragore lever room
+}
+
+local bakragoreEntrance = MoveEvent()
+
+function bakragoreEntrance.onStepIn(creature, item, position, fromPosition)
+ local player = creature:getPlayer()
+ if not player then
+ return false
+ end
+
+ if player:getLevel() < 250 then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need at least level 250 to enter.")
+ player:teleportTo(fromPosition, true)
+ return false
+ end
+
+ local now, text = os.time(), ""
+ for _, bossName in pairs({ "murcion", "chagorz", "ichgahal", "vemiath" }) do
+ local cooldown = player:kv():scoped("rotten-blood-quest"):scoped(bossName):get("cooldown") or 0
+ if cooldown <= now then
+ text = text .. "\n" .. bossName:titleCase()
+ end
+ end
+
+ if text ~= "" then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You still need to defeat: " .. text)
+ player:teleportTo(fromPosition, true)
+ return false
+ end
+
+ local taints = player:kv():scoped("rotten-blood-quest"):get("taints") or 0
+ if taints < 4 then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have %i taints.", taints))
+ player:teleportTo(fromPosition, true)
+ return false
+ end
+
+ if entrance.position == position then
+ player:teleportTo(entrance.destination)
+ player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
+ end
+
+ return true
+end
+
+bakragoreEntrance:type("stepin")
+bakragoreEntrance:position(entrance.position)
+bakragoreEntrance:register()
diff --git a/data-otservbr-global/scripts/actions/quests/rotten_blood/sacrifice.lua b/data-otservbr-global/scripts/actions/quests/rotten_blood/sacrifice.lua
new file mode 100644
index 00000000000..dcb1cfef4ab
--- /dev/null
+++ b/data-otservbr-global/scripts/actions/quests/rotten_blood/sacrifice.lua
@@ -0,0 +1,34 @@
+local sacrificialPlate = Action()
+
+function sacrificialPlate.onUse(player, item, fromPosition, target, toPosition, isHotkey)
+ local access = player:kv():scoped("rotten-blood-quest"):get("access") or 0
+ if access > 3 then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You already have access.")
+ return true
+ end
+
+ if player:getItemCount(32594) < 1 then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Find the keeper of the sanguine tears and offer his life fluids to the sanguine master of this realm.")
+ return false
+ end
+
+ if access == 1 then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Reinforce your sacrifice for a blooded tear to the sanguine master of this realm to seal this trade.")
+ player:kv():scoped("rotten-blood-quest"):set("access", 2)
+ return true
+ end
+
+ if player:removeItem(32594, 1) then
+ if access == 2 then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your sacrifice has been accepted by the sanguine master of this realm.")
+ elseif access == 3 then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your sacrifices have been accepted by the sanguine master of this realm. Go forth and bedew your root with the waters of life.")
+ end
+ player:kv():scoped("rotten-blood-quest"):set("access", access + 1)
+ end
+
+ return true
+end
+
+sacrificialPlate:id(43891)
+sacrificialPlate:register()
diff --git a/data-otservbr-global/scripts/spells/monster/large_black_ring.lua b/data-otservbr-global/scripts/spells/monster/large_black_ring.lua
new file mode 100644
index 00000000000..8289013ca32
--- /dev/null
+++ b/data-otservbr-global/scripts/spells/monster/large_black_ring.lua
@@ -0,0 +1,35 @@
+local spell = Spell("instant")
+
+local aLarge = {
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 1, 1, 1, 1, 0, 0, 3, 0, 0, 1, 1, 1, 1 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+}
+
+local combatLargeRing = Combat()
+combatLargeRing:setParameter(COMBAT_PARAM_TYPE, COMBAT_DEATHDAMAGE)
+combatLargeRing:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_BLACKSMOKE)
+combatLargeRing:setArea(createCombatArea(aLarge))
+
+local combats = { combatLargeRing }
+
+function spell.onCastSpell(creature, var)
+ local randomCombat = combats[math.random(#combats)]
+ return randomCombat:execute(creature, var)
+end
+
+spell:name("largeblackring")
+spell:words("###large_black_ring")
+spell:needLearn(true)
+spell:isSelfTarget(true)
+spell:register()
diff --git a/data-otservbr-global/scripts/spells/monster/large_blackportal_ring.lua b/data-otservbr-global/scripts/spells/monster/large_blackportal_ring.lua
new file mode 100644
index 00000000000..f506b1a2790
--- /dev/null
+++ b/data-otservbr-global/scripts/spells/monster/large_blackportal_ring.lua
@@ -0,0 +1,35 @@
+local spell = Spell("instant")
+
+local aLarge = {
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 1, 1, 1, 1, 0, 0, 3, 0, 0, 1, 1, 1, 1 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+}
+
+local combatLargeRing = Combat()
+combatLargeRing:setParameter(COMBAT_PARAM_TYPE, COMBAT_DEATHDAMAGE)
+combatLargeRing:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_MORTAREA)
+combatLargeRing:setArea(createCombatArea(aLarge))
+
+local combats = { combatLargeRing }
+
+function spell.onCastSpell(creature, var)
+ local randomCombat = combats[math.random(#combats)]
+ return randomCombat:execute(creature, var)
+end
+
+spell:name("largedeathring")
+spell:words("###large_death_ring")
+spell:needLearn(true)
+spell:isSelfTarget(true)
+spell:register()
diff --git a/data-otservbr-global/scripts/spells/monster/large_energy_ring.lua b/data-otservbr-global/scripts/spells/monster/large_energy_ring.lua
new file mode 100644
index 00000000000..f049228927d
--- /dev/null
+++ b/data-otservbr-global/scripts/spells/monster/large_energy_ring.lua
@@ -0,0 +1,35 @@
+local spell = Spell("instant")
+
+local aLarge = {
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 1, 1, 1, 1, 0, 0, 3, 0, 0, 1, 1, 1, 1 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+}
+
+local combatLargeRing = Combat()
+combatLargeRing:setParameter(COMBAT_PARAM_TYPE, COMBAT_ENERGYDAMAGE)
+combatLargeRing:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_ENERGYHIT)
+combatLargeRing:setArea(createCombatArea(aLarge))
+
+local combats = { combatLargeRing }
+
+function spell.onCastSpell(creature, var)
+ local randomCombat = combats[math.random(#combats)]
+ return randomCombat:execute(creature, var)
+end
+
+spell:name("largeenergyring")
+spell:words("###large_energy_ring")
+spell:needLearn(true)
+spell:isSelfTarget(true)
+spell:register()
diff --git a/data-otservbr-global/scripts/spells/monster/large_fire_ring.lua b/data-otservbr-global/scripts/spells/monster/large_fire_ring.lua
new file mode 100644
index 00000000000..7c14843c640
--- /dev/null
+++ b/data-otservbr-global/scripts/spells/monster/large_fire_ring.lua
@@ -0,0 +1,35 @@
+local spell = Spell("instant")
+
+local aLarge = {
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 1, 1, 1, 1, 0, 0, 3, 0, 0, 1, 1, 1, 1 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+}
+
+local combatLargeRing = Combat()
+combatLargeRing:setParameter(COMBAT_PARAM_TYPE, COMBAT_FIREDAMAGE)
+combatLargeRing:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_FIREATTACK)
+combatLargeRing:setArea(createCombatArea(aLarge))
+
+local combats = { combatLargeRing }
+
+function spell.onCastSpell(creature, var)
+ local randomCombat = combats[math.random(#combats)]
+ return randomCombat:execute(creature, var)
+end
+
+spell:name("largefirering")
+spell:words("###large_fire_ring")
+spell:needLearn(true)
+spell:isSelfTarget(true)
+spell:register()
diff --git a/data-otservbr-global/scripts/spells/monster/large_holy_ring.lua b/data-otservbr-global/scripts/spells/monster/large_holy_ring.lua
new file mode 100644
index 00000000000..50b05444a66
--- /dev/null
+++ b/data-otservbr-global/scripts/spells/monster/large_holy_ring.lua
@@ -0,0 +1,35 @@
+local spell = Spell("instant")
+
+local aLarge = {
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 1, 1, 1, 1, 0, 0, 3, 0, 0, 1, 1, 1, 1 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+}
+
+local combatLargeRing = Combat()
+combatLargeRing:setParameter(COMBAT_PARAM_TYPE, COMBAT_HOLYDAMAGE)
+combatLargeRing:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_CONST_ME_HOLYAREA)
+combatLargeRing:setArea(createCombatArea(aLarge))
+
+local combats = { combatLargeRing }
+
+function spell.onCastSpell(creature, var)
+ local randomCombat = combats[math.random(#combats)]
+ return randomCombat:execute(creature, var)
+end
+
+spell:name("largeholyring")
+spell:words("###large_holy_ring")
+spell:needLearn(true)
+spell:isSelfTarget(true)
+spell:register()
diff --git a/data-otservbr-global/scripts/spells/monster/large_ice_ring.lua b/data-otservbr-global/scripts/spells/monster/large_ice_ring.lua
new file mode 100644
index 00000000000..ef1868f9585
--- /dev/null
+++ b/data-otservbr-global/scripts/spells/monster/large_ice_ring.lua
@@ -0,0 +1,35 @@
+local spell = Spell("instant")
+
+local aLarge = {
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 1, 1, 1, 1, 0, 0, 3, 0, 0, 1, 1, 1, 1 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+}
+
+local combatLargeRing = Combat()
+combatLargeRing:setParameter(COMBAT_PARAM_TYPE, COMBAT_ICEDAMAGE)
+combatLargeRing:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_ICEAREA)
+combatLargeRing:setArea(createCombatArea(aLarge))
+
+local combats = { combatLargeRing }
+
+function spell.onCastSpell(creature, var)
+ local randomCombat = combats[math.random(#combats)]
+ return randomCombat:execute(creature, var)
+end
+
+spell:name("largeicering")
+spell:words("###large_ice_ring")
+spell:needLearn(true)
+spell:isSelfTarget(true)
+spell:register()
diff --git a/data-otservbr-global/scripts/spells/monster/large_pink_ring.lua b/data-otservbr-global/scripts/spells/monster/large_pink_ring.lua
new file mode 100644
index 00000000000..1bb28cefeb9
--- /dev/null
+++ b/data-otservbr-global/scripts/spells/monster/large_pink_ring.lua
@@ -0,0 +1,35 @@
+local spell = Spell("instant")
+
+local aLarge = {
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 1, 1, 1, 1, 0, 0, 3, 0, 0, 1, 1, 1, 1 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+}
+
+local combatLargeRing = Combat()
+combatLargeRing:setParameter(COMBAT_PARAM_TYPE, COMBAT_ENERGYDAMAGE)
+combatLargeRing:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_ELECTRICALSPARK)
+combatLargeRing:setArea(createCombatArea(aLarge))
+
+local combats = { combatLargeRing }
+
+function spell.onCastSpell(creature, var)
+ local randomCombat = combats[math.random(#combats)]
+ return randomCombat:execute(creature, var)
+end
+
+spell:name("largepinkring")
+spell:words("###large_pink_ring")
+spell:needLearn(true)
+spell:isSelfTarget(true)
+spell:register()
diff --git a/data-otservbr-global/scripts/spells/monster/large_poison_ring.lua b/data-otservbr-global/scripts/spells/monster/large_poison_ring.lua
new file mode 100644
index 00000000000..98737e088df
--- /dev/null
+++ b/data-otservbr-global/scripts/spells/monster/large_poison_ring.lua
@@ -0,0 +1,35 @@
+local spell = Spell("instant")
+
+local aLarge = {
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 1, 1, 1, 1, 0, 0, 3, 0, 0, 1, 1, 1, 1 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+}
+
+local combatLargeRing = Combat()
+combatLargeRing:setParameter(COMBAT_PARAM_TYPE, COMBAT_EARTHDAMAGE)
+combatLargeRing:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_GREEN_RINGS)
+combatLargeRing:setArea(createCombatArea(aLarge))
+
+local combats = { combatLargeRing }
+
+function spell.onCastSpell(creature, var)
+ local randomCombat = combats[math.random(#combats)]
+ return randomCombat:execute(creature, var)
+end
+
+spell:name("largepoisonring")
+spell:words("###large_poison_ring")
+spell:needLearn(true)
+spell:isSelfTarget(true)
+spell:register()
diff --git a/data-otservbr-global/scripts/spells/monster/large_purple_ring_smoke.lua b/data-otservbr-global/scripts/spells/monster/large_purple_ring_smoke.lua
new file mode 100644
index 00000000000..7d7d40c26c3
--- /dev/null
+++ b/data-otservbr-global/scripts/spells/monster/large_purple_ring_smoke.lua
@@ -0,0 +1,35 @@
+local spell = Spell("instant")
+
+local aLarge = {
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 1, 1, 1, 1, 0, 0, 3, 0, 0, 1, 1, 1, 1 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+}
+
+local combatLargeRing = Combat()
+combatLargeRing:setParameter(COMBAT_PARAM_TYPE, COMBAT_HOLYDAMAGE)
+combatLargeRing:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_PURPLESMOKE)
+combatLargeRing:setArea(createCombatArea(aLarge))
+
+local combats = { combatLargeRing }
+
+function spell.onCastSpell(creature, var)
+ local randomCombat = combats[math.random(#combats)]
+ return randomCombat:execute(creature, var)
+end
+
+spell:name("largepurplering")
+spell:words("###large_purple_ring")
+spell:needLearn(true)
+spell:isSelfTarget(true)
+spell:register()
diff --git a/data-otservbr-global/scripts/spells/monster/large_red_ring.lua b/data-otservbr-global/scripts/spells/monster/large_red_ring.lua
new file mode 100644
index 00000000000..0103ae5c31b
--- /dev/null
+++ b/data-otservbr-global/scripts/spells/monster/large_red_ring.lua
@@ -0,0 +1,35 @@
+local spell = Spell("instant")
+
+local aLarge = {
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 1, 1, 1, 1, 0, 0, 3, 0, 0, 1, 1, 1, 1 },
+ { 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
+ { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0 },
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+}
+
+local combatLargeRing = Combat()
+combatLargeRing:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)
+combatLargeRing:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_REDSMOKE)
+combatLargeRing:setArea(createCombatArea(aLarge))
+
+local combats = { combatLargeRing }
+
+function spell.onCastSpell(creature, var)
+ local randomCombat = combats[math.random(#combats)]
+ return randomCombat:execute(creature, var)
+end
+
+spell:name("largeredring")
+spell:words("###large_red_ring")
+spell:needLearn(true)
+spell:isSelfTarget(true)
+spell:register()
diff --git a/data-otservbr-global/world/otservbr-monster.xml b/data-otservbr-global/world/otservbr-monster.xml
index 424e4210b80..2fe127164f3 100644
--- a/data-otservbr-global/world/otservbr-monster.xml
+++ b/data-otservbr-global/world/otservbr-monster.xml
@@ -162462,6 +162462,9 @@
+
+
+
@@ -162487,6 +162490,15 @@
+
+
+
+
+
+
+
+
+
@@ -162502,6 +162514,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162518,6 +162542,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162534,6 +162573,15 @@
+
+
+
+
+
+
+
+
+
@@ -162553,12 +162601,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162574,9 +162637,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162590,6 +162674,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162603,6 +162699,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162628,11 +162742,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162659,6 +162788,15 @@
+
+
+
+
+
+
+
+
+
@@ -162684,6 +162822,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162704,6 +162857,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162716,19 +162881,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162747,15 +162951,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162767,6 +163004,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162776,18 +163040,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162795,21 +163146,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162817,6 +163243,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162831,6 +163278,15 @@
+
+
+
+
+
+
+
+
+
@@ -162840,30 +163296,156 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162876,6 +163458,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162885,6 +163497,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162897,10 +163533,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162908,6 +163670,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162917,15 +163703,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -162936,29 +163773,167 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
+
+
-
-
+
+
-
-
-
+
+
@@ -162970,6 +163945,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163232,10 +164222,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163263,6 +164271,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163270,12 +164302,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163302,6 +164364,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163309,9 +164392,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163326,18 +164427,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163368,11 +164523,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163382,9 +164579,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163409,6 +164633,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163418,14 +164657,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163464,15 +164733,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163497,12 +164796,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163512,15 +164841,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163530,6 +164892,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163541,12 +164951,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163561,12 +165100,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163582,12 +165157,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163597,18 +165184,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163616,6 +165248,12 @@
+
+
+
+
+
+
@@ -163627,6 +165265,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163639,24 +165289,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163666,6 +165361,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163680,6 +165393,15 @@
+
+
+
+
+
+
+
+
+
@@ -163689,9 +165411,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163702,6 +165469,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163712,15 +165491,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163732,6 +165547,15 @@
+
+
+
+
+
+
+
+
+
@@ -163741,12 +165565,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -163788,6 +165630,12 @@
+
+
+
+
+
+
@@ -163804,6 +165652,9 @@
+
+
+
@@ -163824,6 +165675,9 @@
+
+
+
@@ -173593,6 +175447,9 @@
+
+
+
@@ -173611,9 +175468,18 @@
+
+
+
+
+
+
+
+
+
@@ -173640,18 +175506,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -173682,6 +175563,15 @@
+
+
+
+
+
+
+
+
+
@@ -173698,6 +175588,12 @@
+
+
+
+
+
+
@@ -173729,10 +175625,16 @@
+
+
+
+
+
+
@@ -173755,10 +175657,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -173767,6 +175699,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -173783,6 +175748,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -173795,9 +175832,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -173821,14 +175885,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -173839,6 +175936,9 @@
+
+
+
@@ -173851,15 +175951,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -173875,6 +175993,9 @@
+
+
+
@@ -173882,6 +176003,9 @@
+
+
+
@@ -173899,6 +176023,12 @@
+
+
+
+
+
+
@@ -173924,6 +176054,12 @@
+
+
+
+
+
+
@@ -173942,12 +176078,21 @@
+
+
+
+
+
+
+
+
+
@@ -173961,6 +176106,15 @@
+
+
+
+
+
+
+
+
+
@@ -174451,6 +176605,9 @@
+
+
+
@@ -174478,9 +176635,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -174516,9 +176697,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -174558,6 +176769,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -174602,9 +176837,15 @@
+
+
+
+
+
+
@@ -174614,6 +176855,12 @@
+
+
+
+
+
+
@@ -174678,9 +176925,15 @@
+
+
+
+
+
+
@@ -174731,6 +176984,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -174770,6 +177050,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -174799,6 +177091,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -174837,12 +177141,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -174859,6 +177178,9 @@
+
+
+
@@ -174870,6 +177192,12 @@
+
+
+
+
+
+
@@ -174918,6 +177246,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -174928,18 +177268,27 @@
+
+
+
+
+
+
+
+
+
@@ -174952,13 +177301,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -175014,9 +177378,18 @@
+
+
+
+
+
+
+
+
+
@@ -175052,9 +177425,18 @@
+
+
+
+
+
+
+
+
+
@@ -175064,9 +177446,18 @@
+
+
+
+
+
+
+
+
+
@@ -175091,18 +177482,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -175130,13 +177560,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -175156,12 +177601,18 @@
+
+
+
+
+
+
@@ -175169,6 +177620,9 @@
+
+
+
@@ -175180,6 +177634,9 @@
+
+
+
@@ -175193,6 +177650,9 @@
+
+
+
diff --git a/data-otservbr-global/world/otservbr-npc.xml b/data-otservbr-global/world/otservbr-npc.xml
index 5c53601aa9c..897f70baa1d 100644
--- a/data-otservbr-global/world/otservbr-npc.xml
+++ b/data-otservbr-global/world/otservbr-npc.xml
@@ -1893,6 +1893,9 @@
+
+
+
@@ -2451,6 +2454,9 @@
+
+
+
@@ -2863,6 +2869,9 @@
+
+
+
diff --git a/data/items/items.xml b/data/items/items.xml
index c1b8e8a63ba..17bc96b1bad 100644
--- a/data/items/items.xml
+++ b/data/items/items.xml
@@ -74883,21 +74883,12 @@ Granted by TibiaGoals.com"/>
-
-
- -
-
-
-
- -
-
-
-
- -
-
+
-
+
+
-
- -
-
+
-
+
-
@@ -74916,6 +74907,22 @@ Granted by TibiaGoals.com"/>
-
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
-
@@ -74947,6 +74954,190 @@ Granted by TibiaGoals.com"/>
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
-
@@ -75117,6 +75308,196 @@ Granted by TibiaGoals.com"/>
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
-
@@ -75850,6 +76231,9 @@ Granted by TibiaGoals.com"/>
+ -
+
+
-
diff --git a/data/libs/tables/doors.lua b/data/libs/tables/doors.lua
index c87e8cb01ec..3daafd890b9 100644
--- a/data/libs/tables/doors.lua
+++ b/data/libs/tables/doors.lua
@@ -225,6 +225,7 @@ QuestDoorTable = {
{ closedDoor = 36547, openDoor = 36548 },
{ closedDoor = 39351, openDoor = 39353 },
{ closedDoor = 39352, openDoor = 39354 },
+ { closedDoor = 42744, openDoor = 42745 },
}
-- Level doors.
From b4f60576e3148f49395aa84eab2a0a861bc0b563 Mon Sep 17 00:00:00 2001
From: Luan Luciano
Date: Tue, 31 Dec 2024 15:19:26 -0300
Subject: [PATCH 07/32] fix: imbuement shrine and time guardian script errors
(#2877)
---
.../actions/objects/imbuement_shrine.lua | 2 +-
.../actions/object/imbuement_shrine.lua | 2 +-
.../scripts/lib/register_actions.lua | 2 +-
.../cults_of_tibia/actions_analyser.lua | 2 +-
.../cults_of_tibia/actions_counter_agent.lua | 2 +-
.../cults_of_tibia/actions_magnifier.lua | 2 +-
.../actions_gnomish_pesticide.lua | 2 +-
.../actions_using_crystals.lua | 2 +-
.../magma_bubble_fight.lua | 2 +-
.../roshamuul_quest/actions_mixture.lua | 2 +-
.../quests/roshamuul_quest/actions_mortar.lua | 2 +-
.../quests/roshamuul_quest/actions_trough.lua | 2 +-
.../quests/spike_tasks/actions_fertilizer.lua | 2 +-
.../quests/spike_tasks/actions_lode_stone.lua | 2 +-
.../spike_tasks/actions_spirit_shovel.lua | 2 +-
.../spike_tasks/actions_tuning_fork.lua | 2 +-
.../scripts/spells/monster/time_guardiann.lua | 99 +++++++++++++------
.../actions/items/bed_modification_kits.lua | 6 +-
.../items/exercise_training_weapons.lua | 2 +-
19 files changed, 93 insertions(+), 46 deletions(-)
diff --git a/data-canary/scripts/actions/objects/imbuement_shrine.lua b/data-canary/scripts/actions/objects/imbuement_shrine.lua
index 11d75150cf8..3d3d7ded8fe 100644
--- a/data-canary/scripts/actions/objects/imbuement_shrine.lua
+++ b/data-canary/scripts/actions/objects/imbuement_shrine.lua
@@ -5,7 +5,7 @@ function imbuement.onUse(player, item, fromPosition, target, toPosition, isHotke
return player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You did not collect enough knowledge from the ancient Shapers. Visit the Shaper temple in Montag for help.")
end
- if not target or not (target:isItem()) then
+ if not target or type(target) ~= "userdata" or not target:isItem() then
return player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use the shrine on an valid item.")
end
diff --git a/data-otservbr-global/scripts/actions/object/imbuement_shrine.lua b/data-otservbr-global/scripts/actions/object/imbuement_shrine.lua
index f5d878df284..5cec0f7506d 100644
--- a/data-otservbr-global/scripts/actions/object/imbuement_shrine.lua
+++ b/data-otservbr-global/scripts/actions/object/imbuement_shrine.lua
@@ -5,7 +5,7 @@ function imbuement.onUse(player, item, fromPosition, target, toPosition, isHotke
return player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You did not collect enough knowledge from the ancient Shapers. Visit the Shaper temple in Thais for help.")
end
- if not target or not (target:isItem()) then
+ if type(target) ~= "userdata" or not target:isItem() then
return player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use the shrine on an valid item.")
end
diff --git a/data-otservbr-global/scripts/lib/register_actions.lua b/data-otservbr-global/scripts/lib/register_actions.lua
index d89772f7ce9..48ef4c38993 100644
--- a/data-otservbr-global/scripts/lib/register_actions.lua
+++ b/data-otservbr-global/scripts/lib/register_actions.lua
@@ -242,7 +242,7 @@ local function addFerumbrasAscendantReward(player, target, toPosition)
end
function onDestroyItem(player, item, fromPosition, target, toPosition, isHotkey)
- if not target or target == nil or type(target) ~= "userdata" or not target:isItem() then
+ if not target or type(target) ~= "userdata" or not target:isItem() then
return false
end
diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_analyser.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_analyser.lua
index ce01d36c244..d50d13b6cbd 100644
--- a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_analyser.lua
+++ b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_analyser.lua
@@ -6,7 +6,7 @@ function cultsOfTibiaAnalyser.onUse(player, item, fromPosition, target, toPositi
return true
end
- if not target:isItem() then
+ if not target or type(target) ~= "userdata" or not target:isItem() then
return false
end
diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_counter_agent.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_counter_agent.lua
index 24b414399ee..b083bd45a4c 100644
--- a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_counter_agent.lua
+++ b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_counter_agent.lua
@@ -19,7 +19,7 @@ function cultsOfTibiaCounter.onUse(player, item, fromPosition, target, toPositio
return true
end
- if not target:isItem() then
+ if not target or type(target) ~= "userdata" or not target:isItem() then
return false
end
diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_magnifier.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_magnifier.lua
index e50f94226b0..929ebf362f1 100644
--- a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_magnifier.lua
+++ b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_magnifier.lua
@@ -7,7 +7,7 @@ function cultsOfTibiaMagnifier.onUse(player, item, fromPosition, target, toPosit
return true
end
- if not target:isItem() then
+ if not target or type(target) ~= "userdata" or not target:isItem() then
return false
end
diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnomish_pesticide.lua b/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnomish_pesticide.lua
index 55aae368926..9d5b9da150d 100644
--- a/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnomish_pesticide.lua
+++ b/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnomish_pesticide.lua
@@ -5,7 +5,7 @@ function dangerousDepthPesticide.onUse(player, item, fromPosition, target, toPos
return true
end
- if not (target or target:isItem()) then
+ if not target or type(target) ~= "userdata" or not target:isItem() then
return false
end
diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/actions_using_crystals.lua b/data-otservbr-global/scripts/quests/dangerous_depth/actions_using_crystals.lua
index 6f134dab02d..948106be648 100644
--- a/data-otservbr-global/scripts/quests/dangerous_depth/actions_using_crystals.lua
+++ b/data-otservbr-global/scripts/quests/dangerous_depth/actions_using_crystals.lua
@@ -405,7 +405,7 @@ function dangerousDepthCrystals.onUse(player, item, fromPosition, target, toPosi
return true
end
- if not target or not target.isItem or not target:isItem() then
+ if not target or type(target) ~= "userdata" or not target:isItem() then
return false
end
diff --git a/data-otservbr-global/scripts/quests/primal_ordeal_quest/magma_bubble_fight.lua b/data-otservbr-global/scripts/quests/primal_ordeal_quest/magma_bubble_fight.lua
index 36de1fb1497..35b9728bd69 100644
--- a/data-otservbr-global/scripts/quests/primal_ordeal_quest/magma_bubble_fight.lua
+++ b/data-otservbr-global/scripts/quests/primal_ordeal_quest/magma_bubble_fight.lua
@@ -208,7 +208,7 @@ function chargedFlameAction.onUse(player, item, fromPosition, target, toPosition
if not player then
return false
end
- if not target or not target:isItem() then
+ if not target or type(target) ~= "userdata" or not target:isItem() then
return false
end
if target:getId() ~= config.cooledCrystalId then
diff --git a/data-otservbr-global/scripts/quests/roshamuul_quest/actions_mixture.lua b/data-otservbr-global/scripts/quests/roshamuul_quest/actions_mixture.lua
index cb2e30d97c8..1e42b82231b 100644
--- a/data-otservbr-global/scripts/quests/roshamuul_quest/actions_mixture.lua
+++ b/data-otservbr-global/scripts/quests/roshamuul_quest/actions_mixture.lua
@@ -6,7 +6,7 @@ local buckets = {
local lowerRoshamuulMixtune = Action()
function lowerRoshamuulMixtune.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if (target == nil) or not target:isItem() then
+ if not target or type(target) ~= "userdata" or not target:isItem() then
return false
end
diff --git a/data-otservbr-global/scripts/quests/roshamuul_quest/actions_mortar.lua b/data-otservbr-global/scripts/quests/roshamuul_quest/actions_mortar.lua
index f62c18dc089..9add87e1b51 100644
--- a/data-otservbr-global/scripts/quests/roshamuul_quest/actions_mortar.lua
+++ b/data-otservbr-global/scripts/quests/roshamuul_quest/actions_mortar.lua
@@ -1,6 +1,6 @@
local lowerRoshamuulMortar = Action()
function lowerRoshamuulMortar.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if (target == nil) or not target:isItem() then
+ if not target or type(target) ~= "userdata" or not target:isItem() then
return false
end
diff --git a/data-otservbr-global/scripts/quests/roshamuul_quest/actions_trough.lua b/data-otservbr-global/scripts/quests/roshamuul_quest/actions_trough.lua
index c511ae3d921..11613fe68d6 100644
--- a/data-otservbr-global/scripts/quests/roshamuul_quest/actions_trough.lua
+++ b/data-otservbr-global/scripts/quests/roshamuul_quest/actions_trough.lua
@@ -1,7 +1,7 @@
local lowerRoshamuulTrough = Action()
function lowerRoshamuulTrough.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if (target == nil) or not target:isItem() then
+ if not target or type(target) ~= "userdata" or not target:isItem() then
return false
end
diff --git a/data-otservbr-global/scripts/quests/spike_tasks/actions_fertilizer.lua b/data-otservbr-global/scripts/quests/spike_tasks/actions_fertilizer.lua
index c97e06e98a4..5965fce6d54 100644
--- a/data-otservbr-global/scripts/quests/spike_tasks/actions_fertilizer.lua
+++ b/data-otservbr-global/scripts/quests/spike_tasks/actions_fertilizer.lua
@@ -8,7 +8,7 @@ function spikeTasksFertilizer.onUse(player, item, fromPosition, target, toPositi
return false
end
- if (target == nil) or not target:isItem() or (target:getId() ~= 19215) then
+ if not target or type(target) ~= "userdata" or not target:isItem() or (target:getId() ~= 19215) then
return false
end
diff --git a/data-otservbr-global/scripts/quests/spike_tasks/actions_lode_stone.lua b/data-otservbr-global/scripts/quests/spike_tasks/actions_lode_stone.lua
index 7a3060a1fc9..b976684a818 100644
--- a/data-otservbr-global/scripts/quests/spike_tasks/actions_lode_stone.lua
+++ b/data-otservbr-global/scripts/quests/spike_tasks/actions_lode_stone.lua
@@ -11,7 +11,7 @@ function spikeTasksStone.onUse(player, item, fromPosition, target, toPosition, i
return false
end
- if (target == nil) or not target:isItem() or (target:getId() ~= 19217) then
+ if not target or type(target) ~= "userdata" or not target:isItem() or (target:getId() ~= 19217) then
return false
end
diff --git a/data-otservbr-global/scripts/quests/spike_tasks/actions_spirit_shovel.lua b/data-otservbr-global/scripts/quests/spike_tasks/actions_spirit_shovel.lua
index 51979b622ca..f2d2c13fc42 100644
--- a/data-otservbr-global/scripts/quests/spike_tasks/actions_spirit_shovel.lua
+++ b/data-otservbr-global/scripts/quests/spike_tasks/actions_spirit_shovel.lua
@@ -12,7 +12,7 @@ function spikeTasksShovel.onUse(player, item, fromPosition, target, toPosition,
return player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
end
- if (target == nil) or not target:isItem() or (target:getId() ~= 19211) then
+ if not target or type(target) ~= "userdata" or not target:isItem() or (target:getId() ~= 19211) then
return player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
end
diff --git a/data-otservbr-global/scripts/quests/spike_tasks/actions_tuning_fork.lua b/data-otservbr-global/scripts/quests/spike_tasks/actions_tuning_fork.lua
index 2ae79bd368c..74086b9408f 100644
--- a/data-otservbr-global/scripts/quests/spike_tasks/actions_tuning_fork.lua
+++ b/data-otservbr-global/scripts/quests/spike_tasks/actions_tuning_fork.lua
@@ -4,7 +4,7 @@ function spikeTasksFork.onUse(player, item, fromPosition, target, toPosition, is
return player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
end
- if (target == nil) or not target:isItem() or (target:getId() ~= 19208) then
+ if not target or type(target) ~= "userdata" or not target:isItem() or (target:getId() ~= 19208) then
return player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
end
diff --git a/data-otservbr-global/scripts/spells/monster/time_guardiann.lua b/data-otservbr-global/scripts/spells/monster/time_guardiann.lua
index f98ee4195c9..9e4ffe6ae81 100644
--- a/data-otservbr-global/scripts/spells/monster/time_guardiann.lua
+++ b/data-otservbr-global/scripts/spells/monster/time_guardiann.lua
@@ -3,51 +3,94 @@ local monsters = {
[2] = { pos = Position(32815, 32664, 14) },
}
-local function functionBack(position, oldpos)
- local guardian = Tile(position):getTopCreature()
- local bool, diference, health = false, 0, 0
- local spectators, spectator = Game.getSpectators(Position(32813, 32664, 14), false, false, 15, 15, 15, 15)
- for v = 1, #spectators do
- spectator = spectators[v]
- if spectator:getName():lower() == "the blazing time guardian" or spectator:getName():lower() == "the freezing time guardian" then
- oldpos = spectator:getPosition()
- bool = true
+local function functionBack(pos, oldPos)
+ local position = Position(pos)
+ if not position then
+ return
+ end
+
+ local tile = Tile(position)
+ if not tile then
+ return
+ end
+
+ local guardian = tile:getTopCreature()
+ if not guardian then
+ return
+ end
+
+ local haveGuardianMonster = false
+ local spectator1 = nil
+
+ local spectators1 = Game.getSpectators(Position(32813, 32664, 14), false, false, 15, 15, 15, 15)
+ for index = 1, #spectators1 do
+ spectator1 = spectators1[index]
+ if spectator1 then
+ if spectator1:isMonster() and spectator1:getName():lower() == "the blazing time guardian" or spectator1:getName():lower() == "the freezing time guardian" then
+ oldPos = spectator1:getPosition()
+ haveGuardianMonster = true
+ end
end
end
- if not bool then
+
+ if not haveGuardianMonster then
guardian:remove()
return true
end
- local specs, spec = Game.getSpectators(Position(32813, 32664, 14), false, false, 15, 15, 15, 15)
- for i = 1, #specs do
- spec = specs[i]
- if spec:isMonster() and spec:getName():lower() == "the blazing time guardian" or spec:getName():lower() == "the freezing time guardian" then
- spec:teleportTo(position)
- health = spec:getHealth()
- diference = guardian:getHealth() - health
+
+ local diference = 0
+ local spectator = nil
+
+ local spectators2, spectator2 = Game.getSpectators(Position(32813, 32664, 14), false, false, 15, 15, 15, 15)
+ for i = 1, #spectators2 do
+ spectator2 = spectators2[i]
+ if spectator2 then
+ if spectator2:isMonster() and spectator2:getName():lower() == "the blazing time guardian" or spectator2:getName():lower() == "the freezing time guardian" then
+ spectator2:teleportTo(position)
+ diference = guardian:getHealth() - spectator2:getHealth()
+ end
end
end
- guardian:addHealth(-diference)
- guardian:teleportTo(oldpos)
+
+ if diference > 0 then
+ guardian:addHealth(-diference)
+ end
+
+ guardian:teleportTo(oldPos)
end
local spell = Spell("instant")
function spell.onCastSpell(creature, var)
- local index = math.random(1, 2)
local monsterPos = creature:getPosition()
if monsterPos.z ~= 14 then
return true
end
+
+ local index = math.random(1, 2)
local position = monsters[index].pos
- local form = Tile(position):getTopCreature()
- creature:teleportTo(position)
- local diference, health = 0, 0
- health = creature:getHealth()
- diference = form:getHealth() - health
- form:addHealth(-diference)
- form:teleportTo(monsterPos)
- addEvent(functionBack, 30 * 1000, position, monsterPos)
+ if position then
+ local tile = Tile(position)
+ if not tile then
+ return true
+ end
+
+ local form = tile:getTopCreature()
+ if not form then
+ return true
+ end
+
+ creature:teleportTo(position)
+
+ local diference = form:getHealth() - creature:getHealth()
+ if diference and diference > 0 then
+ form:addHealth(-diference)
+ end
+
+ form:teleportTo(monsterPos)
+ addEvent(functionBack, 30 * 1000, position, monsterPos)
+ end
+
return true
end
diff --git a/data/scripts/actions/items/bed_modification_kits.lua b/data/scripts/actions/items/bed_modification_kits.lua
index a406f7fd5b2..a50a7a683ec 100644
--- a/data/scripts/actions/items/bed_modification_kits.lua
+++ b/data/scripts/actions/items/bed_modification_kits.lua
@@ -19,8 +19,12 @@ end
local bedModificationKits = Action()
function bedModificationKits.onUse(player, item, fromPosition, target, toPosition, isHotkey)
+ if not target or type(target) ~= "userdata" or not target:isItem() then
+ return false
+ end
+
local newBed = setting[item:getId()]
- if not newBed or not target or not target:isItem() then
+ if not newBed then
return false
end
diff --git a/data/scripts/actions/items/exercise_training_weapons.lua b/data/scripts/actions/items/exercise_training_weapons.lua
index 3c62d7c1183..52730ad6edb 100644
--- a/data/scripts/actions/items/exercise_training_weapons.lua
+++ b/data/scripts/actions/items/exercise_training_weapons.lua
@@ -133,7 +133,7 @@ end
local exerciseTraining = Action()
function exerciseTraining.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if not target or type(target) == "table" or not target:getId() then
+ if not target or type(target) ~= "userdata" or not target:isItem() then
return true
end
From b4f2994b2319f22fa6674655a588feff94d9305e Mon Sep 17 00:00:00 2001
From: Luan Luciano
Date: Tue, 31 Dec 2024 15:27:37 -0300
Subject: [PATCH 08/32] fix: problem with stack of items on the podium after
server save (#2879)
---
data/events/scripts/player.lua | 35 +++++++++++--------
.../functions/items/item_type_functions.cpp | 11 ++++++
.../functions/items/item_type_functions.hpp | 1 +
3 files changed, 33 insertions(+), 14 deletions(-)
diff --git a/data/events/scripts/player.lua b/data/events/scripts/player.lua
index c526553132d..af1bb93e846 100644
--- a/data/events/scripts/player.lua
+++ b/data/events/scripts/player.lua
@@ -275,12 +275,18 @@ function Player:onMoveItem(item, count, fromPosition, toPosition, fromCylinder,
return true
end
- -- Bath tube
local toTile = Tile(toCylinder:getPosition())
if toTile then
local topDownItem = toTile:getTopDownItem()
- if topDownItem and table.contains({ BATHTUB_EMPTY, BATHTUB_FILLED }, topDownItem:getId()) then
- return false
+ if topDownItem then
+ local topDownItemItemId = topDownItem:getId()
+ if table.contains({ BATHTUB_EMPTY, BATHTUB_FILLED }, topDownItemItemId) then -- Bath tube
+ return false
+ elseif ItemType(topDownItemItemId):isPodium() then -- Podium
+ self:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
+ self:getPosition():sendMagicEffect(CONST_ME_POFF)
+ return false
+ end
end
end
@@ -348,19 +354,20 @@ function Player:onMoveItem(item, count, fromPosition, toPosition, fromCylinder,
return false
end
- -- Players cannot throw items on reward chest
- local tileChest = Tile(toPosition)
- if tileChest and tileChest:getItemById(ITEM_REWARD_CHEST) then
- self:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
- self:getPosition():sendMagicEffect(CONST_ME_POFF)
- return false
- end
+ if tile then
+ -- Players cannot throw items on reward chest
+ if tile:getItemById(ITEM_REWARD_CHEST) then
+ self:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
+ self:getPosition():sendMagicEffect(CONST_ME_POFF)
+ return false
+ end
- if tile and tile:getItemById(370) then
-- Trapdoor
- self:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
- self:getPosition():sendMagicEffect(CONST_ME_POFF)
- return false
+ if tile:getItemById(370) then
+ self:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
+ self:getPosition():sendMagicEffect(CONST_ME_POFF)
+ return false
+ end
end
if not antiPush(self, item, count, fromPosition, toPosition, fromCylinder, toCylinder) then
diff --git a/src/lua/functions/items/item_type_functions.cpp b/src/lua/functions/items/item_type_functions.cpp
index 1669e6b795e..9c12f80e586 100644
--- a/src/lua/functions/items/item_type_functions.cpp
+++ b/src/lua/functions/items/item_type_functions.cpp
@@ -283,6 +283,17 @@ int ItemTypeFunctions::luaItemTypeIsQuiver(lua_State* L) {
return 1;
}
+int ItemTypeFunctions::luaItemTypeIsPodium(lua_State* L) {
+ // itemType:isPodium()
+ const auto* itemType = Lua::getUserdata(L, 1);
+ if (itemType) {
+ Lua::pushBoolean(L, itemType->isPodium);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
int ItemTypeFunctions::luaItemTypeGetType(lua_State* L) {
// itemType:getType()
const auto* itemType = Lua::getUserdata(L, 1);
diff --git a/src/lua/functions/items/item_type_functions.hpp b/src/lua/functions/items/item_type_functions.hpp
index 19a401bc235..fa17f57575f 100644
--- a/src/lua/functions/items/item_type_functions.hpp
+++ b/src/lua/functions/items/item_type_functions.hpp
@@ -35,6 +35,7 @@ class ItemTypeFunctions {
static int luaItemTypeIsPickupable(lua_State* L);
static int luaItemTypeIsKey(lua_State* L);
static int luaItemTypeIsQuiver(lua_State* L);
+ static int luaItemTypeIsPodium(lua_State* L);
static int luaItemTypeGetType(lua_State* L);
static int luaItemTypeGetId(lua_State* L);
From bb6f7b085ea39c6443f4bf02ff84110e9572b7fe Mon Sep 17 00:00:00 2001
From: murilo09 <78226931+murilo09@users.noreply.github.com>
Date: Tue, 31 Dec 2024 20:25:51 -0300
Subject: [PATCH 09/32] fix: distro rotten blood quest warnings (#3204)
---
.../monster/bosses/bakragore.lua | 31 ++++++++-----------
.../quests/rotten_blood/darklight_matter.lua | 2 +-
.../quests/rotten_blood/sopping_corpus.lua | 10 +++---
data-otservbr-global/npc/yasir.lua | 1 +
data/items/items.xml | 18 +++++++++--
5 files changed, 35 insertions(+), 27 deletions(-)
diff --git a/data-otservbr-global/monster/bosses/bakragore.lua b/data-otservbr-global/monster/bosses/bakragore.lua
index 2a0ca5c60fc..c13592e7ade 100644
--- a/data-otservbr-global/monster/bosses/bakragore.lua
+++ b/data-otservbr-global/monster/bosses/bakragore.lua
@@ -84,24 +84,19 @@ monster.voices = {
}
monster.loot = {
- { name = "crystal coin", chance = 8938, maxCount = 200 },
- { name = "supreme health potion", chance = 8938, maxCount = 300 },
- { name = "ultimate mana potion", chance = 11433, maxCount = 300 },
- { name = "ultimate spirit potion", chance = 11433, maxCount = 300 },
- { name = "berserk potion", chance = 10938, maxCount = 90 },
- { name = "bullseye potion", chance = 10938, maxCount = 90 },
- { name = "mastermind potion", chance = 10938, maxCount = 30 },
- { name = "blue gem", chance = 10570, maxCount = 10 },
- { name = "giant amethyst", chance = 10570, maxCount = 10 },
- { name = "giant emerald", chance = 10570, maxCount = 10 },
- { name = "giant ruby", chance = 10570, maxCount = 10 },
- { name = "red gem", chance = 10570, maxCount = 10 },
- { name = "giant sapphire", chance = 10570, maxCount = 10 },
- { name = "giant topaz", chance = 10570, maxCount = 10 },
- { name = "violet gem", chance = 10970, maxCount = 10 },
- { name = "yellow gem", chance = 10970, maxCount = 10 },
- { name = "figurine of bakragore", chance = 10970 },
- { name = "bakragore's amalgamation", chance = 570 },
+ { name = "crystal coin", chance = 8938, maxCount = 165 },
+ { name = "ultimate mana potion", chance = 11433, maxCount = 198 },
+ { name = "giant amethyst", chance = 10570, maxCount = 4 },
+ { name = "giant topaz", chance = 10570, maxCount = 6 },
+ { name = "ultimate spirit potion", chance = 11433, maxCount = 45 },
+ { name = "giant ruby", chance = 10570, maxCount = 1 },
+ { name = "giant sapphire", chance = 10570, maxCount = 1 },
+ { name = "mastermind potion", chance = 10938, maxCount = 23 },
+ { id = 3039, chance = 10570, maxCount = 3 }, -- red gem
+ { name = "violet gem", chance = 10970, maxCount = 8 },
+ { name = "yellow gem", chance = 10970, maxCount = 9 },
+ -- { name = "figurine of bakragore", chance = 10970 },
+ -- { name = "bakragore's amalgamation", chance = 570 },
{ name = "spiritual horseshoe", chance = 470 },
{ id = 43895, chance = 360 }, -- Bag you covet
}
diff --git a/data-otservbr-global/monster/quests/rotten_blood/darklight_matter.lua b/data-otservbr-global/monster/quests/rotten_blood/darklight_matter.lua
index 10d4ff36d84..fa2fae08e24 100644
--- a/data-otservbr-global/monster/quests/rotten_blood/darklight_matter.lua
+++ b/data-otservbr-global/monster/quests/rotten_blood/darklight_matter.lua
@@ -82,7 +82,7 @@ monster.loot = {
{ name = "unstable darklight matter", chance = 9060, maxCount = 1 },
{ name = "darklight core", chance = 12887, maxCount = 1 },
{ name = "ultimate health potion", chance = 6553, maxCount = 6 },
- { id = 3039, chance = 5852, maxCount = 1 }, -- red gem
+ { id = 3039, chance = 1430 }, -- red gem
{ name = "darklight matter", chance = 8849, maxCount = 1 },
{ name = "rubber cap", chance = 7180, maxCount = 1 },
{ id = 23544, chance = 3500, maxCount = 1 }, -- collar of red plasma
diff --git a/data-otservbr-global/monster/quests/rotten_blood/sopping_corpus.lua b/data-otservbr-global/monster/quests/rotten_blood/sopping_corpus.lua
index 24d7a5f87b1..16cc74d32a0 100644
--- a/data-otservbr-global/monster/quests/rotten_blood/sopping_corpus.lua
+++ b/data-otservbr-global/monster/quests/rotten_blood/sopping_corpus.lua
@@ -77,10 +77,10 @@ monster.voices = {
}
monster.loot = {
- { name = "crystal coin", chance = 6961, maxCount = 1 },
- { name = "ultimate mana potion", chance = 10285, maxCount = 2 },
- { name = "crimson sword", chance = 10090, maxCount = 3 },
- { name = "ultimate health potion", chance = 10285, maxCount = 2 },
+ { name = "crystal coin", chance = 42860 },
+ { name = "ultimate mana potion", chance = 42860, minCount = 2, maxCount = 3 },
+ { id = 7385, chance = 14290 }, -- crimson sword
+ { name = "ultimate health potion", chance = 14290, maxCount = 2 },
{ name = "organic acid", chance = 7678, maxCount = 1 },
{ name = "rotten roots", chance = 13133, maxCount = 1 },
{ name = "emerald bangle", chance = 8558, maxCount = 1 },
@@ -89,7 +89,7 @@ monster.loot = {
{ name = "blue gem", chance = 9808, maxCount = 1 },
{ name = "relic sword", chance = 6964, maxCount = 1 },
{ name = "skullcracker armor", chance = 7270, maxCount = 1 },
- { name = "ring of green plasma", chance = 3073, maxCount = 1 },
+ { id = 23531, chance = 3073, maxCount = 1 }, -- ring of green plasma
}
monster.attacks = {
diff --git a/data-otservbr-global/npc/yasir.lua b/data-otservbr-global/npc/yasir.lua
index 9c5cf3dbf69..a197cae33d5 100644
--- a/data-otservbr-global/npc/yasir.lua
+++ b/data-otservbr-global/npc/yasir.lua
@@ -175,6 +175,7 @@ npcConfig.shop = {
{ itemName = "dandelion seeds", clientId = 25695, sell = 200 },
{ itemName = "dangerous proto matter", clientId = 23515, sell = 300 },
{ itemName = "dark bell", clientId = 32596, sell = 310000 },
+ { itemName = "darklight matter", clientId = 43851, sell = 5500 },
{ itemName = "dark obsidian splinter", clientId = 43850, sell = 4400 },
{ itemName = "dark rosary", clientId = 10303, sell = 48 },
{ itemName = "darklight core", clientId = 43853, sell = 4100 },
diff --git a/data/items/items.xml b/data/items/items.xml
index 17bc96b1bad..379095e5d9c 100644
--- a/data/items/items.xml
+++ b/data/items/items.xml
@@ -75475,8 +75475,16 @@ Granted by TibiaGoals.com"/>
-
-
+ -
+
+
+
+
+ -
+
+
+
+
-
@@ -75487,7 +75495,11 @@ Granted by TibiaGoals.com"/>
-
+ -
+
+
+
+
-
From 142aafbfdf7fe29cb935149b875f5009b527d214 Mon Sep 17 00:00:00 2001
From: murilo09 <78226931+murilo09@users.noreply.github.com>
Date: Thu, 2 Jan 2025 13:30:17 -0300
Subject: [PATCH 10/32] fix: balancing 13.40 (#3206)
---
data-otservbr-global/npc/alaistar.lua | 12 ++--
data-otservbr-global/npc/alexander.lua | 16 ++---
data-otservbr-global/npc/archery.lua | 2 +-
data-otservbr-global/npc/asima.lua | 30 ++++----
data-otservbr-global/npc/asnarus.lua | 18 ++---
data-otservbr-global/npc/baltim.lua | 30 ++++----
data-otservbr-global/npc/brengus.lua | 32 ++++-----
data-otservbr-global/npc/cedrik.lua | 32 ++++-----
data-otservbr-global/npc/chartan.lua | 18 ++---
data-otservbr-global/npc/chuckles.lua | 30 ++++----
data-otservbr-global/npc/dario.lua | 2 +-
data-otservbr-global/npc/digger.lua | 6 +-
data-otservbr-global/npc/edoch.lua | 2 +-
data-otservbr-global/npc/eliyas.lua | 2 +-
data-otservbr-global/npc/esrik.lua | 30 ++++----
data-otservbr-global/npc/faloriel.lua | 12 ++--
data-otservbr-global/npc/fenech.lua | 18 ++---
data-otservbr-global/npc/flint.lua | 30 ++++----
data-otservbr-global/npc/frans.lua | 18 ++---
data-otservbr-global/npc/frederik.lua | 30 ++++----
data-otservbr-global/npc/gamel.lua | 30 ++++----
data-otservbr-global/npc/ghorza.lua | 22 +++---
data-otservbr-global/npc/gnomegica.lua | 24 +++----
data-otservbr-global/npc/habdel.lua | 30 ++++----
data-otservbr-global/npc/hardek.lua | 30 ++++----
data-otservbr-global/npc/haroun.lua | 2 +-
data-otservbr-global/npc/hireling.lua | 24 +++----
data-otservbr-global/npc/isika.lua | 2 +-
data-otservbr-global/npc/khanna.lua | 22 +++---
data-otservbr-global/npc/maun.lua | 18 ++---
data-otservbr-global/npc/mehkesh.lua | 12 ++--
data-otservbr-global/npc/memech.lua | 30 ++++----
data-otservbr-global/npc/mordecai.lua | 22 +++---
data-otservbr-global/npc/morpel.lua | 30 ++++----
data-otservbr-global/npc/mugruu.lua | 2 +-
data-otservbr-global/npc/nelly.lua | 30 ++++----
data-otservbr-global/npc/nipuna.lua | 34 ++++-----
data-otservbr-global/npc/perod.lua | 2 +-
data-otservbr-global/npc/pompan.lua | 2 +-
data-otservbr-global/npc/rabaz.lua | 30 ++++----
data-otservbr-global/npc/rachel.lua | 24 +++----
data-otservbr-global/npc/robert.lua | 32 ++++-----
.../npc/rock_in_a_hard_place.lua | 24 +++----
data-otservbr-global/npc/romella.lua | 30 ++++----
data-otservbr-global/npc/romir.lua | 30 ++++----
data-otservbr-global/npc/rowenna.lua | 30 ++++----
.../npc/runtel_blackspark.lua | 22 +++---
data-otservbr-global/npc/sam.lua | 30 ++++----
data-otservbr-global/npc/sandra.lua | 12 ++--
data-otservbr-global/npc/shanar.lua | 30 ++++----
data-otservbr-global/npc/shiriel.lua | 24 +++----
data-otservbr-global/npc/siflind.lua | 30 ++++----
data-otservbr-global/npc/sigurd.lua | 24 +++----
data-otservbr-global/npc/silas.lua | 2 +-
data-otservbr-global/npc/sundara.lua | 34 ++++-----
data-otservbr-global/npc/taegen.lua | 2 +-
data-otservbr-global/npc/talila.lua | 22 +++---
data-otservbr-global/npc/tandros.lua | 30 ++++----
data-otservbr-global/npc/tarun.lua | 12 ++--
data-otservbr-global/npc/topsy.lua | 24 +++----
data-otservbr-global/npc/turvy.lua | 30 ++++----
data-otservbr-global/npc/ulrik.lua | 30 ++++----
data-otservbr-global/npc/uzgod.lua | 30 ++++----
data-otservbr-global/npc/valindara.lua | 22 +++---
data-otservbr-global/npc/vincent.lua | 2 +-
data-otservbr-global/npc/willard.lua | 32 ++++-----
data-otservbr-global/npc/xodet.lua | 24 +++----
data-otservbr-global/npc/yaman.lua | 2 +-
data-otservbr-global/npc/zuma_magehide.lua | 4 +-
data/XML/imbuements.xml | 4 +-
data/scripts/lib/shops.lua | 70 +++++++++----------
data/scripts/spells/attack/annihilation.lua | 2 +-
.../spells/attack/ultimate_ice_strike.lua | 2 +-
.../spells/attack/ultimate_terra_strike.lua | 2 +-
src/game/game.cpp | 7 +-
75 files changed, 750 insertions(+), 749 deletions(-)
diff --git a/data-otservbr-global/npc/alaistar.lua b/data-otservbr-global/npc/alaistar.lua
index c82e478e89b..61d52af9dfc 100644
--- a/data-otservbr-global/npc/alaistar.lua
+++ b/data-otservbr-global/npc/alaistar.lua
@@ -29,16 +29,16 @@ local itemsTable = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
["creature products"] = {
diff --git a/data-otservbr-global/npc/alexander.lua b/data-otservbr-global/npc/alexander.lua
index bd6a0329652..a4735708ce2 100644
--- a/data-otservbr-global/npc/alexander.lua
+++ b/data-otservbr-global/npc/alexander.lua
@@ -31,12 +31,12 @@ npcConfig.voices = {
local itemsTable = {
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["creature products"] = {
{ itemName = "crystal ball", clientId = 3076, buy = 530, sell = 190 },
@@ -64,8 +64,8 @@ local itemsTable = {
{ itemName = "paralyze rune", clientId = 3165, buy = 700 },
{ itemName = "poison bomb rune", clientId = 3173, buy = 85 },
{ itemName = "soulfire rune", clientId = 3195, buy = 46 },
- { itemName = "stone shower rune", clientId = 3175, buy = 37 },
- { itemName = "thunderstorm rune", clientId = 3202, buy = 47 },
+ { itemName = "stone shower rune", clientId = 3175, buy = 41 },
+ { itemName = "thunderstorm rune", clientId = 3202, buy = 52 },
{ itemName = "wild growth rune", clientId = 3156, buy = 160 },
},
["wands"] = {
diff --git a/data-otservbr-global/npc/archery.lua b/data-otservbr-global/npc/archery.lua
index fc5d37524f4..9fdbfad1576 100644
--- a/data-otservbr-global/npc/archery.lua
+++ b/data-otservbr-global/npc/archery.lua
@@ -76,7 +76,7 @@ npcConfig.shop = {
{ itemName = "burst arrow", clientId = 3449, buy = 15 },
{ itemName = "crossbow", clientId = 3349, buy = 500 },
{ itemName = "crystalline arrow", clientId = 15793, buy = 20 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
{ itemName = "earth arrow", clientId = 774, buy = 5 },
{ itemName = "envenomed arrow", clientId = 16143, buy = 12 },
diff --git a/data-otservbr-global/npc/asima.lua b/data-otservbr-global/npc/asima.lua
index 2d209e95d4c..94d86631420 100644
--- a/data-otservbr-global/npc/asima.lua
+++ b/data-otservbr-global/npc/asima.lua
@@ -25,28 +25,28 @@ npcConfig.flags = {
local itemsTable = {
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["potions"] = {
{ itemName = "empty potion flask", clientId = 283, sell = 5 },
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
["others"] = {
@@ -56,7 +56,7 @@ local itemsTable = {
{ itemName = "spellbook", clientId = 3059, buy = 150 },
},
["runes"] = {
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -68,13 +68,13 @@ local itemsTable = {
{ itemName = "fire bomb rune", clientId = 3192, buy = 147 },
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "intense healing rune", clientId = 3152, buy = 95 },
{ itemName = "light magic missile rune", clientId = 3174, buy = 4 },
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
diff --git a/data-otservbr-global/npc/asnarus.lua b/data-otservbr-global/npc/asnarus.lua
index 3992d513e9b..aae08a3655e 100644
--- a/data-otservbr-global/npc/asnarus.lua
+++ b/data-otservbr-global/npc/asnarus.lua
@@ -64,7 +64,7 @@ npcConfig.shop = {
{ itemName = "crystalline arrow", clientId = 15793, buy = 20 },
{ itemName = "dead weight", clientId = 20202, sell = 450 },
{ itemName = "desintegrate rune", clientId = 3197, buy = 26 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
{ itemName = "earth arrow", clientId = 774, buy = 5 },
{ itemName = "empty potion flask", clientId = 283, sell = 5 },
@@ -79,8 +79,8 @@ npcConfig.shop = {
{ itemName = "frazzle tongue", clientId = 20198, sell = 700 },
{ itemName = "goosebump leather", clientId = 20205, sell = 650 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "hemp rope", clientId = 20206, sell = 350 },
{ itemName = "holy missile rune", clientId = 3182, buy = 16 },
@@ -105,17 +105,17 @@ npcConfig.shop = {
{ itemName = "soulfire rune", clientId = 3195, buy = 46 },
{ itemName = "spear", clientId = 3277, buy = 9, sell = 3 },
{ itemName = "spectral bolt", clientId = 35902, buy = 70 },
- { itemName = "stone shower rune", clientId = 3175, buy = 37 },
+ { itemName = "stone shower rune", clientId = 3175, buy = 41 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "tarsal arrow", clientId = 14251, buy = 6 },
{ itemName = "throwing star", clientId = 3287, buy = 42 },
- { itemName = "thunderstorm rune", clientId = 3202, buy = 47 },
+ { itemName = "thunderstorm rune", clientId = 3202, buy = 52 },
{ itemName = "trapped bad dream monster", clientId = 20203, sell = 900 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
{ itemName = "vortex bolt", clientId = 14252, buy = 6 },
{ itemName = "wild growth rune", clientId = 3156, buy = 160 },
diff --git a/data-otservbr-global/npc/baltim.lua b/data-otservbr-global/npc/baltim.lua
index d0b472f6bd2..41a134c07df 100644
--- a/data-otservbr-global/npc/baltim.lua
+++ b/data-otservbr-global/npc/baltim.lua
@@ -75,17 +75,17 @@ npcConfig.shop = {
{ itemName = "dagger", clientId = 3267, buy = 5, sell = 2 },
{ itemName = "double axe", clientId = 3275, sell = 260 },
{ itemName = "doublet", clientId = 3379, buy = 16, sell = 3 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
{ itemName = "dwarven shield", clientId = 3425, buy = 500, sell = 100 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "frost giant pelt", clientId = 9658, sell = 160 },
{ itemName = "halberd", clientId = 3269, sell = 400 },
@@ -94,11 +94,11 @@ npcConfig.shop = {
{ itemName = "iron helmet", clientId = 3353, buy = 390, sell = 150 },
{ itemName = "jacket", clientId = 3561, buy = 12, sell = 1 },
{ itemName = "katana", clientId = 3300, sell = 35 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
{ itemName = "leather armor", clientId = 3361, buy = 35, sell = 12 },
{ itemName = "leather boots", clientId = 3552, buy = 10, sell = 2 },
{ itemName = "leather helmet", clientId = 3355, buy = 12, sell = 4 },
diff --git a/data-otservbr-global/npc/brengus.lua b/data-otservbr-global/npc/brengus.lua
index 75657c858d6..32c89b4923d 100644
--- a/data-otservbr-global/npc/brengus.lua
+++ b/data-otservbr-global/npc/brengus.lua
@@ -81,23 +81,23 @@ npcConfig.shop = {
{ itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 },
{ itemName = "crystalline arrow", clientId = 15793, buy = 20 },
{ itemName = "dagger", clientId = 3267, buy = 5, sell = 2 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "double axe", clientId = 3275, sell = 260 },
{ itemName = "doublet", clientId = 3379, buy = 16, sell = 3 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
{ itemName = "dwarven shield", clientId = 3425, buy = 500, sell = 100 },
{ itemName = "earth arrow", clientId = 774, buy = 5 },
{ itemName = "envenomed arrow", clientId = 16143, buy = 12 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
{ itemName = "feather headdress", clientId = 3406, sell = 850 },
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "flaming arrow", clientId = 763, buy = 5 },
@@ -111,11 +111,11 @@ npcConfig.shop = {
{ itemName = "jacket", clientId = 3561, buy = 12, sell = 1 },
{ itemName = "katana", clientId = 3300, sell = 35 },
{ itemName = "kongra's shoulderpad", clientId = 11471, sell = 100 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
{ itemName = "leather armor", clientId = 3361, buy = 35, sell = 12 },
{ itemName = "leather boots", clientId = 3552, buy = 10, sell = 2 },
{ itemName = "leather helmet", clientId = 3355, buy = 12, sell = 4 },
diff --git a/data-otservbr-global/npc/cedrik.lua b/data-otservbr-global/npc/cedrik.lua
index e54ee58c041..f34ce5b8247 100644
--- a/data-otservbr-global/npc/cedrik.lua
+++ b/data-otservbr-global/npc/cedrik.lua
@@ -178,23 +178,23 @@ npcConfig.shop = {
{ itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 },
{ itemName = "crystalline arrow", clientId = 15793, buy = 20 },
{ itemName = "dagger", clientId = 3267, buy = 5, sell = 2 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "double axe", clientId = 3275, sell = 260 },
{ itemName = "doublet", clientId = 3379, buy = 16, sell = 3 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
{ itemName = "dwarven shield", clientId = 3425, buy = 500, sell = 100 },
{ itemName = "earth arrow", clientId = 774, buy = 5 },
{ itemName = "envenomed arrow", clientId = 16143, buy = 12 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "flaming arrow", clientId = 763, buy = 5 },
{ itemName = "flash arrow", clientId = 761, buy = 5 },
@@ -204,11 +204,11 @@ npcConfig.shop = {
{ itemName = "iron helmet", clientId = 3353, buy = 390, sell = 150 },
{ itemName = "jacket", clientId = 3561, buy = 12, sell = 1 },
{ itemName = "katana", clientId = 3300, sell = 35 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
{ itemName = "leather armor", clientId = 3361, buy = 35, sell = 12 },
{ itemName = "leather boots", clientId = 3552, buy = 10, sell = 2 },
{ itemName = "leather helmet", clientId = 3355, buy = 12, sell = 4 },
diff --git a/data-otservbr-global/npc/chartan.lua b/data-otservbr-global/npc/chartan.lua
index 41be66800c9..d166de31295 100644
--- a/data-otservbr-global/npc/chartan.lua
+++ b/data-otservbr-global/npc/chartan.lua
@@ -110,7 +110,7 @@ npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
npcConfig.shop = {
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -127,10 +127,10 @@ npcConfig.shop = {
{ itemName = "fire bomb rune", clientId = 3192, buy = 147 },
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "ham", clientId = 3582, buy = 10 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
@@ -141,13 +141,13 @@ npcConfig.shop = {
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
}
-- On buy npc shop message
diff --git a/data-otservbr-global/npc/chuckles.lua b/data-otservbr-global/npc/chuckles.lua
index 5642304172f..34ef215d3fc 100644
--- a/data-otservbr-global/npc/chuckles.lua
+++ b/data-otservbr-global/npc/chuckles.lua
@@ -24,20 +24,20 @@ local itemsTable = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
["runes"] = {
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -49,14 +49,14 @@ local itemsTable = {
{ itemName = "fire bomb rune", clientId = 3192, buy = 147 },
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "intense healing rune", clientId = 3152, buy = 95 },
{ itemName = "light magic missile rune", clientId = 3174, buy = 4 },
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
["wands"] = {
@@ -78,12 +78,12 @@ local itemsTable = {
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["others"] = {
{ itemName = "spellwand", clientId = 651, sell = 299 },
diff --git a/data-otservbr-global/npc/dario.lua b/data-otservbr-global/npc/dario.lua
index c679fb6e28c..d4b3e431737 100644
--- a/data-otservbr-global/npc/dario.lua
+++ b/data-otservbr-global/npc/dario.lua
@@ -73,7 +73,7 @@ npcConfig.shop = {
{ itemName = "bow", clientId = 3350, buy = 400, sell = 100 },
{ itemName = "crossbow", clientId = 3349, buy = 500, sell = 120 },
{ itemName = "crystalline arrow", clientId = 15793, buy = 20 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
{ itemName = "earth arrow", clientId = 774, buy = 5 },
{ itemName = "envenomed arrow", clientId = 16143, buy = 12 },
diff --git a/data-otservbr-global/npc/digger.lua b/data-otservbr-global/npc/digger.lua
index 863595cbc32..3774cb81583 100644
--- a/data-otservbr-global/npc/digger.lua
+++ b/data-otservbr-global/npc/digger.lua
@@ -57,12 +57,12 @@ npcConfig.shop = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
{ itemName = "vial", clientId = 2874, sell = 5 },
}
diff --git a/data-otservbr-global/npc/edoch.lua b/data-otservbr-global/npc/edoch.lua
index 754652ba507..7b852370408 100644
--- a/data-otservbr-global/npc/edoch.lua
+++ b/data-otservbr-global/npc/edoch.lua
@@ -59,7 +59,7 @@ npcConfig.shop = {
{ itemName = "bow", clientId = 3350, buy = 400, sell = 100 },
{ itemName = "crossbow", clientId = 3349, buy = 500, sell = 120 },
{ itemName = "crystalline arrow", clientId = 15793, buy = 20 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
{ itemName = "earth arrow", clientId = 774, buy = 5 },
{ itemName = "envenomed arrow", clientId = 16143, buy = 12 },
diff --git a/data-otservbr-global/npc/eliyas.lua b/data-otservbr-global/npc/eliyas.lua
index 51ae0c46e9e..96f6fd585b0 100644
--- a/data-otservbr-global/npc/eliyas.lua
+++ b/data-otservbr-global/npc/eliyas.lua
@@ -79,7 +79,7 @@ npcConfig.shop = {
{ itemName = "bow", clientId = 3350, buy = 400, sell = 100 },
{ itemName = "crossbow", clientId = 3349, buy = 500, sell = 120 },
{ itemName = "crystalline arrow", clientId = 15793, buy = 20 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
{ itemName = "earth arrow", clientId = 774, buy = 5 },
{ itemName = "envenomed arrow", clientId = 16143, buy = 12 },
diff --git a/data-otservbr-global/npc/esrik.lua b/data-otservbr-global/npc/esrik.lua
index 5890ff5843c..b36bb284f87 100644
--- a/data-otservbr-global/npc/esrik.lua
+++ b/data-otservbr-global/npc/esrik.lua
@@ -114,21 +114,21 @@ npcConfig.shop = {
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "twin hooks", clientId = 10392, buy = 1100 },
{ itemName = "zaoan halberd", clientId = 10406, buy = 1200 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
-- 3 tomes
{ itemName = "lizard weapon rack kit", clientId = 10210, buy = 500, storageKey = tomes, storageValue = 3 },
-- 9 tomes
diff --git a/data-otservbr-global/npc/faloriel.lua b/data-otservbr-global/npc/faloriel.lua
index 7d259c72ea3..4f4b0fb8ba8 100644
--- a/data-otservbr-global/npc/faloriel.lua
+++ b/data-otservbr-global/npc/faloriel.lua
@@ -95,16 +95,16 @@ npcConfig.shop = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
}
-- On buy npc shop message
diff --git a/data-otservbr-global/npc/fenech.lua b/data-otservbr-global/npc/fenech.lua
index acc2ab55b45..0539cf6e718 100644
--- a/data-otservbr-global/npc/fenech.lua
+++ b/data-otservbr-global/npc/fenech.lua
@@ -49,7 +49,7 @@ local itemsTable = {
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
["runes"] = {
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -61,23 +61,23 @@ local itemsTable = {
{ itemName = "fire bomb rune", clientId = 3192, buy = 147 },
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "intense healing rune", clientId = 3152, buy = 95 },
{ itemName = "light magic missile rune", clientId = 3174, buy = 4 },
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["others"] = {
{ itemName = "spellwand", clientId = 651, sell = 299 },
diff --git a/data-otservbr-global/npc/flint.lua b/data-otservbr-global/npc/flint.lua
index 0b50a00df1f..e8c83e70279 100644
--- a/data-otservbr-global/npc/flint.lua
+++ b/data-otservbr-global/npc/flint.lua
@@ -77,18 +77,18 @@ npcConfig.shop = {
{ itemName = "dagger", clientId = 3267, buy = 5, sell = 2 },
{ itemName = "double axe", clientId = 3275, sell = 260 },
{ itemName = "doublet", clientId = 3379, buy = 16, sell = 3 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
{ itemName = "dwarven shield", clientId = 3425, buy = 500, sell = 100 },
{ itemName = "execowtioner axe", clientId = 21176, sell = 12000 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "gearwheel chain", clientId = 21170, sell = 5000 },
{ itemName = "glooth amulet", clientId = 21183, sell = 2000 },
@@ -104,11 +104,11 @@ npcConfig.shop = {
{ itemName = "iron helmet", clientId = 3353, buy = 390, sell = 150 },
{ itemName = "jacket", clientId = 3561, buy = 12, sell = 1 },
{ itemName = "katana", clientId = 3300, sell = 35 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
{ itemName = "leather armor", clientId = 3361, buy = 35, sell = 12 },
{ itemName = "leather boots", clientId = 3552, buy = 10, sell = 2 },
{ itemName = "leather helmet", clientId = 3355, buy = 12, sell = 4 },
diff --git a/data-otservbr-global/npc/frans.lua b/data-otservbr-global/npc/frans.lua
index ad5b30c4fd5..0c9806221b5 100644
--- a/data-otservbr-global/npc/frans.lua
+++ b/data-otservbr-global/npc/frans.lua
@@ -36,7 +36,7 @@ local itemsTable = {
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
["runes"] = {
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -48,23 +48,23 @@ local itemsTable = {
{ itemName = "fire bomb rune", clientId = 3192, buy = 147 },
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "intense healing rune", clientId = 3152, buy = 95 },
{ itemName = "light magic missile rune", clientId = 3174, buy = 4 },
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["others"] = {
{ itemName = "spellwand", clientId = 651, sell = 299 },
diff --git a/data-otservbr-global/npc/frederik.lua b/data-otservbr-global/npc/frederik.lua
index d7850598c56..991926a12b6 100644
--- a/data-otservbr-global/npc/frederik.lua
+++ b/data-otservbr-global/npc/frederik.lua
@@ -43,7 +43,7 @@ local itemsTable = {
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
["runes"] = {
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -55,14 +55,14 @@ local itemsTable = {
{ itemName = "fire bomb rune", clientId = 3192, buy = 147 },
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "intense healing rune", clientId = 3152, buy = 95 },
{ itemName = "light magic missile rune", clientId = 3174, buy = 4 },
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
["potions"] = {
@@ -70,25 +70,25 @@ local itemsTable = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["others"] = {
{ itemName = "spellwand", clientId = 651, sell = 299 },
diff --git a/data-otservbr-global/npc/gamel.lua b/data-otservbr-global/npc/gamel.lua
index f6b50617a3b..5ef43633faa 100644
--- a/data-otservbr-global/npc/gamel.lua
+++ b/data-otservbr-global/npc/gamel.lua
@@ -119,27 +119,27 @@ npcConfig.shop = {
{ itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 },
{ itemName = "dagger", clientId = 3267, buy = 5, sell = 2 },
{ itemName = "double axe", clientId = 3275, sell = 260 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "halberd", clientId = 3269, sell = 400 },
{ itemName = "hand axe", clientId = 3268, buy = 8, sell = 4 },
{ itemName = "hatchet", clientId = 3276, sell = 25 },
{ itemName = "iron helmet", clientId = 3353, buy = 390, sell = 150 },
{ itemName = "katana", clientId = 3300, sell = 35 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
{ itemName = "leather helmet", clientId = 3355, buy = 12, sell = 4 },
{ itemName = "legion helmet", clientId = 3374, sell = 22 },
{ itemName = "longsword", clientId = 3285, buy = 160, sell = 51 },
diff --git a/data-otservbr-global/npc/ghorza.lua b/data-otservbr-global/npc/ghorza.lua
index 43db1d3238f..f144be24646 100644
--- a/data-otservbr-global/npc/ghorza.lua
+++ b/data-otservbr-global/npc/ghorza.lua
@@ -37,21 +37,21 @@ local itemsTable = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
["runes"] = {
{ itemName = "animate dead rune", clientId = 3203, buy = 375 },
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -66,7 +66,7 @@ local itemsTable = {
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
{ itemName = "fireball rune", clientId = 3189, buy = 30 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "icicle rune", clientId = 3158, buy = 30 },
{ itemName = "holy missile rune", clientId = 3182, buy = 16 },
@@ -77,9 +77,9 @@ local itemsTable = {
{ itemName = "poison bomb rune", clientId = 3173, buy = 85 },
{ itemName = "soulfire rune", clientId = 3195, buy = 46 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "stone shower rune", clientId = 3175, buy = 37 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
- { itemName = "thunderstorm rune", clientId = 3202, buy = 47 },
+ { itemName = "stone shower rune", clientId = 3175, buy = 41 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
+ { itemName = "thunderstorm rune", clientId = 3202, buy = 52 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
{ itemName = "wild growth rune", clientId = 3156, buy = 160 },
},
diff --git a/data-otservbr-global/npc/gnomegica.lua b/data-otservbr-global/npc/gnomegica.lua
index 5b776260bd5..84086212acd 100644
--- a/data-otservbr-global/npc/gnomegica.lua
+++ b/data-otservbr-global/npc/gnomegica.lua
@@ -36,17 +36,17 @@ local itemsTable = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
["runes"] = {
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -58,14 +58,14 @@ local itemsTable = {
{ itemName = "fire bomb rune", clientId = 3192, buy = 147 },
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "intense healing rune", clientId = 3152, buy = 95 },
{ itemName = "light magic missile rune", clientId = 3174, buy = 4 },
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
["wands"] = {
@@ -79,12 +79,12 @@ local itemsTable = {
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["others"] = {
{ itemName = "spellwand", clientId = 651, sell = 299 },
diff --git a/data-otservbr-global/npc/habdel.lua b/data-otservbr-global/npc/habdel.lua
index 1a060e95fa6..278c52209fe 100644
--- a/data-otservbr-global/npc/habdel.lua
+++ b/data-otservbr-global/npc/habdel.lua
@@ -122,26 +122,26 @@ npcConfig.shop = {
{ itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 },
{ itemName = "dagger", clientId = 3267, buy = 5, sell = 2 },
{ itemName = "double axe", clientId = 3275, sell = 260 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "halberd", clientId = 3269, sell = 400 },
{ itemName = "hand axe", clientId = 3268, buy = 8, sell = 4 },
{ itemName = "hatchet", clientId = 3276, sell = 25 },
{ itemName = "katana", clientId = 3300, sell = 35 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
{ itemName = "longsword", clientId = 3285, buy = 160, sell = 51 },
{ itemName = "mace", clientId = 3286, buy = 90, sell = 30 },
{ itemName = "morning star", clientId = 3282, buy = 430, sell = 100 },
diff --git a/data-otservbr-global/npc/hardek.lua b/data-otservbr-global/npc/hardek.lua
index 27f5ddc23a0..45277fb4596 100644
--- a/data-otservbr-global/npc/hardek.lua
+++ b/data-otservbr-global/npc/hardek.lua
@@ -86,17 +86,17 @@ npcConfig.shop = {
{ itemName = "dagger", clientId = 3267, buy = 5, sell = 2 },
{ itemName = "double axe", clientId = 3275, sell = 260 },
{ itemName = "doublet", clientId = 3379, buy = 16, sell = 3 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
{ itemName = "dwarven shield", clientId = 3425, buy = 500, sell = 100 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "halberd", clientId = 3269, sell = 400 },
{ itemName = "hand axe", clientId = 3268, buy = 8, sell = 4 },
@@ -104,11 +104,11 @@ npcConfig.shop = {
{ itemName = "iron helmet", clientId = 3353, buy = 390, sell = 150 },
{ itemName = "jacket", clientId = 3561, buy = 12, sell = 1 },
{ itemName = "katana", clientId = 3300, sell = 35 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
{ itemName = "leather armor", clientId = 3361, buy = 35, sell = 12 },
{ itemName = "leather boots", clientId = 3552, buy = 10, sell = 2 },
{ itemName = "leather helmet", clientId = 3355, buy = 12, sell = 4 },
diff --git a/data-otservbr-global/npc/haroun.lua b/data-otservbr-global/npc/haroun.lua
index aa7560af9dd..f3407b7043b 100644
--- a/data-otservbr-global/npc/haroun.lua
+++ b/data-otservbr-global/npc/haroun.lua
@@ -157,7 +157,7 @@ npcConfig.shop = {
{ itemName = "orb", clientId = 3060, sell = 750 },
{ itemName = "power ring", clientId = 3050, buy = 100, sell = 50 },
{ itemName = "stealth ring", clientId = 3049, buy = 5000, sell = 200 },
- { itemName = "stone skin amulet", clientId = 3081, buy = 5000, sell = 500, count = 5 },
+ { itemName = "stone skin amulet", clientId = 3081, buy = 25000, sell = 500, count = 5 },
{ itemName = "sword ring", clientId = 3091, buy = 500, sell = 100 },
{ itemName = "wand of cosmic energy", clientId = 3073, sell = 2000 },
{ itemName = "wand of decay", clientId = 3072, sell = 1000 },
diff --git a/data-otservbr-global/npc/hireling.lua b/data-otservbr-global/npc/hireling.lua
index bfc9000ca0f..234c28012f1 100644
--- a/data-otservbr-global/npc/hireling.lua
+++ b/data-otservbr-global/npc/hireling.lua
@@ -233,7 +233,7 @@ function createHirelingType(HirelingName)
{ itemName = "crossbow", clientId = 3349, buy = 500, sell = 120 },
{ itemName = "crystalline arrow", clientId = 15793, buy = 450 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "earth arrow", clientId = 774, buy = 5 },
{ itemName = "envenomed arrow", clientId = 16143, buy = 12 },
{ itemName = "flaming arrow", clientId = 763, buy = 5 },
@@ -276,16 +276,16 @@ function createHirelingType(HirelingName)
},
["potions"] = {
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "empty potion flask", clientId = 283, sell = 5 },
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
@@ -293,7 +293,7 @@ function createHirelingType(HirelingName)
},
["runes"] = {
{ itemName = "animate dead rune", clientId = 3203, buy = 375 },
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -308,7 +308,7 @@ function createHirelingType(HirelingName)
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
{ itemName = "fireball rune", clientId = 3189, buy = 30 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "holy missile rune", clientId = 3182, buy = 16 },
{ itemName = "icicle rune", clientId = 3158, buy = 30 },
@@ -321,9 +321,9 @@ function createHirelingType(HirelingName)
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "soulfire rune", clientId = 3195, buy = 46 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "stone shower rune", clientId = 3175, buy = 37 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
- { itemName = "thunderstorm rune", clientId = 3202, buy = 47 },
+ { itemName = "stone shower rune", clientId = 3175, buy = 41 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
+ { itemName = "thunderstorm rune", clientId = 3202, buy = 52 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
{ itemName = "wild growth rune", clientId = 3156, buy = 160 },
},
diff --git a/data-otservbr-global/npc/isika.lua b/data-otservbr-global/npc/isika.lua
index 45f0bc43ed5..41ac51090fd 100644
--- a/data-otservbr-global/npc/isika.lua
+++ b/data-otservbr-global/npc/isika.lua
@@ -56,7 +56,7 @@ npcConfig.shop = {
{ itemName = "crossbow", clientId = 3349, buy = 500, sell = 120 },
{ itemName = "crystalline arrow", clientId = 15793, buy = 20 },
{ itemName = "dragon tapestry", clientId = 23723, buy = 80 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
{ itemName = "earth arrow", clientId = 774, buy = 5 },
{ itemName = "envenomed arrow", clientId = 16143, buy = 12 },
diff --git a/data-otservbr-global/npc/khanna.lua b/data-otservbr-global/npc/khanna.lua
index 497a8f87c64..8558b258f62 100644
--- a/data-otservbr-global/npc/khanna.lua
+++ b/data-otservbr-global/npc/khanna.lua
@@ -33,7 +33,7 @@ npcConfig.voices = {
local itemsTable = {
["runes"] = {
{ itemName = "animate dead rune", clientId = 3203, buy = 375 },
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 20 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -48,7 +48,7 @@ local itemsTable = {
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
{ itemName = "fireball rune", clientId = 3189, buy = 30 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "holy missile rune", clientId = 3182, buy = 16 },
{ itemName = "icicle rune", clientId = 3158, buy = 30 },
@@ -60,9 +60,9 @@ local itemsTable = {
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "stone shower rune", clientId = 3175, buy = 37 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
- { itemName = "thunderstorm rune", clientId = 3202, buy = 47 },
+ { itemName = "stone shower rune", clientId = 3175, buy = 41 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
+ { itemName = "thunderstorm rune", clientId = 3202, buy = 52 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
{ itemName = "wild growth rune", clientId = 3156, buy = 160 },
},
@@ -85,12 +85,12 @@ local itemsTable = {
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["creature products"] = {
{ itemName = "bashmu fang", clientId = 36820, sell = 600 },
diff --git a/data-otservbr-global/npc/maun.lua b/data-otservbr-global/npc/maun.lua
index 1bce8918562..f12c005ae1c 100644
--- a/data-otservbr-global/npc/maun.lua
+++ b/data-otservbr-global/npc/maun.lua
@@ -69,7 +69,7 @@ npcConfig.shop = {
{ itemName = "crusher", clientId = 46627, buy = 500 },
{ itemName = "crystalline arrow", clientId = 15793, buy = 20 },
{ itemName = "desintegrate rune", clientId = 3197, buy = 26 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
{ itemName = "earth arrow", clientId = 774, buy = 5 },
{ itemName = "empty potion flask", clientId = 283, sell = 5 },
@@ -82,8 +82,8 @@ npcConfig.shop = {
{ itemName = "flaming arrow", clientId = 763, buy = 5 },
{ itemName = "flash arrow", clientId = 761, buy = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "hand auger", clientId = 31334, buy = 25 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "holy missile rune", clientId = 3182, buy = 16 },
@@ -111,17 +111,17 @@ npcConfig.shop = {
{ itemName = "soulfire rune", clientId = 3195, buy = 46 },
{ itemName = "spear", clientId = 3277, buy = 9, sell = 3 },
{ itemName = "spectral bolt", clientId = 35902, buy = 70 },
- { itemName = "stone shower rune", clientId = 3175, buy = 37 },
+ { itemName = "stone shower rune", clientId = 3175, buy = 41 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "tarsal arrow", clientId = 14251, buy = 6 },
{ itemName = "throwing star", clientId = 3287, buy = 42 },
- { itemName = "thunderstorm rune", clientId = 3202, buy = 47 },
+ { itemName = "thunderstorm rune", clientId = 3202, buy = 52 },
{ itemName = "torch", clientId = 2920, buy = 2 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
{ itemName = "vortex bolt", clientId = 14252, buy = 6 },
{ itemName = "watch", clientId = 2906, buy = 20, sell = 6 },
diff --git a/data-otservbr-global/npc/mehkesh.lua b/data-otservbr-global/npc/mehkesh.lua
index 509b4cd8080..e81b05823ca 100644
--- a/data-otservbr-global/npc/mehkesh.lua
+++ b/data-otservbr-global/npc/mehkesh.lua
@@ -67,17 +67,17 @@ npcConfig.shop = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "jug of embalming fluid", clientId = 8761, buy = 200 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
}
-- On buy npc shop message
diff --git a/data-otservbr-global/npc/memech.lua b/data-otservbr-global/npc/memech.lua
index f7b3f9f9498..7b7336fa564 100644
--- a/data-otservbr-global/npc/memech.lua
+++ b/data-otservbr-global/npc/memech.lua
@@ -83,17 +83,17 @@ npcConfig.shop = {
{ itemName = "dagger", clientId = 3267, buy = 5, sell = 2 },
{ itemName = "double axe", clientId = 3275, sell = 260 },
{ itemName = "doublet", clientId = 3379, buy = 16, sell = 3 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
{ itemName = "dwarven shield", clientId = 3425, buy = 500, sell = 100 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "halberd", clientId = 3269, sell = 400 },
{ itemName = "hand axe", clientId = 3268, buy = 8, sell = 4 },
@@ -101,11 +101,11 @@ npcConfig.shop = {
{ itemName = "iron helmet", clientId = 3353, buy = 390, sell = 150 },
{ itemName = "jacket", clientId = 3561, buy = 12, sell = 1 },
{ itemName = "katana", clientId = 3300, sell = 35 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
{ itemName = "leather armor", clientId = 3361, buy = 35, sell = 12 },
{ itemName = "leather boots", clientId = 3552, buy = 10, sell = 2 },
{ itemName = "leather helmet", clientId = 3355, buy = 12, sell = 4 },
diff --git a/data-otservbr-global/npc/mordecai.lua b/data-otservbr-global/npc/mordecai.lua
index 3b2ec4e60e8..281b5f63c5b 100644
--- a/data-otservbr-global/npc/mordecai.lua
+++ b/data-otservbr-global/npc/mordecai.lua
@@ -37,7 +37,7 @@ npcConfig.voices = {
local itemsTable = {
["runes"] = {
{ itemName = "animate dead rune", clientId = 3203, buy = 375 },
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -52,7 +52,7 @@ local itemsTable = {
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
{ itemName = "fireball rune", clientId = 3189, buy = 30 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "holy missile rune", clientId = 3182, buy = 16 },
{ itemName = "icicle rune", clientId = 3158, buy = 30 },
@@ -64,9 +64,9 @@ local itemsTable = {
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "stone shower rune", clientId = 3175, buy = 37 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
- { itemName = "thunderstorm rune", clientId = 3202, buy = 47 },
+ { itemName = "stone shower rune", clientId = 3175, buy = 41 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
+ { itemName = "thunderstorm rune", clientId = 3202, buy = 52 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
{ itemName = "wild growth rune", clientId = 3156, buy = 160 },
},
@@ -89,12 +89,12 @@ local itemsTable = {
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["others"] = {
{ itemName = "spellwand", clientId = 651, sell = 299 },
diff --git a/data-otservbr-global/npc/morpel.lua b/data-otservbr-global/npc/morpel.lua
index 3f58c58ae93..15e8c6a317a 100644
--- a/data-otservbr-global/npc/morpel.lua
+++ b/data-otservbr-global/npc/morpel.lua
@@ -77,17 +77,17 @@ npcConfig.shop = {
{ itemName = "dagger", clientId = 3267, buy = 5, sell = 2 },
{ itemName = "double axe", clientId = 3275, sell = 260 },
{ itemName = "doublet", clientId = 3379, buy = 16, sell = 3 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
{ itemName = "dwarven shield", clientId = 3425, buy = 500, sell = 100 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "halberd", clientId = 3269, sell = 400 },
{ itemName = "hand axe", clientId = 3268, buy = 8, sell = 4 },
@@ -95,11 +95,11 @@ npcConfig.shop = {
{ itemName = "iron helmet", clientId = 3353, buy = 390, sell = 150 },
{ itemName = "jacket", clientId = 3561, buy = 12, sell = 1 },
{ itemName = "katana", clientId = 3300, sell = 35 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
{ itemName = "leather armor", clientId = 3361, buy = 35, sell = 12 },
{ itemName = "leather boots", clientId = 3552, buy = 10, sell = 2 },
{ itemName = "leather helmet", clientId = 3355, buy = 12, sell = 4 },
diff --git a/data-otservbr-global/npc/mugruu.lua b/data-otservbr-global/npc/mugruu.lua
index 7bf2e2efd08..4cccf2b3c78 100644
--- a/data-otservbr-global/npc/mugruu.lua
+++ b/data-otservbr-global/npc/mugruu.lua
@@ -50,7 +50,7 @@ npcConfig.shop = {
{ itemName = "bow", clientId = 3350, buy = 400, sell = 100 },
{ itemName = "crossbow", clientId = 3349, buy = 500, sell = 120 },
{ itemName = "crystalline arrow", clientId = 15793, buy = 20 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
{ itemName = "earth arrow", clientId = 774, buy = 5 },
{ itemName = "envenomed arrow", clientId = 16143, buy = 12 },
diff --git a/data-otservbr-global/npc/nelly.lua b/data-otservbr-global/npc/nelly.lua
index 8a3cb6be150..6062259444b 100644
--- a/data-otservbr-global/npc/nelly.lua
+++ b/data-otservbr-global/npc/nelly.lua
@@ -36,20 +36,20 @@ local itemsTable = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
["runes"] = {
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -61,14 +61,14 @@ local itemsTable = {
{ itemName = "fire bomb rune", clientId = 3192, buy = 147 },
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "intense healing rune", clientId = 3152, buy = 95 },
{ itemName = "light magic missile rune", clientId = 3174, buy = 4 },
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
["wands"] = {
@@ -95,12 +95,12 @@ local itemsTable = {
{ itemName = "parcel", clientId = 3503, buy = 15 },
},
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["others"] = {
{ itemName = "spellwand", clientId = 651, sell = 299 },
diff --git a/data-otservbr-global/npc/nipuna.lua b/data-otservbr-global/npc/nipuna.lua
index d428b45c9c8..9be3f491f18 100644
--- a/data-otservbr-global/npc/nipuna.lua
+++ b/data-otservbr-global/npc/nipuna.lua
@@ -36,21 +36,21 @@ local itemsTable = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
["runes"] = {
{ itemName = "animate dead rune", clientId = 3203, buy = 375 },
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -65,7 +65,7 @@ local itemsTable = {
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
{ itemName = "fireball rune", clientId = 3189, buy = 30 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "holy missile rune", clientId = 3182, buy = 16 },
{ itemName = "icicle rune", clientId = 3158, buy = 30 },
@@ -77,9 +77,9 @@ local itemsTable = {
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "stone shower rune", clientId = 3175, buy = 37 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
- { itemName = "thunderstorm rune", clientId = 3202, buy = 47 },
+ { itemName = "stone shower rune", clientId = 3175, buy = 41 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
+ { itemName = "thunderstorm rune", clientId = 3202, buy = 52 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
{ itemName = "wild growth rune", clientId = 3156, buy = 160 },
},
@@ -102,12 +102,12 @@ local itemsTable = {
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["others"] = {
{ itemName = "spellwand", clientId = 651, sell = 299 },
diff --git a/data-otservbr-global/npc/perod.lua b/data-otservbr-global/npc/perod.lua
index 098117839de..b34d8ade96f 100644
--- a/data-otservbr-global/npc/perod.lua
+++ b/data-otservbr-global/npc/perod.lua
@@ -108,7 +108,7 @@ npcConfig.shop = {
{ itemName = "crusher", clientId = 46627, buy = 500 },
{ itemName = "crystalline arrow", clientId = 15793, buy = 20 },
{ itemName = "cup", clientId = 2884, buy = 2 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "document", clientId = 2818, buy = 12 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
{ itemName = "earth arrow", clientId = 774, buy = 5 },
diff --git a/data-otservbr-global/npc/pompan.lua b/data-otservbr-global/npc/pompan.lua
index 19fed2f7ed0..4c9f83698df 100644
--- a/data-otservbr-global/npc/pompan.lua
+++ b/data-otservbr-global/npc/pompan.lua
@@ -55,7 +55,7 @@ npcConfig.shop = {
{ name = "bow", clientId = 3350, buy = 400, sell = 100, storageKey = tomes, storageValue = 1 },
{ name = "crossbow", clientId = 3349, buy = 500, sell = 120, storageKey = tomes, storageValue = 1 },
{ name = "crystalline arrow", clientId = 15793, buy = 20, storageKey = tomes, storageValue = 1 },
- { name = "diamond arrow", clientId = 35901, buy = 100, storageKey = tomes, storageValue = 1 },
+ { name = "diamond arrow", clientId = 35901, buy = 130, storageKey = tomes, storageValue = 1 },
{ name = "dragon tapestry", clientId = 10347, buy = 80, storageKey = tomes, storageValue = 1 },
{ name = "drill bolt", clientId = 16142, buy = 12, storageKey = tomes, storageValue = 1 },
{ name = "earth arrow", clientId = 774, buy = 5, storageKey = tomes, storageValue = 1 },
diff --git a/data-otservbr-global/npc/rabaz.lua b/data-otservbr-global/npc/rabaz.lua
index 0de19876209..488f3810665 100644
--- a/data-otservbr-global/npc/rabaz.lua
+++ b/data-otservbr-global/npc/rabaz.lua
@@ -29,20 +29,20 @@ local itemsTable = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
["runes"] = {
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -54,14 +54,14 @@ local itemsTable = {
{ itemName = "fire bomb rune", clientId = 3192, buy = 147 },
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "intense healing rune", clientId = 3152, buy = 95 },
{ itemName = "light magic missile rune", clientId = 3174, buy = 4 },
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
["wands"] = {
@@ -83,12 +83,12 @@ local itemsTable = {
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["others"] = {
{ itemName = "spellwand", clientId = 651, sell = 299 },
diff --git a/data-otservbr-global/npc/rachel.lua b/data-otservbr-global/npc/rachel.lua
index a301abf2f93..0380e3973a9 100644
--- a/data-otservbr-global/npc/rachel.lua
+++ b/data-otservbr-global/npc/rachel.lua
@@ -29,17 +29,17 @@ local itemsTable = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
["runes"] = {
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -51,14 +51,14 @@ local itemsTable = {
{ itemName = "fire bomb rune", clientId = 3192, buy = 147 },
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "intense healing rune", clientId = 3152, buy = 95 },
{ itemName = "light magic missile rune", clientId = 3174, buy = 4 },
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
["wands"] = {
@@ -72,12 +72,12 @@ local itemsTable = {
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["others"] = {
{ itemName = "spellwand", clientId = 651, sell = 299 },
diff --git a/data-otservbr-global/npc/robert.lua b/data-otservbr-global/npc/robert.lua
index 794d1bcb122..d6612336f0b 100644
--- a/data-otservbr-global/npc/robert.lua
+++ b/data-otservbr-global/npc/robert.lua
@@ -80,23 +80,23 @@ npcConfig.shop = {
{ itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 },
{ itemName = "crystalline arrow", clientId = 15793, buy = 20 },
{ itemName = "dagger", clientId = 3267, buy = 5, sell = 2 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "double axe", clientId = 3275, sell = 260 },
{ itemName = "doublet", clientId = 3379, buy = 16, sell = 3 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
{ itemName = "dwarven shield", clientId = 3425, buy = 500, sell = 100 },
{ itemName = "earth arrow", clientId = 774, buy = 5 },
{ itemName = "envenomed arrow", clientId = 16143, buy = 12 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "flaming arrow", clientId = 763, buy = 5 },
{ itemName = "flash arrow", clientId = 761, buy = 5 },
@@ -106,11 +106,11 @@ npcConfig.shop = {
{ itemName = "iron helmet", clientId = 3353, buy = 390, sell = 150 },
{ itemName = "jacket", clientId = 3561, buy = 12, sell = 1 },
{ itemName = "katana", clientId = 3300, sell = 35 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
{ itemName = "leather armor", clientId = 3361, buy = 35, sell = 12 },
{ itemName = "leather boots", clientId = 3552, buy = 10, sell = 2 },
{ itemName = "leather helmet", clientId = 3355, buy = 12, sell = 4 },
diff --git a/data-otservbr-global/npc/rock_in_a_hard_place.lua b/data-otservbr-global/npc/rock_in_a_hard_place.lua
index 4d4c6b1ee10..534c9a8d511 100644
--- a/data-otservbr-global/npc/rock_in_a_hard_place.lua
+++ b/data-otservbr-global/npc/rock_in_a_hard_place.lua
@@ -48,7 +48,7 @@ end
local itemsTable = {
["magic stuff"] = {
{ itemName = "animate dead rune", clientId = 3203, buy = 375 },
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -67,10 +67,10 @@ local itemsTable = {
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
{ itemName = "fireball rune", clientId = 3189, buy = 30 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "holy missile rune", clientId = 3182, buy = 16 },
@@ -85,16 +85,16 @@ local itemsTable = {
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "soulfire rune", clientId = 3195, buy = 46 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "stone shower rune", clientId = 3175, buy = 37 },
+ { itemName = "stone shower rune", clientId = 3175, buy = 41 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
- { itemName = "thunderstorm rune", clientId = 3202, buy = 47 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
+ { itemName = "thunderstorm rune", clientId = 3202, buy = 52 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
{ itemName = "wild growth rune", clientId = 3156, buy = 160 },
},
@@ -216,7 +216,7 @@ local itemsTable = {
{ itemName = "bow", clientId = 3350, buy = 400, sell = 100 },
{ itemName = "crossbow", clientId = 3349, buy = 500, sell = 120 },
{ itemName = "crystalline arrow", clientId = 15793, buy = 20 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
{ itemName = "earth arrow", clientId = 774, buy = 5 },
{ itemName = "envenomed arrow", clientId = 16143, buy = 12 },
diff --git a/data-otservbr-global/npc/romella.lua b/data-otservbr-global/npc/romella.lua
index 50caf76084e..58445165820 100644
--- a/data-otservbr-global/npc/romella.lua
+++ b/data-otservbr-global/npc/romella.lua
@@ -72,26 +72,26 @@ npcConfig.shop = {
{ itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 },
{ itemName = "dagger", clientId = 3267, buy = 5, sell = 2 },
{ itemName = "double axe", clientId = 3275, sell = 260 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "halberd", clientId = 3269, sell = 400 },
{ itemName = "hand axe", clientId = 3268, buy = 8, sell = 4 },
{ itemName = "hatchet", clientId = 3276, sell = 25 },
{ itemName = "katana", clientId = 3300, sell = 35 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
{ itemName = "longsword", clientId = 3285, buy = 160, sell = 51 },
{ itemName = "mace", clientId = 3286, buy = 90, sell = 30 },
{ itemName = "morning star", clientId = 3282, buy = 430, sell = 100 },
diff --git a/data-otservbr-global/npc/romir.lua b/data-otservbr-global/npc/romir.lua
index c658e358e6f..3d8ba015127 100644
--- a/data-otservbr-global/npc/romir.lua
+++ b/data-otservbr-global/npc/romir.lua
@@ -29,20 +29,20 @@ local itemsTable = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
["runes"] = {
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -54,14 +54,14 @@ local itemsTable = {
{ itemName = "fire bomb rune", clientId = 3192, buy = 147 },
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "intense healing rune", clientId = 3152, buy = 95 },
{ itemName = "light magic missile rune", clientId = 3174, buy = 4 },
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
["wands"] = {
@@ -83,12 +83,12 @@ local itemsTable = {
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["others"] = {
{ itemName = "spellwand", clientId = 651, sell = 299 },
diff --git a/data-otservbr-global/npc/rowenna.lua b/data-otservbr-global/npc/rowenna.lua
index 1286e52c5ff..5e82f712738 100644
--- a/data-otservbr-global/npc/rowenna.lua
+++ b/data-otservbr-global/npc/rowenna.lua
@@ -73,26 +73,26 @@ npcConfig.shop = {
{ itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 },
{ itemName = "dagger", clientId = 3267, buy = 5, sell = 2 },
{ itemName = "double axe", clientId = 3275, sell = 260 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "halberd", clientId = 3269, sell = 400 },
{ itemName = "hand axe", clientId = 3268, buy = 8, sell = 4 },
{ itemName = "hatchet", clientId = 3276, sell = 25 },
{ itemName = "katana", clientId = 3300, sell = 35 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
{ itemName = "longsword", clientId = 3285, buy = 160, sell = 51 },
{ itemName = "mace", clientId = 3286, buy = 90, sell = 30 },
{ itemName = "morning star", clientId = 3282, buy = 430, sell = 100 },
diff --git a/data-otservbr-global/npc/runtel_blackspark.lua b/data-otservbr-global/npc/runtel_blackspark.lua
index 0722946c735..23ea56cfa6e 100644
--- a/data-otservbr-global/npc/runtel_blackspark.lua
+++ b/data-otservbr-global/npc/runtel_blackspark.lua
@@ -54,7 +54,7 @@ npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
npcConfig.shop = {
{ itemName = "animate dead rune", clientId = 3203, buy = 375 },
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -69,10 +69,10 @@ npcConfig.shop = {
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
{ itemName = "fireball rune", clientId = 3189, buy = 30 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "holy missile rune", clientId = 3182, buy = 16 },
@@ -87,16 +87,16 @@ npcConfig.shop = {
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "soulfire rune", clientId = 3195, buy = 46 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "stone shower rune", clientId = 3175, buy = 37 },
+ { itemName = "stone shower rune", clientId = 3175, buy = 41 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
- { itemName = "thunderstorm rune", clientId = 3202, buy = 47 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
+ { itemName = "thunderstorm rune", clientId = 3202, buy = 52 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "wild growth rune", clientId = 3156, buy = 160 },
}
-- On buy npc shop message
diff --git a/data-otservbr-global/npc/sam.lua b/data-otservbr-global/npc/sam.lua
index 77b082ec02e..6bb28c3cf39 100644
--- a/data-otservbr-global/npc/sam.lua
+++ b/data-otservbr-global/npc/sam.lua
@@ -179,17 +179,17 @@ npcConfig.shop = {
{ itemName = "dagger", clientId = 3267, buy = 5, sell = 2 },
{ itemName = "double axe", clientId = 3275, sell = 260 },
{ itemName = "doublet", clientId = 3379, buy = 16, sell = 3 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
{ itemName = "dwarven shield", clientId = 3425, buy = 500, sell = 100 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "halberd", clientId = 3269, sell = 400 },
{ itemName = "hand axe", clientId = 3268, buy = 8, sell = 4 },
@@ -197,11 +197,11 @@ npcConfig.shop = {
{ itemName = "iron helmet", clientId = 3353, buy = 390, sell = 150 },
{ itemName = "jacket", clientId = 3561, buy = 12, sell = 1 },
{ itemName = "katana", clientId = 3300, sell = 35 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
{ itemName = "leather armor", clientId = 3361, buy = 35, sell = 12 },
{ itemName = "leather boots", clientId = 3552, buy = 10, sell = 2 },
{ itemName = "leather helmet", clientId = 3355, buy = 12, sell = 4 },
diff --git a/data-otservbr-global/npc/sandra.lua b/data-otservbr-global/npc/sandra.lua
index a362b8ee78c..48c882af114 100644
--- a/data-otservbr-global/npc/sandra.lua
+++ b/data-otservbr-global/npc/sandra.lua
@@ -152,16 +152,16 @@ npcConfig.shop = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
{ itemName = "vial of blood", clientId = 2874, buy = 15, count = 5 },
{ itemName = "vial of oil", clientId = 2874, buy = 20, count = 7 },
diff --git a/data-otservbr-global/npc/shanar.lua b/data-otservbr-global/npc/shanar.lua
index 157a27000ac..a0928f06386 100644
--- a/data-otservbr-global/npc/shanar.lua
+++ b/data-otservbr-global/npc/shanar.lua
@@ -83,26 +83,26 @@ npcConfig.shop = {
{ itemName = "dagger", clientId = 3267, buy = 5, sell = 2 },
{ itemName = "devil helmet", clientId = 3356, sell = 450 },
{ itemName = "dragon shield", clientId = 3416, sell = 360 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "guardian shield", clientId = 3415, sell = 180 },
{ itemName = "iron helmet", clientId = 3353, sell = 145 },
{ itemName = "knight armor", clientId = 3370, sell = 875 },
{ itemName = "knight legs", clientId = 3371, sell = 375 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
{ itemName = "leather armor", clientId = 3361, buy = 35, sell = 12 },
{ itemName = "leather helmet", clientId = 3355, buy = 12, sell = 4 },
{ itemName = "leather legs", clientId = 3559, sell = 1 },
diff --git a/data-otservbr-global/npc/shiriel.lua b/data-otservbr-global/npc/shiriel.lua
index eda4a03f1e0..faadabbb83a 100644
--- a/data-otservbr-global/npc/shiriel.lua
+++ b/data-otservbr-global/npc/shiriel.lua
@@ -28,17 +28,17 @@ local itemsTable = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
["runes"] = {
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -50,14 +50,14 @@ local itemsTable = {
{ itemName = "fire bomb rune", clientId = 3192, buy = 147 },
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "intense healing rune", clientId = 3152, buy = 95 },
{ itemName = "light magic missile rune", clientId = 3174, buy = 4 },
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
["wands"] = {
@@ -71,12 +71,12 @@ local itemsTable = {
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["others"] = {
{ itemName = "spellwand", clientId = 651, sell = 299 },
diff --git a/data-otservbr-global/npc/siflind.lua b/data-otservbr-global/npc/siflind.lua
index fc4fc111da8..ade119ca8ee 100644
--- a/data-otservbr-global/npc/siflind.lua
+++ b/data-otservbr-global/npc/siflind.lua
@@ -217,35 +217,35 @@ npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
npcConfig.shop = {
{ itemName = "animal cure", clientId = 8819, buy = 400 },
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
{ itemName = "cure poison rune", clientId = 3153, buy = 65 },
{ itemName = "destroy field rune", clientId = 3148, buy = 15 },
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
{ itemName = "empty potion flask", clientId = 283, sell = 5 },
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "energy field rune", clientId = 3164, buy = 38 },
{ itemName = "energy wall rune", clientId = 3166, buy = 85 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
{ itemName = "explosion rune", clientId = 3200, buy = 31 },
{ itemName = "fire bomb rune", clientId = 3192, buy = 147 },
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "hailstorm rod", clientId = 3067, buy = 15000 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "intense healing rune", clientId = 3152, buy = 95 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
{ itemName = "light magic missile rune", clientId = 3174, buy = 4 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "moonlight rod", clientId = 3070, buy = 1000 },
@@ -258,14 +258,14 @@ npcConfig.shop = {
{ itemName = "springsprout rod", clientId = 8084, buy = 18000 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "terra rod", clientId = 3065, buy = 10000 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "underworld rod", clientId = 8082, buy = 22000 },
{ itemName = "vial", clientId = 2874, sell = 5 },
{ itemName = "wand of cosmic energy", clientId = 3073, buy = 10000 },
diff --git a/data-otservbr-global/npc/sigurd.lua b/data-otservbr-global/npc/sigurd.lua
index 57d14f02f4b..cb10f5bb3a8 100644
--- a/data-otservbr-global/npc/sigurd.lua
+++ b/data-otservbr-global/npc/sigurd.lua
@@ -30,17 +30,17 @@ local itemsTable = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
["runes"] = {
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -52,14 +52,14 @@ local itemsTable = {
{ itemName = "fire bomb rune", clientId = 3192, buy = 147 },
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "intense healing rune", clientId = 3152, buy = 95 },
{ itemName = "light magic missile rune", clientId = 3174, buy = 4 },
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
["wands"] = {
@@ -73,12 +73,12 @@ local itemsTable = {
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["others"] = {
{ itemName = "spellwand", clientId = 651, sell = 299 },
diff --git a/data-otservbr-global/npc/silas.lua b/data-otservbr-global/npc/silas.lua
index 87271b1c8f3..987c04bebbd 100644
--- a/data-otservbr-global/npc/silas.lua
+++ b/data-otservbr-global/npc/silas.lua
@@ -59,7 +59,7 @@ npcConfig.shop = {
{ itemName = "bow", clientId = 3350, buy = 400, sell = 100 },
{ itemName = "crossbow", clientId = 3349, sell = 120 },
{ itemName = "crystalline arrow", clientId = 15793, buy = 20 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
{ itemName = "earth arrow", clientId = 774, buy = 5 },
{ itemName = "envenomed arrow", clientId = 16143, buy = 12 },
diff --git a/data-otservbr-global/npc/sundara.lua b/data-otservbr-global/npc/sundara.lua
index b75a7349432..438df6dc98c 100644
--- a/data-otservbr-global/npc/sundara.lua
+++ b/data-otservbr-global/npc/sundara.lua
@@ -36,21 +36,21 @@ local itemsTable = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
["runes"] = {
{ itemName = "animate dead rune", clientId = 3203, buy = 375 },
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -65,7 +65,7 @@ local itemsTable = {
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
{ itemName = "fireball rune", clientId = 3189, buy = 30 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "holy missile rune", clientId = 3182, buy = 16 },
{ itemName = "icicle rune", clientId = 3158, buy = 30 },
@@ -77,9 +77,9 @@ local itemsTable = {
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "stone shower rune", clientId = 3175, buy = 37 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
- { itemName = "thunderstorm rune", clientId = 3202, buy = 47 },
+ { itemName = "stone shower rune", clientId = 3175, buy = 41 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
+ { itemName = "thunderstorm rune", clientId = 3202, buy = 52 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
{ itemName = "wild growth rune", clientId = 3156, buy = 160 },
},
@@ -102,12 +102,12 @@ local itemsTable = {
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["others"] = {
{ itemName = "spellwand", clientId = 651, sell = 299 },
diff --git a/data-otservbr-global/npc/taegen.lua b/data-otservbr-global/npc/taegen.lua
index 5df29712232..202118c3522 100644
--- a/data-otservbr-global/npc/taegen.lua
+++ b/data-otservbr-global/npc/taegen.lua
@@ -65,7 +65,7 @@ npcConfig.shop = {
{ itemName = "bow", clientId = 3350, buy = 400, sell = 100 },
{ itemName = "crossbow", clientId = 3349, buy = 500, sell = 120 },
{ itemName = "crystalline arrow", clientId = 15793, buy = 20 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
{ itemName = "earth arrow", clientId = 774, buy = 5 },
{ itemName = "envenomed arrow", clientId = 16143, buy = 12 },
diff --git a/data-otservbr-global/npc/talila.lua b/data-otservbr-global/npc/talila.lua
index c3d374ee1fb..b292763f493 100644
--- a/data-otservbr-global/npc/talila.lua
+++ b/data-otservbr-global/npc/talila.lua
@@ -69,7 +69,7 @@ npcConfig.shop = {
{ itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "ancient coin", clientId = 24390, sell = 350 },
{ itemName = "animate dead rune", clientId = 3203, buy = 375 },
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "bar of gold", clientId = 14112, sell = 10000 },
{ itemName = "black pearl", clientId = 3027, buy = 560, sell = 280 },
{ itemName = "blue crystal shard", clientId = 16119, sell = 1500 },
@@ -117,10 +117,10 @@ npcConfig.shop = {
{ itemName = "golden amulet", clientId = 3013, buy = 6600 },
{ itemName = "golden figurine", clientId = 5799, sell = 3000 },
{ itemName = "golden goblet", clientId = 5805, buy = 5000 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "greater guardian gem", clientId = 44604, sell = 10000 },
{ itemName = "greater marksman gem", clientId = 44607, sell = 10000 },
{ itemName = "greater mystic gem", clientId = 44613, sell = 10000 },
@@ -183,18 +183,18 @@ npcConfig.shop = {
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
{ itemName = "star herb", clientId = 3736, sell = 15 },
{ itemName = "stone herb", clientId = 3735, sell = 20 },
- { itemName = "stone shower rune", clientId = 3175, buy = 37 },
+ { itemName = "stone shower rune", clientId = 3175, buy = 41 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
{ itemName = "summer dress", clientId = 8046, sell = 1500 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
- { itemName = "thunderstorm rune", clientId = 3202, buy = 47 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
+ { itemName = "thunderstorm rune", clientId = 3202, buy = 52 },
{ itemName = "tiger eye", clientId = 24961, sell = 350 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "unicorn figurine", clientId = 30054, sell = 50000 },
{ itemName = "vial", clientId = 2874, sell = 5 },
{ itemName = "violet crystal shard", clientId = 16120, sell = 1500 },
diff --git a/data-otservbr-global/npc/tandros.lua b/data-otservbr-global/npc/tandros.lua
index 07e51abc163..a91bd9b9257 100644
--- a/data-otservbr-global/npc/tandros.lua
+++ b/data-otservbr-global/npc/tandros.lua
@@ -35,20 +35,20 @@ local itemsTable = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
["runes"] = {
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -60,14 +60,14 @@ local itemsTable = {
{ itemName = "fire bomb rune", clientId = 3192, buy = 147 },
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "intense healing rune", clientId = 3152, buy = 95 },
{ itemName = "light magic missile rune", clientId = 3174, buy = 4 },
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
["wands"] = {
@@ -89,12 +89,12 @@ local itemsTable = {
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["others"] = {
{ itemName = "spellwand", clientId = 651, sell = 299 },
diff --git a/data-otservbr-global/npc/tarun.lua b/data-otservbr-global/npc/tarun.lua
index 65b9882f90e..57510390f19 100644
--- a/data-otservbr-global/npc/tarun.lua
+++ b/data-otservbr-global/npc/tarun.lua
@@ -128,8 +128,8 @@ npcConfig.shop = {
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "golden lotus brooch", clientId = 21974, sell = 270 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "hellspawn tail", clientId = 10304, sell = 475 },
{ itemName = "mammoth tusk", clientId = 10321, sell = 100 },
@@ -139,12 +139,12 @@ npcConfig.shop = {
{ itemName = "sabretooth", clientId = 10311, sell = 400 },
{ itemName = "spider silk", clientId = 5879, sell = 100 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "tusk", clientId = 3044, sell = 100 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "vial", clientId = 2874, sell = 5 },
}
-- On buy npc shop message
diff --git a/data-otservbr-global/npc/topsy.lua b/data-otservbr-global/npc/topsy.lua
index c37faecb0fd..6423ff34c4e 100644
--- a/data-otservbr-global/npc/topsy.lua
+++ b/data-otservbr-global/npc/topsy.lua
@@ -35,17 +35,17 @@ local itemsTable = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
["runes"] = {
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -57,14 +57,14 @@ local itemsTable = {
{ itemName = "fire bomb rune", clientId = 3192, buy = 147 },
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "intense healing rune", clientId = 3152, buy = 95 },
{ itemName = "light magic missile rune", clientId = 3174, buy = 4 },
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
["wands"] = {
@@ -79,12 +79,12 @@ local itemsTable = {
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["others"] = {
{ itemName = "spellwand", clientId = 651, sell = 299 },
diff --git a/data-otservbr-global/npc/turvy.lua b/data-otservbr-global/npc/turvy.lua
index b579efb9c75..c1e01b04c10 100644
--- a/data-otservbr-global/npc/turvy.lua
+++ b/data-otservbr-global/npc/turvy.lua
@@ -84,17 +84,17 @@ npcConfig.shop = {
{ itemName = "dagger", clientId = 3267, buy = 5, sell = 2 },
{ itemName = "double axe", clientId = 3275, sell = 260 },
{ itemName = "doublet", clientId = 3379, buy = 16, sell = 3 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
{ itemName = "dwarven shield", clientId = 3425, buy = 500, sell = 100 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "halberd", clientId = 3269, sell = 400 },
{ itemName = "hand axe", clientId = 3268, buy = 8, sell = 4 },
@@ -102,11 +102,11 @@ npcConfig.shop = {
{ itemName = "iron helmet", clientId = 3353, buy = 390, sell = 150 },
{ itemName = "jacket", clientId = 3561, buy = 12, sell = 1 },
{ itemName = "katana", clientId = 3300, sell = 35 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
{ itemName = "leather armor", clientId = 3361, buy = 35, sell = 12 },
{ itemName = "leather boots", clientId = 3552, buy = 10, sell = 2 },
{ itemName = "leather helmet", clientId = 3355, buy = 12, sell = 4 },
diff --git a/data-otservbr-global/npc/ulrik.lua b/data-otservbr-global/npc/ulrik.lua
index 8a7cb33f11a..359d7591dc3 100644
--- a/data-otservbr-global/npc/ulrik.lua
+++ b/data-otservbr-global/npc/ulrik.lua
@@ -72,17 +72,17 @@ npcConfig.shop = {
{ itemName = "dagger", clientId = 3267, buy = 5, sell = 2 },
{ itemName = "double axe", clientId = 3275, sell = 260 },
{ itemName = "doublet", clientId = 3379, buy = 16, sell = 3 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
{ itemName = "dwarven shield", clientId = 3425, buy = 500, sell = 100 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "halberd", clientId = 3269, sell = 400 },
{ itemName = "hand axe", clientId = 3268, buy = 8, sell = 4 },
@@ -90,11 +90,11 @@ npcConfig.shop = {
{ itemName = "iron helmet", clientId = 3353, buy = 390, sell = 150 },
{ itemName = "jacket", clientId = 3561, buy = 12, sell = 1 },
{ itemName = "katana", clientId = 3300, sell = 35 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
{ itemName = "leather armor", clientId = 3361, buy = 35, sell = 12 },
{ itemName = "leather helmet", clientId = 3355, buy = 12, sell = 4 },
{ itemName = "legion helmet", clientId = 3374, sell = 22 },
diff --git a/data-otservbr-global/npc/uzgod.lua b/data-otservbr-global/npc/uzgod.lua
index d7c1c4bb457..75d24176d66 100644
--- a/data-otservbr-global/npc/uzgod.lua
+++ b/data-otservbr-global/npc/uzgod.lua
@@ -166,26 +166,26 @@ npcConfig.shop = {
{ itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 },
{ itemName = "dagger", clientId = 3267, buy = 5, sell = 2 },
{ itemName = "double axe", clientId = 3275, sell = 260 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "halberd", clientId = 3269, sell = 310 },
{ itemName = "hand axe", clientId = 3268, buy = 8, sell = 4 },
{ itemName = "hatchet", clientId = 3276, sell = 25 },
{ itemName = "katana", clientId = 3300, sell = 35 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
{ itemName = "longsword", clientId = 3285, buy = 160, sell = 51 },
{ itemName = "mace", clientId = 3286, buy = 90 },
{ itemName = "morning star", clientId = 3282, buy = 430 },
diff --git a/data-otservbr-global/npc/valindara.lua b/data-otservbr-global/npc/valindara.lua
index cf9506fcdd5..5e4e2f9dc1d 100644
--- a/data-otservbr-global/npc/valindara.lua
+++ b/data-otservbr-global/npc/valindara.lua
@@ -74,7 +74,7 @@ npcConfig.shop = {
{ itemName = "amber", clientId = 32626, sell = 20000 },
{ itemName = "ancient coin", clientId = 24390, sell = 350 },
{ itemName = "animate dead rune", clientId = 3203, buy = 375 },
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "bar of gold", clientId = 14112, sell = 10000 },
{ itemName = "black pearl", clientId = 3027, buy = 560, sell = 280 },
{ itemName = "blue crystal shard", clientId = 16119, sell = 1500 },
@@ -122,10 +122,10 @@ npcConfig.shop = {
{ itemName = "golden amulet", clientId = 3013, buy = 6600 },
{ itemName = "golden figurine", clientId = 5799, sell = 3000 },
{ itemName = "golden goblet", clientId = 5805, buy = 5000 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "greater guardian gem", clientId = 44604, sell = 10000 },
{ itemName = "greater marksman gem", clientId = 44607, sell = 10000 },
{ itemName = "greater mystic gem", clientId = 44613, sell = 10000 },
@@ -188,18 +188,18 @@ npcConfig.shop = {
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
{ itemName = "star herb", clientId = 3736, sell = 15 },
{ itemName = "stone herb", clientId = 3735, sell = 20 },
- { itemName = "stone shower rune", clientId = 3175, buy = 37 },
+ { itemName = "stone shower rune", clientId = 3175, buy = 41 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
{ itemName = "summer dress", clientId = 8046, sell = 1500 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
- { itemName = "thunderstorm rune", clientId = 3202, buy = 47 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
+ { itemName = "thunderstorm rune", clientId = 3202, buy = 52 },
{ itemName = "tiger eye", clientId = 24961, sell = 350 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
{ itemName = "unicorn figurine", clientId = 30054, sell = 50000 },
{ itemName = "vial", clientId = 2874, sell = 5 },
{ itemName = "violet crystal shard", clientId = 16120, sell = 1500 },
diff --git a/data-otservbr-global/npc/vincent.lua b/data-otservbr-global/npc/vincent.lua
index 58f933670f7..dbd3605e800 100644
--- a/data-otservbr-global/npc/vincent.lua
+++ b/data-otservbr-global/npc/vincent.lua
@@ -59,7 +59,7 @@ npcConfig.shop = {
{ itemName = "bow", clientId = 3350, buy = 400, sell = 100 },
{ itemName = "crossbow", clientId = 3349, buy = 500, sell = 120 },
{ itemName = "crystalline arrow", clientId = 15793, buy = 20 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
{ itemName = "earth arrow", clientId = 774, buy = 5 },
{ itemName = "envenomed arrow", clientId = 16143, buy = 12 },
diff --git a/data-otservbr-global/npc/willard.lua b/data-otservbr-global/npc/willard.lua
index f6395476ba7..4cbaa0d0579 100644
--- a/data-otservbr-global/npc/willard.lua
+++ b/data-otservbr-global/npc/willard.lua
@@ -120,23 +120,23 @@ npcConfig.shop = {
{ itemName = "crowbar", clientId = 3304, buy = 260, sell = 50 },
{ itemName = "crystalline arrow", clientId = 15793, buy = 20 },
{ itemName = "dagger", clientId = 3267, buy = 5, sell = 2 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "double axe", clientId = 3275, sell = 260 },
{ itemName = "doublet", clientId = 3379, buy = 16, sell = 3 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
- { itemName = "durable exercise axe", clientId = 35280, buy = 945000, count = 1800 },
- { itemName = "durable exercise bow", clientId = 35282, buy = 945000, count = 1800 },
- { itemName = "durable exercise club", clientId = 35281, buy = 945000, count = 1800 },
- { itemName = "durable exercise shield", clientId = 44066, buy = 945000, count = 1800 },
- { itemName = "durable exercise sword", clientId = 35279, buy = 945000, count = 1800 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000, count = 1800 },
{ itemName = "dwarven shield", clientId = 3425, buy = 500, sell = 100 },
{ itemName = "earth arrow", clientId = 774, buy = 5 },
{ itemName = "envenomed arrow", clientId = 16143, buy = 12 },
- { itemName = "exercise axe", clientId = 28553, buy = 262500, count = 500 },
- { itemName = "exercise bow", clientId = 28555, buy = 262500, count = 500 },
- { itemName = "exercise club", clientId = 28554, buy = 262500, count = 500 },
- { itemName = "exercise shield", clientId = 44065, buy = 262500, count = 500 },
- { itemName = "exercise sword", clientId = 28552, buy = 262500, count = 500 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222, count = 500 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222, count = 500 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222, count = 500 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222, count = 500 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222, count = 500 },
{ itemName = "fire sword", clientId = 3280, sell = 1000 },
{ itemName = "flaming arrow", clientId = 763, buy = 5 },
{ itemName = "flash arrow", clientId = 761, buy = 5 },
@@ -146,11 +146,11 @@ npcConfig.shop = {
{ itemName = "iron helmet", clientId = 3353, buy = 390, sell = 150 },
{ itemName = "jacket", clientId = 3561, buy = 12, sell = 1 },
{ itemName = "katana", clientId = 3300, sell = 35 },
- { itemName = "lasting exercise axe", clientId = 35286, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise bow", clientId = 35288, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise club", clientId = 35287, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise shield", clientId = 44067, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise sword", clientId = 35285, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000, count = 14400 },
{ itemName = "leather armor", clientId = 3361, buy = 35, sell = 12 },
{ itemName = "leather boots", clientId = 3552, buy = 10, sell = 2 },
{ itemName = "leather helmet", clientId = 3355, buy = 12, sell = 4 },
diff --git a/data-otservbr-global/npc/xodet.lua b/data-otservbr-global/npc/xodet.lua
index f1148505266..7f0fa10d904 100644
--- a/data-otservbr-global/npc/xodet.lua
+++ b/data-otservbr-global/npc/xodet.lua
@@ -29,17 +29,17 @@ local itemsTable = {
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
{ itemName = "empty potion flask", clientId = 285, sell = 5 },
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
["runes"] = {
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -51,14 +51,14 @@ local itemsTable = {
{ itemName = "fire bomb rune", clientId = 3192, buy = 147 },
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 12 },
{ itemName = "intense healing rune", clientId = 3152, buy = 95 },
{ itemName = "light magic missile rune", clientId = 3174, buy = 4 },
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 12 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
["wands"] = {
@@ -73,12 +73,12 @@ local itemsTable = {
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
["exercise weapons"] = {
- { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
- { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
- { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
- { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
- { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
- { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000, count = 14400 },
},
["others"] = {
{ itemName = "spellwand", clientId = 651, sell = 299 },
diff --git a/data-otservbr-global/npc/yaman.lua b/data-otservbr-global/npc/yaman.lua
index 85620c0d79e..1c2d9146574 100644
--- a/data-otservbr-global/npc/yaman.lua
+++ b/data-otservbr-global/npc/yaman.lua
@@ -179,7 +179,7 @@ npcConfig.shop = {
{ itemName = "glacial rod", clientId = 16118, sell = 6500 },
{ itemName = "hailstorm rod", clientId = 3067, sell = 3000 },
{ itemName = "life ring", clientId = 3052, buy = 900, sell = 50 },
- { itemName = "might ring", clientId = 3048, buy = 5000, sell = 250, count = 20 },
+ { itemName = "might ring", clientId = 3048, buy = 25000, sell = 250, count = 20 },
{ itemName = "moonlight rod", clientId = 3070, sell = 200 },
{ itemName = "muck rod", clientId = 16117, sell = 6000 },
{ itemName = "mysterious fetish", clientId = 3078, sell = 50 },
diff --git a/data-otservbr-global/npc/zuma_magehide.lua b/data-otservbr-global/npc/zuma_magehide.lua
index 3633331a0bf..3d5a51717bc 100644
--- a/data-otservbr-global/npc/zuma_magehide.lua
+++ b/data-otservbr-global/npc/zuma_magehide.lua
@@ -62,13 +62,13 @@ npcConfig.shop = {
{ itemName = "energy ring", clientId = 3051, buy = 5000 },
{ itemName = "garlic necklace", clientId = 3083, buy = 100, count = 150 },
{ itemName = "life ring", clientId = 3052, buy = 900 },
- { itemName = "might ring", clientId = 3048, buy = 5000, count = 20 },
+ { itemName = "might ring", clientId = 3048, buy = 25000, count = 20 },
{ itemName = "power ring", clientId = 3050, buy = 100 },
{ itemName = "protection amulet", clientId = 3084, buy = 700, count = 250 },
{ itemName = "ring of healing", clientId = 3098, buy = 2000 },
{ itemName = "silver amulet", clientId = 3054, buy = 100, count = 200 },
{ itemName = "stealth ring", clientId = 3049, buy = 5000 },
- { itemName = "stone skin amulet", clientId = 3081, buy = 5000, count = 5 },
+ { itemName = "stone skin amulet", clientId = 3081, buy = 25000, count = 5 },
{ itemName = "strange talisman", clientId = 3045, buy = 100, count = 200 },
{ itemName = "sword ring", clientId = 3091, buy = 500 },
{ itemName = "time ring", clientId = 3053, buy = 5000 },
diff --git a/data/XML/imbuements.xml b/data/XML/imbuements.xml
index 0a2fd90702f..fa1c3312d8d 100644
--- a/data/XML/imbuements.xml
+++ b/data/XML/imbuements.xml
@@ -1,7 +1,7 @@
-
-
+
+
diff --git a/data/scripts/lib/shops.lua b/data/scripts/lib/shops.lua
index af19fd80386..8c38934938c 100644
--- a/data/scripts/lib/shops.lua
+++ b/data/scripts/lib/shops.lua
@@ -4,31 +4,31 @@ SupplyShopConfigTable = {
{ itemName = "fire mushroom", clientId = 3731, buy = 300 },
},
["exercise weapons"] = {
- { itemName = "enhanced exercise axe", clientId = 35280, buy = 2340000 },
- { itemName = "enhanced exercise bow", clientId = 35282, buy = 2340000 },
- { itemName = "enhanced exercise club", clientId = 35281, buy = 2340000 },
- { itemName = "enhanced exercise rod", clientId = 35283, buy = 2340000 },
- { itemName = "enhanced exercise shield", clientId = 44066, buy = 2340000 },
- { itemName = "enhanced exercise sword", clientId = 35279, buy = 2340000 },
- { itemName = "enhanced exercise wand", clientId = 35284, buy = 2340000 },
- { itemName = "exercise axe", clientId = 28553, buy = 1800000 },
- { itemName = "exercise bow", clientId = 28555, buy = 1800000 },
- { itemName = "exercise club", clientId = 28554, buy = 1800000 },
- { itemName = "exercise rod", clientId = 28556, buy = 1800000 },
- { itemName = "exercise shield", clientId = 44065, buy = 1800000 },
- { itemName = "exercise sword", clientId = 28552, buy = 1800000 },
- { itemName = "exercise wand", clientId = 28557, buy = 1800000 },
- { itemName = "masterful exercise axe", clientId = 35286, buy = 2700000 },
- { itemName = "masterful exercise bow", clientId = 35288, buy = 2700000 },
- { itemName = "masterful exercise club", clientId = 35287, buy = 2700000 },
- { itemName = "masterful exercise rod", clientId = 35289, buy = 2700000 },
- { itemName = "masterful exercise shield", clientId = 44067, buy = 2700000 },
- { itemName = "masterful exercise sword", clientId = 35285, buy = 2700000 },
- { itemName = "masterful exercise wand", clientId = 35290, buy = 2700000 },
+ { itemName = "durable exercise axe", clientId = 35280, buy = 1250000 },
+ { itemName = "durable exercise bow", clientId = 35282, buy = 1250000 },
+ { itemName = "durable exercise club", clientId = 35281, buy = 1250000 },
+ { itemName = "durable exercise rod", clientId = 35283, buy = 1250000 },
+ { itemName = "durable exercise shield", clientId = 44066, buy = 1250000 },
+ { itemName = "durable exercise sword", clientId = 35279, buy = 1250000 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 1250000 },
+ { itemName = "exercise axe", clientId = 28553, buy = 347222 },
+ { itemName = "exercise bow", clientId = 28555, buy = 347222 },
+ { itemName = "exercise club", clientId = 28554, buy = 347222 },
+ { itemName = "exercise rod", clientId = 28556, buy = 347222 },
+ { itemName = "exercise shield", clientId = 44065, buy = 347222 },
+ { itemName = "exercise sword", clientId = 28552, buy = 347222 },
+ { itemName = "exercise wand", clientId = 28557, buy = 347222 },
+ { itemName = "lasting exercise axe", clientId = 35286, buy = 10000000 },
+ { itemName = "lasting exercise bow", clientId = 35288, buy = 10000000 },
+ { itemName = "lasting exercise club", clientId = 35287, buy = 10000000 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 10000000 },
+ { itemName = "lasting exercise shield", clientId = 44067, buy = 10000000 },
+ { itemName = "lasting exercise sword", clientId = 35285, buy = 10000000 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 10000000 },
},
["distance equipments"] = {
{ itemName = "envenomed arrow", clientId = 16143, buy = 12 },
- { itemName = "diamond arrow", clientId = 35901, buy = 100 },
+ { itemName = "diamond arrow", clientId = 35901, buy = 130 },
{ itemName = "drill bolt", clientId = 16142, buy = 12 },
{ itemName = "crystalline arrow", clientId = 15793, buy = 20 },
{ itemName = "blue quiver", clientId = 35848, buy = 400 },
@@ -58,7 +58,7 @@ SupplyShopConfigTable = {
},
["runes"] = {
{ itemName = "animate dead rune", clientId = 3203, buy = 375 },
- { itemName = "avalanche rune", clientId = 3161, buy = 57 },
+ { itemName = "avalanche rune", clientId = 3161, buy = 64 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
@@ -74,7 +74,7 @@ SupplyShopConfigTable = {
{ itemName = "fire field rune", clientId = 3188, buy = 28 },
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
{ itemName = "fireball rune", clientId = 3189, buy = 65 },
- { itemName = "great fireball rune", clientId = 3191, buy = 57 },
+ { itemName = "great fireball rune", clientId = 3191, buy = 64 },
{ itemName = "heavy magic missile rune", clientId = 3198, buy = 65 },
{ itemName = "holy missile rune", clientId = 3182, buy = 16 },
{ itemName = "icicle rune", clientId = 3158, buy = 65 },
@@ -87,9 +87,9 @@ SupplyShopConfigTable = {
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "soulfire rune", clientId = 3195, buy = 46 },
{ itemName = "stalagmite rune", clientId = 3179, buy = 65 },
- { itemName = "stone shower rune", clientId = 3175, buy = 57 },
- { itemName = "sudden death rune", clientId = 3155, buy = 135 },
- { itemName = "thunderstorm rune", clientId = 3202, buy = 57 },
+ { itemName = "stone shower rune", clientId = 3175, buy = 41 },
+ { itemName = "sudden death rune", clientId = 3155, buy = 162 },
+ { itemName = "thunderstorm rune", clientId = 3202, buy = 52 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
{ itemName = "wild growth rune", clientId = 3156, buy = 160 },
},
@@ -110,7 +110,7 @@ SupplyShopConfigTable = {
{ itemName = "prismatic necklace", clientId = 16113, buy = 20000 },
{ itemName = "sacred tree amulet", clientId = 9302, buy = 30000 },
{ itemName = "shockwave amulet", clientId = 9304, buy = 30000 },
- { itemName = "stone skin amulet", clientId = 3081, buy = 5000 },
+ { itemName = "stone skin amulet", clientId = 3081, buy = 25000 },
{ itemName = "collar of blue plasma", clientId = 23542, buy = 60000 },
{ itemName = "collar of green plasma", clientId = 23543, buy = 60000 },
{ itemName = "collar of red plasma", clientId = 23544, buy = 60000 },
@@ -118,7 +118,7 @@ SupplyShopConfigTable = {
},
["rings"] = {
{ itemName = "life ring", clientId = 3052, buy = 900 },
- { itemName = "might ring", clientId = 3048, buy = 5000 },
+ { itemName = "might ring", clientId = 3048, buy = 25000 },
{ itemName = "ring of blue plasma", clientId = 23529, buy = 80000 },
{ itemName = "ring of green plasma", clientId = 23531, buy = 80000 },
{ itemName = "ring of healing", clientId = 3098, buy = 2000 },
@@ -131,17 +131,17 @@ SupplyShopConfigTable = {
},
["potions"] = {
{ itemName = "great health potion", clientId = 239, buy = 225 },
- { itemName = "great mana potion", clientId = 238, buy = 144 },
- { itemName = "great spirit potion", clientId = 7642, buy = 228 },
+ { itemName = "great mana potion", clientId = 238, buy = 158 },
+ { itemName = "great spirit potion", clientId = 7642, buy = 254 },
{ itemName = "health potion", clientId = 266, buy = 50 },
{ itemName = "mana potion", clientId = 268, buy = 56 },
{ itemName = "mana shield potion", clientId = 35563, buy = 200000 },
{ itemName = "ultimate health potion", clientId = 7643, buy = 379 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
- { itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
- { itemName = "supreme health potion", clientId = 23375, buy = 625 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 488 },
+ { itemName = "ultimate spirit potion", clientId = 23374, buy = 488 },
+ { itemName = "supreme health potion", clientId = 23375, buy = 650 },
{ itemName = "strong health potion", clientId = 236, buy = 115 },
- { itemName = "strong mana potion", clientId = 237, buy = 93 },
+ { itemName = "strong mana potion", clientId = 237, buy = 108 },
},
}
diff --git a/data/scripts/spells/attack/annihilation.lua b/data/scripts/spells/attack/annihilation.lua
index 5b9f7bc7f5b..9bc65f67667 100644
--- a/data/scripts/spells/attack/annihilation.lua
+++ b/data/scripts/spells/attack/annihilation.lua
@@ -33,7 +33,7 @@ spell:needTarget(true)
spell:blockWalls(true)
spell:needWeapon(true)
spell:cooldown(30 * 1000)
-spell:groupCooldown(4 * 1000)
+spell:groupCooldown(2 * 1000)
spell:needLearn(false)
spell:vocation("knight;true", "elite knight;true")
spell:register()
diff --git a/data/scripts/spells/attack/ultimate_ice_strike.lua b/data/scripts/spells/attack/ultimate_ice_strike.lua
index 4f8560248e8..ec494cb7bd7 100644
--- a/data/scripts/spells/attack/ultimate_ice_strike.lua
+++ b/data/scripts/spells/attack/ultimate_ice_strike.lua
@@ -30,7 +30,7 @@ spell:range(3)
spell:needCasterTargetOrDirection(true)
spell:blockWalls(true)
spell:cooldown(30 * 1000)
-spell:groupCooldown(4 * 1000, 30 * 1000)
+spell:groupCooldown(2 * 1000, 30 * 1000)
spell:needLearn(false)
spell:vocation("druid;true", "elder druid;true")
spell:register()
diff --git a/data/scripts/spells/attack/ultimate_terra_strike.lua b/data/scripts/spells/attack/ultimate_terra_strike.lua
index 705571c5989..d7309183fc7 100644
--- a/data/scripts/spells/attack/ultimate_terra_strike.lua
+++ b/data/scripts/spells/attack/ultimate_terra_strike.lua
@@ -30,7 +30,7 @@ spell:range(3)
spell:needCasterTargetOrDirection(true)
spell:blockWalls(true)
spell:cooldown(30 * 1000)
-spell:groupCooldown(4 * 1000, 30 * 1000)
+spell:groupCooldown(2 * 1000, 30 * 1000)
spell:needLearn(false)
spell:vocation("druid;true", "elder druid;true")
spell:register()
diff --git a/src/game/game.cpp b/src/game/game.cpp
index a4a53738b9b..7da1d87fb4a 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -8997,9 +8997,10 @@ void Game::playerCreateMarketOffer(uint32_t playerId, uint8_t type, uint16_t ite
return;
}
- uint64_t calcFee = (price / 100) * amount;
- uint64_t minFee = std::min(100000, calcFee);
- uint64_t fee = std::max(20, minFee);
+ uint64_t totalPrice = price * amount;
+ uint64_t totalFee = totalPrice * 0.02;
+ uint64_t maxFee = std::min(1000000, totalFee);
+ uint64_t fee = std::max(20, totalFee);
if (type == MARKETACTION_SELL) {
if (fee > (player->getBankBalance() + player->getMoney())) {
From 60177d4338bf6c5f1e9fb6d1190f4a8dbf7b881a Mon Sep 17 00:00:00 2001
From: Marco
Date: Thu, 2 Jan 2025 13:30:57 -0300
Subject: [PATCH 11/32] feat: add missing constants for client OS (#3179)
---
src/lua/functions/core/game/lua_enums.cpp | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/lua/functions/core/game/lua_enums.cpp b/src/lua/functions/core/game/lua_enums.cpp
index 2136a3e5927..8bff425808e 100644
--- a/src/lua/functions/core/game/lua_enums.cpp
+++ b/src/lua/functions/core/game/lua_enums.cpp
@@ -772,14 +772,22 @@ void LuaEnums::initCreatureTypeEnums(lua_State* L) {
}
void LuaEnums::initClientOsEnums(lua_State* L) {
+ registerEnum(L, CLIENTOS_NONE);
registerEnum(L, CLIENTOS_LINUX);
registerEnum(L, CLIENTOS_WINDOWS);
registerEnum(L, CLIENTOS_FLASH);
+ registerEnum(L, CLIENTOS_NEW_LINUX);
registerEnum(L, CLIENTOS_NEW_WINDOWS);
registerEnum(L, CLIENTOS_NEW_MAC);
registerEnum(L, CLIENTOS_OTCLIENT_LINUX);
registerEnum(L, CLIENTOS_OTCLIENT_WINDOWS);
registerEnum(L, CLIENTOS_OTCLIENT_MAC);
+ registerEnum(L, CLIENTOS_OTCLIENTV8_LINUX);
+ registerEnum(L, CLIENTOS_OTCLIENTV8_WINDOWS);
+ registerEnum(L, CLIENTOS_OTCLIENTV8_MAC);
+ registerEnum(L, CLIENTOS_OTCLIENTV8_ANDROID);
+ registerEnum(L, CLIENTOS_OTCLIENTV8_IOS);
+ registerEnum(L, CLIENTOS_OTCLIENTV8_WEB);
}
void LuaEnums::initFightModeEnums(lua_State* L) {
From b8f4090e6c19d9a813e6f9ecbe7e06d2835aacb0 Mon Sep 17 00:00:00 2001
From: "Leilani A." <168607226+kaleohanopahala@users.noreply.github.com>
Date: Thu, 2 Jan 2025 14:15:37 -0300
Subject: [PATCH 12/32] fix: thorgrin callbacks (#3202)
---
data-otservbr-global/npc/thorgrin.lua | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/data-otservbr-global/npc/thorgrin.lua b/data-otservbr-global/npc/thorgrin.lua
index 976c404af57..48eba7866bd 100644
--- a/data-otservbr-global/npc/thorgrin.lua
+++ b/data-otservbr-global/npc/thorgrin.lua
@@ -52,15 +52,15 @@ npcType.onCloseChannel = function(npc, creature)
end
-- Travel
-local function addTravelKeyword(keyword, cost, destination)
- local travelKeyword = keywordHandler:addKeyword({ keyword }, StdModule.say, { npcHandler = npcHandler, text = "Do you seek a ride to " .. keyword:titleCase() .. " for |TRAVELCOST|?", cost = cost, discount = "postman" })
- travelKeyword:addChildKeyword({ "yes" }, StdModule.travel, { npcHandler = npcHandler, premium = false, cost = cost, discount = "postman", destination = destination })
- travelKeyword:addChildKeyword({ "no" }, StdModule.say, { npcHandler = npcHandler, text = "Then not.", reset = true })
+local function addTravelKeyword(keyword, cost, destination, text, action, discount)
+ local travelKeyword = keywordHandler:addKeyword({ keyword }, StdModule.say, { npcHandler = npcHandler, text = text or "Do you seek a ride to " .. keyword:titleCase() .. " for |TRAVELCOST|?", cost = cost, discount = discount or "postman" })
+ travelKeyword:addChildKeyword({ "yes" }, StdModule.travel, { npcHandler = npcHandler, premium = false, cost = cost, discount = discount or "postman", destination = destination, text = "Full steam ahead!" }, nil, action)
+ travelKeyword:addChildKeyword({ "no" }, StdModule.say, { npcHandler = npcHandler, text = "Then not", reset = true })
end
addTravelKeyword("kazordoon", 210, Position(32659, 31957, 15))
addTravelKeyword("cormaya", 110, Position(33310, 31988, 15))
-addTravelKeyword("gnomprona", { "Would you like to travel to Gnomprona for |TRAVELCOST|?", "Full steam ahead!", "Then not." }, 200, "postman", Position(33516, 32856, 14))
+addTravelKeyword("gnomprona", 200, Position(33516, 32856, 14), "Would you like to travel to Gnomprona for |TRAVELCOST|?", nil, "postman")
keywordHandler:addKeyword({ "passage" }, StdModule.say, { npcHandler = npcHandler, text = "Do you want me take you to {Cormaya}, {Kazordoon} or {Gnomprona}?" })
From 15e3f1dec2de3ff471b80496922a31e61699b7fb Mon Sep 17 00:00:00 2001
From: Jean Carlo de Souza
Date: Thu, 2 Jan 2025 14:19:12 -0300
Subject: [PATCH 13/32] fix: prevent player corpses from being removed during
tile cleaning (#3205)
---
data/scripts/movements/closing_door.lua | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/data/scripts/movements/closing_door.lua b/data/scripts/movements/closing_door.lua
index 3a63407267e..1f977deb403 100644
--- a/data/scripts/movements/closing_door.lua
+++ b/data/scripts/movements/closing_door.lua
@@ -104,7 +104,7 @@ function closingDoor.onStepOut(creature, item, position, fromPosition)
while tileItem and i < tileCount do
tileItem = tile:getThing(i)
- if tileItem and tileItem:getUniqueId() ~= item.uid and tileItem:getType():isMovable() then
+ if tileItem and tileItem:getUniqueId() ~= item.uid and tileItem:getType():isMovable() and not isCorpse(tileItem:getUniqueId()) then
tileItem:remove()
else
i = i + 1
From 8e6a4fd16cd8208a48ad441db4b2a2a744650134 Mon Sep 17 00:00:00 2001
From: Jean Carlo de Souza
Date: Thu, 2 Jan 2025 14:19:23 -0300
Subject: [PATCH 14/32] fix: preserve decimal precision for skill percentage
calculation (#3207)
---
src/creatures/players/player.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp
index bfbc8148d0a..979f5d9a74d 100644
--- a/src/creatures/players/player.cpp
+++ b/src/creatures/players/player.cpp
@@ -885,7 +885,7 @@ void Player::addSkillAdvance(skills_t skill, uint64_t count) {
skills[skill].tries += count;
- uint32_t newPercent;
+ double_t newPercent;
if (nextReqTries > currReqTries) {
newPercent = Player::getPercentLevel(skills[skill].tries, nextReqTries);
} else {
From 4a92d84d629d54df3184500263f32943681cf69f Mon Sep 17 00:00:00 2001
From: Luan Luciano
Date: Thu, 2 Jan 2025 16:51:12 -0300
Subject: [PATCH 15/32] fix: remove unnecessary function for data-canary
(#2973)
---
data-otservbr-global/lib/functions/load.lua | 1 +
.../lib/functions/players.lua | 76 +++++++++++++++++
data-otservbr-global/lib/lib.lua | 3 +
data-otservbr-global/npc/thorgrin.lua | 6 +-
data/libs/functions/player.lua | 85 -------------------
5 files changed, 83 insertions(+), 88 deletions(-)
create mode 100644 data-otservbr-global/lib/functions/load.lua
create mode 100644 data-otservbr-global/lib/functions/players.lua
diff --git a/data-otservbr-global/lib/functions/load.lua b/data-otservbr-global/lib/functions/load.lua
new file mode 100644
index 00000000000..9862b00d2d8
--- /dev/null
+++ b/data-otservbr-global/lib/functions/load.lua
@@ -0,0 +1 @@
+dofile(DATA_DIRECTORY .. "/lib/functions/players.lua")
diff --git a/data-otservbr-global/lib/functions/players.lua b/data-otservbr-global/lib/functions/players.lua
new file mode 100644
index 00000000000..45fec4c6653
--- /dev/null
+++ b/data-otservbr-global/lib/functions/players.lua
@@ -0,0 +1,76 @@
+function Player.getCookiesDelivered(self)
+ local storage, amount =
+ {
+ Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.SimonTheBeggar,
+ Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.Markwin,
+ Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.Ariella,
+ Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.Hairycles,
+ Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.Djinn,
+ Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.AvarTar,
+ Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.OrcKing,
+ Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.Lorbas,
+ Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.Wyda,
+ Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.Hjaern,
+ }, 0
+ for i = 1, #storage do
+ if self:getStorageValue(storage[i]) == 1 then
+ amount = amount + 1
+ end
+ end
+ return amount
+end
+
+function Player.checkGnomeRank(self)
+ local points = self:getStorageValue(Storage.Quest.U9_60.BigfootsBurden.Rank)
+ local questProgress = self:getStorageValue(Storage.Quest.U9_60.BigfootsBurden.QuestLine)
+ if points >= 30 and points < 120 then
+ if questProgress <= 25 then
+ self:setStorageValue(Storage.Quest.U9_60.BigfootsBurden.QuestLine, 26)
+ self:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
+ self:addAchievement("Gnome Little Helper")
+ end
+ elseif points >= 120 and points < 480 then
+ if questProgress <= 26 then
+ self:setStorageValue(Storage.Quest.U9_60.BigfootsBurden.QuestLine, 27)
+ self:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
+ self:addAchievement("Gnome Little Helper")
+ self:addAchievement("Gnome Friend")
+ end
+ elseif points >= 480 and points < 1440 then
+ if questProgress <= 27 then
+ self:setStorageValue(Storage.Quest.U9_60.BigfootsBurden.QuestLine, 28)
+ self:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
+ self:addAchievement("Gnome Little Helper")
+ self:addAchievement("Gnome Friend")
+ self:addAchievement("Gnomelike")
+ end
+ elseif points >= 1440 then
+ if questProgress <= 29 then
+ self:setStorageValue(Storage.Quest.U9_60.BigfootsBurden.QuestLine, 30)
+ self:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
+ self:addAchievement("Gnome Little Helper")
+ self:addAchievement("Gnome Friend")
+ self:addAchievement("Gnomelike")
+ self:addAchievement("Honorary Gnome")
+ end
+ end
+ return true
+end
+
+function Player.addFamePoint(self)
+ local points = self:getStorageValue(Storage.Quest.U10_20.SpikeTaskQuest.Constants.Spike_Fame_Points)
+ local current = math.max(0, points)
+ self:setStorageValue(Storage.Quest.U10_20.SpikeTaskQuest.Constants.Spike_Fame_Points, current + 1)
+ self:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received a fame point.")
+end
+
+function Player.getFamePoints(self)
+ local points = self:getStorageValue(Storage.Quest.U10_20.SpikeTaskQuest.Constants.Spike_Fame_Points)
+ return math.max(0, points)
+end
+
+function Player.removeFamePoints(self, amount)
+ local points = self:getStorageValue(Storage.Quest.U10_20.SpikeTaskQuest.Constants.Spike_Fame_Points)
+ local current = math.max(0, points)
+ self:setStorageValue(Storage.Quest.U10_20.SpikeTaskQuest.Constants.Spike_Fame_Points, current - amount)
+end
diff --git a/data-otservbr-global/lib/lib.lua b/data-otservbr-global/lib/lib.lua
index 2e9ead889eb..e6ee85db8b4 100644
--- a/data-otservbr-global/lib/lib.lua
+++ b/data-otservbr-global/lib/lib.lua
@@ -9,3 +9,6 @@ dofile(DATA_DIRECTORY .. "/lib/quests/quest.lua")
-- Tables library
dofile(DATA_DIRECTORY .. "/lib/tables/load.lua")
+
+-- Functions library
+dofile(DATA_DIRECTORY .. "/lib/functions/load.lua")
diff --git a/data-otservbr-global/npc/thorgrin.lua b/data-otservbr-global/npc/thorgrin.lua
index 48eba7866bd..febc93e0c2d 100644
--- a/data-otservbr-global/npc/thorgrin.lua
+++ b/data-otservbr-global/npc/thorgrin.lua
@@ -53,9 +53,9 @@ end
-- Travel
local function addTravelKeyword(keyword, cost, destination, text, action, discount)
- local travelKeyword = keywordHandler:addKeyword({ keyword }, StdModule.say, { npcHandler = npcHandler, text = text or "Do you seek a ride to " .. keyword:titleCase() .. " for |TRAVELCOST|?", cost = cost, discount = discount or "postman" })
- travelKeyword:addChildKeyword({ "yes" }, StdModule.travel, { npcHandler = npcHandler, premium = false, cost = cost, discount = discount or "postman", destination = destination, text = "Full steam ahead!" }, nil, action)
- travelKeyword:addChildKeyword({ "no" }, StdModule.say, { npcHandler = npcHandler, text = "Then not", reset = true })
+ local travelKeyword = keywordHandler:addKeyword({ keyword }, StdModule.say, { npcHandler = npcHandler, text = text or "Do you seek a ride to " .. keyword:titleCase() .. " for |TRAVELCOST|?", cost = cost, discount = discount or "postman" })
+ travelKeyword:addChildKeyword({ "yes" }, StdModule.travel, { npcHandler = npcHandler, premium = false, cost = cost, discount = discount or "postman", destination = destination, text = "Full steam ahead!" }, nil, action)
+ travelKeyword:addChildKeyword({ "no" }, StdModule.say, { npcHandler = npcHandler, text = "Then not", reset = true })
end
addTravelKeyword("kazordoon", 210, Position(32659, 31957, 15))
diff --git a/data/libs/functions/player.lua b/data/libs/functions/player.lua
index 0350310e397..8dae1cb7fe5 100644
--- a/data/libs/functions/player.lua
+++ b/data/libs/functions/player.lua
@@ -90,91 +90,6 @@ function Player.addManaSpent(...)
end
-- Functions From OTServBR-Global
-function Player.getCookiesDelivered(self)
- if not IsRunningGlobalDatapack() then
- return true
- end
-
- local storage, amount =
- {
- Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.SimonTheBeggar,
- Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.Markwin,
- Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.Ariella,
- Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.Hairycles,
- Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.Djinn,
- Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.AvarTar,
- Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.OrcKing,
- Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.Lorbas,
- Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.Wyda,
- Storage.Quest.U8_1.WhatAFoolishQuest.CookieDelivery.Hjaern,
- }, 0
- for i = 1, #storage do
- if self:getStorageValue(storage[i]) == 1 then
- amount = amount + 1
- end
- end
- return amount
-end
-
-function Player.checkGnomeRank(self)
- if not IsRunningGlobalDatapack() then
- return true
- end
-
- local points = self:getStorageValue(Storage.Quest.U9_60.BigfootsBurden.Rank)
- local questProgress = self:getStorageValue(Storage.Quest.U9_60.BigfootsBurden.QuestLine)
- if points >= 30 and points < 120 then
- if questProgress <= 25 then
- self:setStorageValue(Storage.Quest.U9_60.BigfootsBurden.QuestLine, 26)
- self:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
- self:addAchievement("Gnome Little Helper")
- end
- elseif points >= 120 and points < 480 then
- if questProgress <= 26 then
- self:setStorageValue(Storage.Quest.U9_60.BigfootsBurden.QuestLine, 27)
- self:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
- self:addAchievement("Gnome Little Helper")
- self:addAchievement("Gnome Friend")
- end
- elseif points >= 480 and points < 1440 then
- if questProgress <= 27 then
- self:setStorageValue(Storage.Quest.U9_60.BigfootsBurden.QuestLine, 28)
- self:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
- self:addAchievement("Gnome Little Helper")
- self:addAchievement("Gnome Friend")
- self:addAchievement("Gnomelike")
- end
- elseif points >= 1440 then
- if questProgress <= 29 then
- self:setStorageValue(Storage.Quest.U9_60.BigfootsBurden.QuestLine, 30)
- self:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
- self:addAchievement("Gnome Little Helper")
- self:addAchievement("Gnome Friend")
- self:addAchievement("Gnomelike")
- self:addAchievement("Honorary Gnome")
- end
- end
- return true
-end
-
-function Player.addFamePoint(self)
- local points = self:getStorageValue(Storage.Quest.U10_20.SpikeTaskQuest.Constants.Spike_Fame_Points)
- local current = math.max(0, points)
- self:setStorageValue(Storage.Quest.U10_20.SpikeTaskQuest.Constants.Spike_Fame_Points, current + 1)
- self:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received a fame point.")
-end
-
-function Player.getFamePoints(self)
- local points = self:getStorageValue(Storage.Quest.U10_20.SpikeTaskQuest.Constants.Spike_Fame_Points)
- return math.max(0, points)
-end
-
-function Player.removeFamePoints(self, amount)
- local points = self:getStorageValue(Storage.Quest.U10_20.SpikeTaskQuest.Constants.Spike_Fame_Points)
- local current = math.max(0, points)
- self:setStorageValue(Storage.Quest.U10_20.SpikeTaskQuest.Constants.Spike_Fame_Points, current - amount)
-end
-
function Player.depositMoney(self, amount)
return Bank.deposit(self, amount)
end
From e5fb63ca1172fb9463ec021af8c404fe0d6fbf0e Mon Sep 17 00:00:00 2001
From: Eduardo Dantas
Date: Thu, 2 Jan 2025 17:22:56 -0300
Subject: [PATCH 16/32] feat: create database backup on server shutdown (#3069)
This update introduces a refined automatic database backup feature
during the server shutdown process. The main improvements include:
1. Automatic Compression: The database backup is now always
compressed using gzip, reducing disk space usage.
2. Backup Management: The system organizes backup files into folders
named by date and automatically deletes backups older than 7 days. This
ensures that the backup storage remains manageable over time without
manual intervention.
The motivation behind these changes is to create a more efficient and
reliable way of managing database backups, ensuring data safety while
optimizing storage space usage. The feature can be highly useful for
production servers, as it creates backups during shutdown and maintains
them efficiently by automatically removing old backups.
---
.github/workflows/build-ubuntu.yml | 10 +--
.gitignore | 3 +
config.lua.dist | 1 +
src/canary_server.cpp | 1 +
src/config/config_enums.hpp | 1 +
src/config/configmanager.cpp | 1 +
src/database/database.cpp | 97 ++++++++++++++++++++++++++++++
src/database/database.hpp | 17 ++++++
8 files changed, 127 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml
index 6a252630f48..692bafc331c 100644
--- a/.github/workflows/build-ubuntu.yml
+++ b/.github/workflows/build-ubuntu.yml
@@ -52,12 +52,14 @@ jobs:
run: >
sudo apt-get update && sudo apt-get install ccache linux-headers-"$(uname -r)"
- - name: Switch to gcc-12 on Ubuntu 22.04
+ - name: Switch to gcc-13 on Ubuntu 22.04
if: matrix.os == 'ubuntu-22.04'
run: |
- sudo apt install gcc-12 g++-12
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 100 --slave /usr/bin/g++ g++ /usr/bin/g++-12 --slave /usr/bin/gcov gcov /usr/bin/gcov-12
- sudo update-alternatives --set gcc /usr/bin/gcc-12
+ sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
+ sudo apt-get update
+ sudo apt install gcc-13 g++-13 -y
+ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 100 --slave /usr/bin/g++ g++ /usr/bin/g++-13 --slave /usr/bin/gcov gcov /usr/bin/gcov-12
+ sudo update-alternatives --set gcc /usr/bin/gcc-13
- name: Switch to gcc-14 on Ubuntu 24.04
if: matrix.os == 'ubuntu-24.04'
diff --git a/.gitignore b/.gitignore
index a73a8c3022c..4bd08dd8908 100644
--- a/.gitignore
+++ b/.gitignore
@@ -395,5 +395,8 @@ canary.old
# VCPKG
vcpkg_installed
+# DB Backups
+database_backup
+
# CLION
cmake-build-*
diff --git a/config.lua.dist b/config.lua.dist
index 7c891c84b5d..60c4b770063 100644
--- a/config.lua.dist
+++ b/config.lua.dist
@@ -401,6 +401,7 @@ mysqlHost = "127.0.0.1"
mysqlUser = "root"
mysqlPass = "root"
mysqlDatabase = "otservbr-global"
+mysqlDatabaseBackup = false
mysqlPort = 3306
mysqlSock = ""
passwordType = "sha1"
diff --git a/src/canary_server.cpp b/src/canary_server.cpp
index 8dfbcfc954c..492028093ed 100644
--- a/src/canary_server.cpp
+++ b/src/canary_server.cpp
@@ -390,6 +390,7 @@ void CanaryServer::modulesLoadHelper(bool loaded, std::string moduleName) {
}
void CanaryServer::shutdown() {
+ g_database().createDatabaseBackup(true);
g_dispatcher().shutdown();
g_metrics().shutdown();
inject().shutdown();
diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp
index 65e585159e8..d180cc668c2 100644
--- a/src/config/config_enums.hpp
+++ b/src/config/config_enums.hpp
@@ -167,6 +167,7 @@ enum ConfigKey_t : uint16_t {
MONTH_KILLS_TO_RED,
MULTIPLIER_ATTACKONFIST,
MYSQL_DB,
+ MYSQL_DB_BACKUP,
MYSQL_HOST,
MYSQL_PASS,
MYSQL_SOCK,
diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp
index 1034a28be3e..5ef93d5461b 100644
--- a/src/config/configmanager.cpp
+++ b/src/config/configmanager.cpp
@@ -71,6 +71,7 @@ bool ConfigManager::load() {
loadStringConfig(L, MAP_DOWNLOAD_URL, "mapDownloadUrl", "");
loadStringConfig(L, MAP_NAME, "mapName", "canary");
loadStringConfig(L, MYSQL_DB, "mysqlDatabase", "canary");
+ loadBoolConfig(L, MYSQL_DB_BACKUP, "mysqlDatabaseBackup", false);
loadStringConfig(L, MYSQL_HOST, "mysqlHost", "127.0.0.1");
loadStringConfig(L, MYSQL_PASS, "mysqlPass", "");
loadStringConfig(L, MYSQL_SOCK, "mysqlSock", "");
diff --git a/src/database/database.cpp b/src/database/database.cpp
index fcb7371708d..a6e732694b1 100644
--- a/src/database/database.cpp
+++ b/src/database/database.cpp
@@ -12,6 +12,7 @@
#include "config/configmanager.hpp"
#include "lib/di/container.hpp"
#include "lib/metrics/metrics.hpp"
+#include "utils/tools.hpp"
Database::~Database() {
if (handle != nullptr) {
@@ -60,6 +61,102 @@ bool Database::connect(const std::string* host, const std::string* user, const s
return true;
}
+void Database::createDatabaseBackup(bool compress) const {
+ if (!g_configManager().getBoolean(MYSQL_DB_BACKUP)) {
+ return;
+ }
+
+ // Get current time for formatting
+ auto now = std::chrono::system_clock::now();
+ std::time_t now_c = std::chrono::system_clock::to_time_t(now);
+ std::string formattedDate = fmt::format("{:%Y-%m-%d}", fmt::localtime(now_c));
+ std::string formattedTime = fmt::format("{:%H-%M-%S}", fmt::localtime(now_c));
+
+ // Create a backup directory based on the current date
+ std::string backupDir = fmt::format("database_backup/{}/", formattedDate);
+ std::filesystem::create_directories(backupDir);
+ std::string backupFileName = fmt::format("{}backup_{}.sql", backupDir, formattedTime);
+
+ // Create a temporary configuration file for MySQL credentials
+ std::string tempConfigFile = "database_backup.cnf";
+ std::ofstream configFile(tempConfigFile);
+ if (configFile.is_open()) {
+ configFile << "[client]\n";
+ configFile << "user=" << g_configManager().getString(MYSQL_USER) << "\n";
+ configFile << "password=" << g_configManager().getString(MYSQL_PASS) << "\n";
+ configFile << "host=" << g_configManager().getString(MYSQL_HOST) << "\n";
+ configFile << "port=" << g_configManager().getNumber(SQL_PORT) << "\n";
+ configFile.close();
+ } else {
+ g_logger().error("Failed to create temporary MySQL configuration file.");
+ return;
+ }
+
+ // Execute mysqldump command to create backup file
+ std::string command = fmt::format(
+ "mysqldump --defaults-extra-file={} {} > {}",
+ tempConfigFile, g_configManager().getString(MYSQL_DB), backupFileName
+ );
+
+ int result = std::system(command.c_str());
+ std::filesystem::remove(tempConfigFile);
+
+ if (result != 0) {
+ g_logger().error("Failed to create database backup using mysqldump.");
+ return;
+ }
+
+ // Compress the backup file if requested
+ std::string compressedFileName;
+ compressedFileName = backupFileName + ".gz";
+ gzFile gzFile = gzopen(compressedFileName.c_str(), "wb9");
+ if (!gzFile) {
+ g_logger().error("Failed to open gzip file for compression.");
+ return;
+ }
+
+ std::ifstream backupFile(backupFileName, std::ios::binary);
+ if (!backupFile.is_open()) {
+ g_logger().error("Failed to open backup file for compression: {}", backupFileName);
+ gzclose(gzFile);
+ return;
+ }
+
+ std::string buffer(8192, '\0');
+ while (backupFile.read(&buffer[0], buffer.size()) || backupFile.gcount() > 0) {
+ gzwrite(gzFile, buffer.data(), backupFile.gcount());
+ }
+
+ backupFile.close();
+ gzclose(gzFile);
+ std::filesystem::remove(backupFileName);
+
+ g_logger().info("Database backup successfully compressed to: {}", compressedFileName);
+
+ // Delete backups older than 7 days
+ auto nowTime = std::chrono::system_clock::now();
+ auto sevenDaysAgo = nowTime - std::chrono::hours(7 * 24); // 7 days in hours
+ for (const auto &entry : std::filesystem::directory_iterator("database_backup")) {
+ if (entry.is_directory()) {
+ try {
+ for (const auto &file : std::filesystem::directory_iterator(entry)) {
+ if (file.path().extension() == ".gz") {
+ auto fileTime = std::filesystem::last_write_time(file);
+ auto fileTimeSystemClock = std::chrono::clock_cast(fileTime);
+
+ if (fileTimeSystemClock < sevenDaysAgo) {
+ std::filesystem::remove(file);
+ g_logger().info("Deleted old backup file: {}", file.path().string());
+ }
+ }
+ }
+ } catch (const std::filesystem::filesystem_error &e) {
+ g_logger().error("Failed to check or delete files in backup directory: {}. Error: {}", entry.path().string(), e.what());
+ }
+ }
+ }
+}
+
bool Database::beginTransaction() {
if (!executeQuery("BEGIN")) {
return false;
diff --git a/src/database/database.hpp b/src/database/database.hpp
index 69c47d324ad..3566425a5cd 100644
--- a/src/database/database.hpp
+++ b/src/database/database.hpp
@@ -37,6 +37,23 @@ class Database {
bool connect(const std::string* host, const std::string* user, const std::string* password, const std::string* database, uint32_t port, const std::string* sock);
+ /**
+ * @brief Creates a backup of the database.
+ *
+ * This function generates a backup of the database, with options for compression.
+ * The backup can be triggered periodically or during specific events like server loading.
+ *
+ * The backup operation will only execute if the configuration option `MYSQL_DB_BACKUP`
+ * is set to true in the `config.lua` file. If this configuration is disabled, the function
+ * will return without performing any action.
+ *
+ * @param compress Indicates whether the backup should be compressed.
+ * - If `compress` is true, the backup is created during an interval-based save, which occurs every 2 hours.
+ * This helps prevent excessive growth in the number of backup files.
+ * - If `compress` is false, the backup is created during the global save, which is triggered once a day when the server loads.
+ */
+ void createDatabaseBackup(bool compress) const;
+
bool retryQuery(std::string_view query, int retries);
bool executeQuery(std::string_view query);
From 09024a755c13e9fac58cfd0ce5ca10a664857add Mon Sep 17 00:00:00 2001
From: Eduardo Dantas
Date: Sat, 4 Jan 2025 20:56:18 -0300
Subject: [PATCH 17/32] improve: boosted boss selection (#3208)
---
src/io/io_bosstiary.cpp | 118 +++++++++++++++++-----------------------
1 file changed, 51 insertions(+), 67 deletions(-)
diff --git a/src/io/io_bosstiary.cpp b/src/io/io_bosstiary.cpp
index 3e8a05524e6..57a9a85e294 100644
--- a/src/io/io_bosstiary.cpp
+++ b/src/io/io_bosstiary.cpp
@@ -20,46 +20,46 @@ IOBosstiary &IOBosstiary::getInstance() {
}
void IOBosstiary::loadBoostedBoss() {
- Database &database = Database::getInstance();
- std::ostringstream query;
- query << "SELECT * FROM `boosted_boss`";
- DBResult_ptr result = database.storeQuery(query.str());
- if (!result) {
- g_logger().error("[{}] Failed to detect boosted boss database. (CODE 01)", __FUNCTION__);
- return;
- }
-
- auto date = result->getNumber("date");
+ std::string query = R"SQL(
+ SELECT `date`, `boostname`, `raceid`, `looktypeEx`, `looktype`,
+ `lookfeet`, `looklegs`, `lookhead`, `lookbody`,
+ `lookaddons`, `lookmount`
+ FROM `boosted_boss`
+ )SQL";
+
+ DBResult_ptr result = g_database().storeQuery(query);
auto timeNow = getTimeNow();
auto time = localtime(&timeNow);
auto today = time->tm_mday;
- auto bossMap = getBosstiaryMap();
+ const auto &bossMap = getBosstiaryMap();
if (bossMap.size() <= 1) {
g_logger().error("[{}] It is not possible to create a boosted boss with only one registered boss. (CODE 02)", __FUNCTION__);
return;
}
- std::string bossName;
- uint16_t bossId = 0;
- if (date == today) {
- bossName = result->getString("boostname");
- bossId = result->getNumber("raceid");
- setBossBoostedName(bossName);
- setBossBoostedId(bossId);
- g_logger().info("Boosted boss: {}", bossName);
- return;
+ if (!result) {
+ g_logger().warn("[{}] No boosted boss found in g_database(). A new one will be selected.", __FUNCTION__);
+ } else {
+ auto date = result->getNumber("date");
+ if (date == today) {
+ std::string bossName = result->getString("boostname");
+ uint16_t bossId = result->getNumber("raceid");
+ setBossBoostedName(bossName);
+ setBossBoostedId(bossId);
+ g_logger().info("Boosted boss: {}", bossName);
+ return;
+ }
}
// Filter only archfoe bosses
- std::map bossInfo;
- for (auto [infoBossRaceId, infoBossName] : bossMap) {
- const auto mType = getMonsterTypeByBossRaceId(infoBossRaceId);
+ std::vector> bossInfo;
+ for (const auto &[infoBossRaceId, infoBossName] : bossMap) {
+ const auto &mType = getMonsterTypeByBossRaceId(infoBossRaceId);
if (!mType || mType->info.bosstiaryRace != BosstiaryRarity_t::RARITY_ARCHFOE) {
continue;
}
-
- bossInfo.try_emplace(infoBossRaceId, infoBossName);
+ bossInfo.emplace_back(infoBossRaceId, infoBossName);
}
// Check if not have archfoe registered boss
@@ -68,55 +68,39 @@ void IOBosstiary::loadBoostedBoss() {
return;
}
- auto oldBossRace = result->getNumber("raceid");
- while (true) {
- uint32_t randomIndex = uniform_random(0, static_cast(bossInfo.size()));
- auto it = std::next(bossInfo.begin(), randomIndex);
- if (it == bossInfo.end()) {
- break;
- }
-
- const auto &[randomBossId, randomBossName] = *it;
- if (randomBossId == oldBossRace) {
- continue;
- }
-
- bossName = randomBossName;
- bossId = randomBossId;
- break;
+ const auto &[randomBossId, randomBossName] = bossInfo[uniform_random(0, static_cast(bossInfo.size() - 1))];
+ std::string bossName = randomBossName;
+ uint16_t bossId = randomBossId;
+
+ query = fmt::format(
+ "UPDATE `boosted_boss` SET `date` = '{}', `boostname` = {}, ",
+ today, g_database().escapeString(bossName)
+ );
+ if (const auto &bossType = getMonsterTypeByBossRaceId(bossId); bossType) {
+ query += fmt::format(
+ "`looktypeEx` = {}, `looktype` = {}, `lookfeet` = {}, `looklegs` = {}, "
+ "`lookhead` = {}, `lookbody` = {}, `lookaddons` = {}, `lookmount` = {}, ",
+ bossType->info.outfit.lookTypeEx, bossType->info.outfit.lookType,
+ bossType->info.outfit.lookFeet, bossType->info.outfit.lookLegs,
+ bossType->info.outfit.lookHead, bossType->info.outfit.lookBody,
+ bossType->info.outfit.lookAddons, bossType->info.outfit.lookMount
+ );
}
+ query += fmt::format("`raceid` = {}", bossId);
- query.str(std::string());
- query << "UPDATE `boosted_boss` SET ";
- query << "`date` = '" << today << "',";
- query << "`boostname` = " << database.escapeString(bossName) << ",";
- if (const auto bossType = getMonsterTypeByBossRaceId(bossId);
- bossType) {
- query << "`looktypeEx` = " << static_cast(bossType->info.outfit.lookTypeEx) << ",";
- query << "`looktype` = " << static_cast(bossType->info.outfit.lookType) << ",";
- query << "`lookfeet` = " << static_cast(bossType->info.outfit.lookFeet) << ",";
- query << "`looklegs` = " << static_cast(bossType->info.outfit.lookLegs) << ",";
- query << "`lookhead` = " << static_cast(bossType->info.outfit.lookHead) << ",";
- query << "`lookbody` = " << static_cast(bossType->info.outfit.lookBody) << ",";
- query << "`lookaddons` = " << static_cast(bossType->info.outfit.lookAddons) << ",";
- query << "`lookmount` = " << static_cast(bossType->info.outfit.lookMount) << ",";
- }
- query << "`raceid` = '" << bossId << "'";
- if (!database.executeQuery(query.str())) {
- g_logger().error("[{}] Failed to detect boosted boss database. (CODE 03)", __FUNCTION__);
+ if (!g_database().executeQuery(query)) {
+ g_logger().error("[{}] Failed to update boosted boss in g_database(). (CODE 03)", __FUNCTION__);
return;
}
- query.str(std::string());
- query << "UPDATE `player_bosstiary` SET `bossIdSlotOne` = 0 WHERE `bossIdSlotOne` = " << bossId;
- if (!database.executeQuery(query.str())) {
- g_logger().error("[{}] Failed to reset players selected boss slot 1. (CODE 03)", __FUNCTION__);
+ query = fmt::format("UPDATE `player_bosstiary` SET `bossIdSlotOne` = 0 WHERE `bossIdSlotOne` = {}", bossId);
+ if (!g_database().executeQuery(query)) {
+ g_logger().error("[{}] Failed to reset players' selected boss slot 1. (CODE 03)", __FUNCTION__);
}
- query.str(std::string());
- query << "UPDATE `player_bosstiary` SET `bossIdSlotTwo` = 0 WHERE `bossIdSlotTwo` = " << bossId;
- if (!database.executeQuery(query.str())) {
- g_logger().error("[{}] Failed to reset players selected boss slot 1. (CODE 03)", __FUNCTION__);
+ query = fmt::format("UPDATE `player_bosstiary` SET `bossIdSlotTwo` = 0 WHERE `bossIdSlotTwo` = {}", bossId);
+ if (!g_database().executeQuery(query)) {
+ g_logger().error("[{}] Failed to reset players' selected boss slot 2. (CODE 03)", __FUNCTION__);
}
setBossBoostedName(bossName);
From e7c15913401b27b62016892278886df2f7be6737 Mon Sep 17 00:00:00 2001
From: Eduardo Dantas
Date: Sat, 4 Jan 2025 20:58:26 -0300
Subject: [PATCH 18/32] perf: npc/monster storage with vector indexing (#3209)
This modifies the internal storage of NPCs and monsters in the Game class from std::unordered_map to a combination of std::vector and std::unordered_map indexes for improved lookup efficiency and better memory locality.
Motivation
Previously, every time a search was performed for an NPC or a monster, the unordered map was iterated, and even though it provides average O(1) complexity for lookups, the actual cost becomes significant when handling a large dataset (e.g., 80,000 monsters). This cost arises from the overhead of hashing and poor memory locality, as unordered maps store elements in a hash table.
Switching to a vector with index-based lookup improves the following:
1. Memory locality: Vectors store elements contiguously in memory, improving cache efficiency when accessing elements sequentially or repeatedly.
2. Lookup efficiency: Using an unordered map as an index to the vector allows leveraging direct index-based access (constant time) to the actual data, combining the best of both structures.
---
src/creatures/combat/condition.cpp | 4 +
src/creatures/monsters/monster.cpp | 1 +
src/creatures/monsters/monster.hpp | 5 +
src/creatures/npcs/npc.cpp | 4 +
src/creatures/npcs/npc.hpp | 2 +
src/creatures/npcs/npcs.cpp | 3 +
src/creatures/npcs/npcs.hpp | 4 +-
src/game/game.cpp | 132 +++++++++++++-----
src/game/game.hpp | 20 ++-
.../monster/monster_type_functions.cpp | 24 ++--
10 files changed, 146 insertions(+), 53 deletions(-)
diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp
index da90b03f65a..eac70f8ba74 100644
--- a/src/creatures/combat/condition.cpp
+++ b/src/creatures/combat/condition.cpp
@@ -1769,6 +1769,10 @@ bool ConditionDamage::getNextDamage(int32_t &damage) {
}
bool ConditionDamage::doDamage(const std::shared_ptr &creature, int32_t healthChange) const {
+ if (owner == 0) {
+ return false;
+ }
+
const auto &attacker = g_game().getPlayerByGUID(owner) ? g_game().getPlayerByGUID(owner)->getCreature() : g_game().getCreatureByID(owner);
bool isPlayer = attacker && attacker->getPlayer();
if (creature->isSuppress(getType(), isPlayer)) {
diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp
index 13b30321a21..845544d61e9 100644
--- a/src/creatures/monsters/monster.cpp
+++ b/src/creatures/monsters/monster.cpp
@@ -35,6 +35,7 @@ std::shared_ptr Monster::createMonster(const std::string &name) {
}
Monster::Monster(const std::shared_ptr &mType) :
+ m_lowerName(asLowerCaseString(mType->name)),
nameDescription(asLowerCaseString(mType->nameDescription)),
mType(mType) {
defaultOutfit = mType->info.outfit;
diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp
index 6e44d8f36bf..3661eea9bef 100644
--- a/src/creatures/monsters/monster.hpp
+++ b/src/creatures/monsters/monster.hpp
@@ -49,6 +49,10 @@ class Monster final : public Creature {
void setNameDescription(std::string_view nameDescription);
std::string getDescription(int32_t) override;
+ const std::string &getLowerName() const {
+ return m_lowerName;
+ }
+
CreatureType_t getType() const override;
const Position &getMasterPos() const;
@@ -244,6 +248,7 @@ class Monster final : public Creature {
ForgeClassifications_t monsterForgeClassification = ForgeClassifications_t::FORGE_NORMAL_MONSTER;
std::string name;
+ std::string m_lowerName;
std::string nameDescription;
std::shared_ptr mType;
diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp
index e2ab99a1dd4..cdcbd83c6b2 100644
--- a/src/creatures/npcs/npc.cpp
+++ b/src/creatures/npcs/npc.cpp
@@ -97,6 +97,10 @@ void Npc::setName(std::string newName) const {
npcType->name = std::move(newName);
}
+const std::string &Npc::getLowerName() const {
+ return npcType->m_lowerName;
+}
+
CreatureType_t Npc::getType() const {
return CREATURETYPE_NPC;
}
diff --git a/src/creatures/npcs/npc.hpp b/src/creatures/npcs/npc.hpp
index dc3e16961f1..47945b79e99 100644
--- a/src/creatures/npcs/npc.hpp
+++ b/src/creatures/npcs/npc.hpp
@@ -51,6 +51,8 @@ class Npc final : public Creature {
void setName(std::string newName) const;
+ const std::string &getLowerName() const;
+
CreatureType_t getType() const override;
const Position &getMasterPos() const;
diff --git a/src/creatures/npcs/npcs.cpp b/src/creatures/npcs/npcs.cpp
index 53ed757336f..9abc6be6efd 100644
--- a/src/creatures/npcs/npcs.cpp
+++ b/src/creatures/npcs/npcs.cpp
@@ -16,6 +16,9 @@
#include "lua/scripts/scripts.hpp"
#include "lib/di/container.hpp"
+NpcType::NpcType(const std::string &initName) :
+ name(initName), m_lowerName(asLowerCaseString(initName)), typeName(initName), nameDescription(initName) {};
+
bool NpcType::canSpawn(const Position &pos) const {
bool canSpawn = true;
const bool isDay = g_game().gameIsDay();
diff --git a/src/creatures/npcs/npcs.hpp b/src/creatures/npcs/npcs.hpp
index b91e1547c07..d587a17b9fa 100644
--- a/src/creatures/npcs/npcs.hpp
+++ b/src/creatures/npcs/npcs.hpp
@@ -77,14 +77,14 @@ class NpcType final : public SharedObject {
public:
NpcType() = default;
- explicit NpcType(const std::string &initName) :
- name(initName), typeName(initName), nameDescription(initName) {};
+ explicit NpcType(const std::string &initName);
// non-copyable
NpcType(const NpcType &) = delete;
NpcType &operator=(const NpcType &) = delete;
std::string name;
+ std::string m_lowerName;
std::string typeName;
std::string nameDescription;
NpcInfo info;
diff --git a/src/game/game.cpp b/src/game/game.cpp
index 7da1d87fb4a..54a666337d3 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -424,7 +424,7 @@ Game &Game::getInstance() {
}
void Game::resetMonsters() const {
- for (const auto &[monsterId, monster] : getMonsters()) {
+ for (const auto &monster : getMonsters()) {
monster->clearTargetList();
monster->clearFriendList();
}
@@ -432,7 +432,7 @@ void Game::resetMonsters() const {
void Game::resetNpcs() const {
// Close shop window from all npcs and reset the shopPlayerSet
- for (const auto &[npcId, npc] : getNpcs()) {
+ for (const auto &npc : getNpcs()) {
npc->closeAllShopWindows();
npc->resetPlayerInteractions();
}
@@ -954,11 +954,16 @@ std::shared_ptr Game::getMonsterByID(uint32_t id) {
return nullptr;
}
- auto it = monsters.find(id);
- if (it == monsters.end()) {
+ auto it = monstersIdIndex.find(id);
+ if (it == monstersIdIndex.end()) {
return nullptr;
}
- return it->second;
+
+ if (it->second >= monsters.size()) {
+ return nullptr;
+ }
+
+ return monsters[it->second];
}
std::shared_ptr Game::getNpcByID(uint32_t id) {
@@ -966,11 +971,12 @@ std::shared_ptr Game::getNpcByID(uint32_t id) {
return nullptr;
}
- auto it = npcs.find(id);
- if (it == npcs.end()) {
+ auto it = npcsIdIndex.find(id);
+ if (it == npcsIdIndex.end()) {
return nullptr;
}
- return it->second;
+
+ return npcs[it->second];
}
std::shared_ptr Game::getPlayerByID(uint32_t id, bool allowOffline /* = false */) {
@@ -990,43 +996,41 @@ std::shared_ptr Game::getPlayerByID(uint32_t id, bool allowOffline /* =
return tmpPlayer;
}
-std::shared_ptr Game::getCreatureByName(const std::string &s) {
- if (s.empty()) {
+std::shared_ptr Game::getCreatureByName(const std::string &creatureName) {
+ if (creatureName.empty()) {
return nullptr;
}
- const std::string &lowerCaseName = asLowerCaseString(s);
+ const std::string &lowerCaseName = asLowerCaseString(creatureName);
auto m_it = mappedPlayerNames.find(lowerCaseName);
if (m_it != mappedPlayerNames.end()) {
return m_it->second.lock();
}
- for (const auto &it : npcs) {
- if (lowerCaseName == asLowerCaseString(it.second->getName())) {
- return it.second;
- }
+ auto npcIterator = npcsNameIndex.find(lowerCaseName);
+ if (npcIterator != npcsNameIndex.end()) {
+ return npcs[npcIterator->second];
}
- for (const auto &it : monsters) {
- if (lowerCaseName == asLowerCaseString(it.second->getName())) {
- return it.second;
- }
+ auto monsterIterator = monstersNameIndex.find(lowerCaseName);
+ if (monsterIterator != monstersNameIndex.end()) {
+ return monsters[monsterIterator->second];
}
return nullptr;
}
-std::shared_ptr Game::getNpcByName(const std::string &s) {
- if (s.empty()) {
+std::shared_ptr Game::getNpcByName(const std::string &npcName) {
+ if (npcName.empty()) {
return nullptr;
}
- const char* npcName = s.c_str();
- for (const auto &it : npcs) {
- if (strcasecmp(npcName, it.second->getName().c_str()) == 0) {
- return it.second;
- }
+ const std::string lowerCaseName = asLowerCaseString(npcName);
+ auto it = npcsNameIndex.find(lowerCaseName);
+ if (it != npcsNameIndex.end()) {
+ return npcs[it->second];
}
+
return nullptr;
}
@@ -3184,13 +3188,18 @@ ReturnValue Game::internalCollectManagedItems(const std::shared_ptr &pla
ReturnValue Game::collectRewardChestItems(const std::shared_ptr &player, uint32_t maxMoveItems /* = 0*/) {
// Check if have item on player reward chest
- std::shared_ptr rewardChest = player->getRewardChest();
+ const std::shared_ptr &rewardChest = player->getRewardChest();
if (rewardChest->empty()) {
g_logger().debug("Reward chest is empty");
return RETURNVALUE_REWARDCHESTISEMPTY;
}
- auto rewardItemsVector = player->getRewardsFromContainer(rewardChest->getContainer());
+ const auto &container = rewardChest->getContainer();
+ if (!container) {
+ return RETURNVALUE_REWARDCHESTISEMPTY;
+ }
+
+ auto rewardItemsVector = player->getRewardsFromContainer(container);
auto rewardCount = rewardItemsVector.size();
uint32_t movedRewardItems = 0;
std::string lootedItemsMessage;
@@ -9931,19 +9940,72 @@ void Game::removePlayer(const std::shared_ptr &player) {
}
void Game::addNpc(const std::shared_ptr &npc) {
- npcs[npc->getID()] = npc;
+ npcs.push_back(npc);
+ size_t index = npcs.size() - 1;
+ npcsNameIndex[npc->getLowerName()] = index;
+ npcsIdIndex[npc->getID()] = index;
}
void Game::removeNpc(const std::shared_ptr &npc) {
- npcs.erase(npc->getID());
+ if (!npc) {
+ return;
+ }
+
+ auto npcId = npc->getID();
+ const auto &npcLowerName = npc->getLowerName();
+ auto it = npcsIdIndex.find(npcId);
+ if (it != npcsIdIndex.end()) {
+ size_t index = it->second;
+ npcsNameIndex.erase(npcLowerName);
+ npcsIdIndex.erase(npcId);
+
+ if (index != npcs.size() - 1) {
+ std::swap(npcs[index], npcs.back());
+
+ const auto &movedNpc = npcs[index];
+ npcsNameIndex[movedNpc->getLowerName()] = index;
+ npcsIdIndex[movedNpc->getID()] = index;
+ }
+
+ npcs.pop_back();
+ }
}
void Game::addMonster(const std::shared_ptr &monster) {
- monsters[monster->getID()] = monster;
+ if (!monster) {
+ return;
+ }
+
+ const auto &lowerName = monster->getLowerName();
+ monsters.push_back(monster);
+ size_t index = monsters.size() - 1;
+ monstersNameIndex[lowerName] = index;
+ monstersIdIndex[monster->getID()] = index;
}
void Game::removeMonster(const std::shared_ptr &monster) {
- monsters.erase(monster->getID());
+ if (!monster) {
+ return;
+ }
+
+ auto monsterId = monster->getID();
+ const auto &monsterLowerName = monster->getLowerName();
+ auto it = monstersIdIndex.find(monsterId);
+ if (it != monstersIdIndex.end()) {
+ size_t index = it->second;
+ monstersNameIndex.erase(monsterLowerName);
+ monstersIdIndex.erase(monsterId);
+
+ if (index != monsters.size() - 1) {
+ std::swap(monsters[index], monsters.back());
+
+ const auto &movedMonster = monsters[index];
+ monstersNameIndex[movedMonster->getLowerName()] = index;
+ monstersIdIndex[movedMonster->getID()] = index;
+ }
+
+ monsters.pop_back();
+ }
}
std::shared_ptr Game::getGuild(uint32_t id, bool allowOffline /* = flase */) const {
@@ -10152,7 +10214,7 @@ uint32_t Game::makeFiendishMonster(uint32_t forgeableMonsterId /* = 0*/, bool cr
forgeableMonsters.clear();
// If the forgeable monsters haven't been created
// Then we'll create them so they don't return in the next if (forgeableMonsters.empty())
- for (const auto &[monsterId, monster] : monsters) {
+ for (const auto &monster : monsters) {
auto monsterTile = monster->getTile();
if (!monster || !monsterTile) {
continue;
@@ -10325,7 +10387,7 @@ void Game::updateForgeableMonsters() {
if (auto influencedLimit = g_configManager().getNumber(FORGE_INFLUENCED_CREATURES_LIMIT);
forgeableMonsters.size() < influencedLimit) {
forgeableMonsters.clear();
- for (const auto &[monsterId, monster] : monsters) {
+ for (const auto &monster : monsters) {
const auto &monsterTile = monster->getTile();
if (!monsterTile) {
continue;
@@ -10541,7 +10603,7 @@ void Game::playerRewardChestCollect(uint32_t playerId, const Position &pos, uint
}
// Updates the parent of the reward chest and reward containers to avoid memory usage after cleaning
- auto playerRewardChest = player->getRewardChest();
+ const auto &playerRewardChest = player->getRewardChest();
if (playerRewardChest && playerRewardChest->empty()) {
player->sendCancelMessage(RETURNVALUE_REWARDCHESTISEMPTY);
return;
diff --git a/src/game/game.hpp b/src/game/game.hpp
index b16d1787d69..a59c7d13a70 100644
--- a/src/game/game.hpp
+++ b/src/game/game.hpp
@@ -522,10 +522,10 @@ class Game {
const phmap::parallel_flat_hash_map> &getPlayers() const {
return players;
}
- const std::map> &getMonsters() const {
+ const auto &getMonsters() const {
return monsters;
}
- const std::map> &getNpcs() const {
+ const auto &getNpcs() const {
return npcs;
}
@@ -539,8 +539,8 @@ class Game {
void addNpc(const std::shared_ptr &npc);
void removeNpc(const std::shared_ptr &npc);
- void addMonster(const std::shared_ptr &npc);
- void removeMonster(const std::shared_ptr &npc);
+ void addMonster(const std::shared_ptr &monster);
+ void removeMonster(const std::shared_ptr &monster);
std::shared_ptr getGuild(uint32_t id, bool allowOffline = false) const;
std::shared_ptr getGuildByName(const std::string &name, bool allowOffline = false) const;
@@ -851,8 +851,16 @@ class Game {
std::shared_ptr wildcardTree = nullptr;
- std::map> npcs;
- std::map> monsters;
+ std::vector> monsters;
+ // This works only for unique monsters (bosses, quest monsters, etc)
+ std::unordered_map monstersNameIndex;
+ std::unordered_map monstersIdIndex;
+
+ std::vector> npcs;
+ // This works only for unique npcs (quest npcs, etc)
+ std::unordered_map npcsNameIndex;
+ std::unordered_map npcsIdIndex;
+
std::vector forgeableMonsters;
std::map> teamFinderMap; // [leaderGUID] = TeamFinder*
diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp
index 07ce44b626c..8b2f4a5f314 100644
--- a/src/lua/functions/creatures/monster/monster_type_functions.cpp
+++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp
@@ -1170,18 +1170,22 @@ int MonsterTypeFunctions::luaMonsterTypeGetCreatureEvents(lua_State* L) {
int MonsterTypeFunctions::luaMonsterTypeRegisterEvent(lua_State* L) {
// monsterType:registerEvent(name)
const auto &monsterType = Lua::getUserdataShared(L, 1);
- if (monsterType) {
- const auto eventName = Lua::getString(L, 2);
- monsterType->info.scripts.insert(eventName);
- for (const auto &[_, monster] : g_game().getMonsters()) {
- if (monster->getMonsterType() == monsterType) {
- monster->registerCreatureEvent(eventName);
- }
- }
- Lua::pushBoolean(L, true);
- } else {
+ if (!monsterType) {
lua_pushnil(L);
+ return 1;
}
+
+ const auto eventName = Lua::getString(L, 2);
+ monsterType->info.scripts.insert(eventName);
+
+ for (const auto &monster : g_game().getMonsters()) {
+ const auto monsterTypeCompare = monster->getMonsterType();
+ if (monsterTypeCompare == monsterType) {
+ monster->registerCreatureEvent(eventName);
+ }
+ }
+
+ Lua::pushBoolean(L, true);
return 1;
}
From 53015a3a11dbadb1547589037cfe521356dd278c Mon Sep 17 00:00:00 2001
From: Eduardo Dantas
Date: Sat, 4 Jan 2025 20:59:44 -0300
Subject: [PATCH 19/32] perf: change zone positions to phmap::flat_hash_set
(#3210)
This replaces `std::unordered_set` with `phmap::flat_hash_set` in the
`Zone` class to improve performance by reducing hash collisions and
optimizing memory usage.
Motivation:
The use of `std::unordered_set` for the `positions` attribute caused
performance issues due to hash collisions, especially when managing
large datasets or heavily clustered data. Collisions result in increased
lookup, insertion, and deletion times as the hash table degrades into a
linked list traversal for conflicting buckets.
Switching to `phmap::flat_hash_set` provides several benefits:
1. Reduced hash collisions: The hashing strategy used by
`phmap::flat_hash_set` significantly reduces the likelihood of
collisions, improving lookup and insertion performance.
2. Improved memory locality: Unlike `std::unordered_set`, which uses
separate allocations for each bucket, `flat_hash_set` uses a contiguous
memory layout, enhancing cache efficiency and reducing overhead.
3. Faster operations: Benchmarks show that `flat_hash_set`
outperforms `std::unordered_set` in scenarios with frequent insertions,
lookups, and deletions due to its optimized design.
---
src/game/zones/zone.hpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/game/zones/zone.hpp b/src/game/zones/zone.hpp
index a3af961bd7f..e13652e8b32 100644
--- a/src/game/zones/zone.hpp
+++ b/src/game/zones/zone.hpp
@@ -216,7 +216,7 @@ class Zone {
Position removeDestination = Position();
std::string name;
std::string monsterVariant;
- std::unordered_set positions;
+ phmap::flat_hash_set positions;
uint32_t id = 0; // ID 0 is used in zones created dynamically from lua. The map editor uses IDs starting from 1 (automatically generated).
weak::set
- itemsCache;
From 2ca896ee665894f8253ef4ab9c8b835c592845c5 Mon Sep 17 00:00:00 2001
From: Eduardo Dantas
Date: Sat, 4 Jan 2025 21:00:56 -0300
Subject: [PATCH 20/32] fix: concurrency crash in closeAllShopWindows caused by
use-after-free (#3211)
This fixes a concurrency crash in the `closeAllShopWindows` method.
The issue occurred when the `shopPlayers` map was cleared while being iterated over, leading to a use-after-free scenario. The solution removes the use of references when iterating over `shopPlayers` keys, ensuring that the keys are copied instead of directly referencing the map. This change prevents crashes and improves stability in concurrent environments.
---
src/creatures/npcs/npc.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp
index cdcbd83c6b2..65bec559872 100644
--- a/src/creatures/npcs/npc.cpp
+++ b/src/creatures/npcs/npc.cpp
@@ -812,7 +812,7 @@ void Npc::removeShopPlayer(uint32_t playerGUID) {
}
void Npc::closeAllShopWindows() {
- for (const auto &playerGUID : shopPlayers | std::views::keys) {
+ for (const auto playerGUID : shopPlayers | std::views::keys) {
const auto &player = g_game().getPlayerByGUID(playerGUID);
if (player) {
player->closeShopWindow();
From 47780781797e766fb53df84a7a3524b150f29492 Mon Sep 17 00:00:00 2001
From: Eduardo Dantas
Date: Sat, 4 Jan 2025 21:01:56 -0300
Subject: [PATCH 21/32] perf: optimize condition creation with ObjectPool
(#3212)
This introduces an ObjectPool for managing Condition objects, replacing frequent calls to std::make_shared with a lock-free pooling allocator. This optimization reduces memory allocation overhead, improves performance, and enhances thread safety in scenarios where Conditions are created and destroyed frequently, such as in onThink events or condition updates.
By reusing objects from the pool instead of allocating and deallocating memory repeatedly, this change significantly reduces the strain on the memory management system and improves runtime efficiency. The pool is designed to handle up to 1024 objects per Condition type and supports safe, high-performance multithreaded operations.
---
src/creatures/combat/condition.cpp | 33 ++++++------
src/utils/object_pool.hpp | 84 ++++++++++++++++++++++++++++++
2 files changed, 102 insertions(+), 15 deletions(-)
create mode 100644 src/utils/object_pool.hpp
diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp
index eac70f8ba74..e5219126025 100644
--- a/src/creatures/combat/condition.cpp
+++ b/src/creatures/combat/condition.cpp
@@ -21,6 +21,7 @@
#include "creatures/creature.hpp"
#include "creatures/players/player.hpp"
#include "server/network/protocol/protocolgame.hpp"
+#include "utils/object_pool.hpp"
/**
* Condition
@@ -215,41 +216,41 @@ std::shared_ptr Condition::createCondition(ConditionId_t id, Conditio
case CONDITION_DAZZLED:
case CONDITION_CURSED:
case CONDITION_BLEEDING:
- return std::make_shared(id, type, buff, subId);
+ return ObjectPool::allocateShared(id, type, buff, subId);
case CONDITION_HASTE:
case CONDITION_PARALYZE:
- return std::make_shared(id, type, ticks, buff, subId, param);
+ return ObjectPool::allocateShared(id, type, ticks, buff, subId, param);
case CONDITION_INVISIBLE:
- return std::make_shared(id, type, ticks, buff, subId);
+ return ObjectPool::allocateShared(id, type, ticks, buff, subId);
case CONDITION_OUTFIT:
- return std::make_shared(id, type, ticks, buff, subId);
+ return ObjectPool::allocateShared(id, type, ticks, buff, subId);
case CONDITION_LIGHT:
- return std::make_shared(id, type, ticks, buff, subId, param & 0xFF, (param & 0xFF00) >> 8);
+ return ObjectPool::allocateShared(id, type, ticks, buff, subId, param & 0xFF, (param & 0xFF00) >> 8);
case CONDITION_REGENERATION:
- return std::make_shared(id, type, ticks, buff, subId);
+ return ObjectPool::allocateShared(id, type, ticks, buff, subId);
case CONDITION_SOUL:
- return std::make_shared(id, type, ticks, buff, subId);
+ return ObjectPool::allocateShared(id, type, ticks, buff, subId);
case CONDITION_ATTRIBUTES:
- return std::make_shared(id, type, ticks, buff, subId);
+ return ObjectPool::allocateShared(id, type, ticks, buff, subId);
case CONDITION_SPELLCOOLDOWN:
- return std::make_shared(id, type, ticks, buff, subId);
+ return ObjectPool::allocateShared(id, type, ticks, buff, subId);
case CONDITION_SPELLGROUPCOOLDOWN:
- return std::make_shared(id, type, ticks, buff, subId);
+ return ObjectPool::allocateShared(id, type, ticks, buff, subId);
case CONDITION_MANASHIELD:
- return std::make_shared(id, type, ticks, buff, subId);
+ return ObjectPool::allocateShared(id, type, ticks, buff, subId);
case CONDITION_FEARED:
- return std::make_shared(id, type, ticks, buff, subId);
+ return ObjectPool::allocateShared(id, type, ticks, buff, subId);
case CONDITION_ROOTED:
case CONDITION_INFIGHT:
@@ -261,11 +262,13 @@ std::shared_ptr Condition::createCondition(ConditionId_t id, Conditio
case CONDITION_CHANNELMUTEDTICKS:
case CONDITION_YELLTICKS:
case CONDITION_PACIFIED:
- return std::make_shared(id, type, ticks, buff, subId);
+ return ObjectPool::allocateShared(id, type, ticks, buff, subId);
+
case CONDITION_BAKRAGORE:
- return std::make_shared(id, type, ticks, buff, subId, isPersistent);
+ return ObjectPool::allocateShared(id, type, ticks, buff, subId, isPersistent);
+
case CONDITION_GOSHNARTAINT:
- return std::make_shared(id, type, ticks, buff, subId);
+ return ObjectPool::allocateShared(id, type, ticks, buff, subId);
default:
return nullptr;
diff --git a/src/utils/object_pool.hpp b/src/utils/object_pool.hpp
new file mode 100644
index 00000000000..c37d1d2efeb
--- /dev/null
+++ b/src/utils/object_pool.hpp
@@ -0,0 +1,84 @@
+/**
+ * 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
+
+#include "utils/lockfree.hpp"
+
+/**
+ * @brief A lock-free object pool for efficient memory allocation and reuse.
+ *
+ * This class provides an efficient mechanism for managing the allocation
+ * and deallocation of objects, reducing the overhead associated with
+ * frequent memory operations. It uses a lock-free structure to ensure
+ * thread safety and high performance in multithreaded environments.
+ *
+ * @tparam T The type of objects managed by the pool.
+ * @tparam CAPACITY The maximum number of objects that can be held in the pool.
+ */
+template
+class ObjectPool {
+public:
+ /**
+ * @brief The allocator type used for managing object memory.
+ */
+ using Allocator = LockfreePoolingAllocator;
+
+ /**
+ * @brief Allocates an object from the pool and returns it as a `std::shared_ptr`.
+ *
+ * The object is constructed in place using the provided arguments.
+ * The `std::shared_ptr` includes a custom deleter that returns the object
+ * to the pool when it is no longer needed.
+ *
+ * @tparam Args The types of the arguments used to construct the object.* @param args The arguments forwarded to the constructor of the object.
+ * @return A `std::shared_ptr` managing the allocated object, or `nullptr` if the pool is empty.
+ */
+ template
+ static std::shared_ptr allocateShared(Args &&... args) {
+ T* obj = allocator.allocate(1);
+ if (obj) {
+ // Construct the object in place
+ std::construct_at(obj, std::forward(args)...);
+
+ // Return a shared_ptr with a custom deleter
+ return std::shared_ptr(obj, [](T* ptr) {
+ std::destroy_at(ptr); // Destroy the object
+ allocator.deallocate(ptr, 1); // Return to the pool
+ });
+ }
+ // Return nullptr if the pool is empty
+ return nullptr;
+ }
+
+ static void clear() {
+ allocator.clear();
+ }
+
+ /**
+ * @brief Preallocates a specified number of objects in the pool.
+ *
+ * This method allows you to populate the pool with preallocated objects
+ * to improve performance by reducing the need for dynamic allocations at runtime.
+ *
+ * @param count The number of objects to preallocate.
+ */
+ static void preallocate(size_t count) {
+ LockfreeFreeList