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::preallocate(count); + } + +private: + /** + * @brief The allocator instance used to manage object memory. + */ + static Allocator allocator; +}; + +template +typename ObjectPool::Allocator ObjectPool::allocator; From 34717832e2470fe17cf6f3820bbec8e330851ecd Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Sat, 4 Jan 2025 21:02:37 -0300 Subject: [PATCH 22/32] fix: add null check for moveFunction to prevent crashes (#3213) This adds a null check for the moveFunction in the MoveEvent::fireAddRemItem method. Previously, the code accessed moveFunction directly without verifying its validity, which could lead to crashes if it was nullptr. The fix ensures stability by: Logging an error message when moveFunction is null. Returning early to prevent further execution with an invalid function pointer. This change improves the reliability of the function, particularly in scenarios where moveFunction might not be properly initialized. --- src/lua/creature/movement.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/lua/creature/movement.cpp b/src/lua/creature/movement.cpp index 6804cdfa7e9..51daea8cf1e 100644 --- a/src/lua/creature/movement.cpp +++ b/src/lua/creature/movement.cpp @@ -808,6 +808,13 @@ uint32_t MoveEvent::fireAddRemItem(const std::shared_ptr &item, const std: if (isLoadedScriptId()) { return executeAddRemItem(item, fromTile, pos); } else { + if (!moveFunction) { + g_logger().error("[MoveEvent::fireAddRemItem - Item {} item on position: {}] " + "Move function is nullptr.", + item->getName(), pos.toString()); + return 0; + } + return moveFunction(item, fromTile, pos); } } @@ -840,6 +847,13 @@ uint32_t MoveEvent::fireAddRemItem(const std::shared_ptr &item, const Posi if (isLoadedScriptId()) { return executeAddRemItem(item, pos); } else { + if (!moveFunction) { + g_logger().error("[MoveEvent::fireAddRemItem - Item {} item on position: {}] " + "Move function is nullptr.", + item->getName(), pos.toString()); + return 0; + } + return moveFunction(item, nullptr, pos); } } @@ -849,9 +863,9 @@ bool MoveEvent::executeAddRemItem(const std::shared_ptr &item, const Posit // onRemoveItem(moveitem, pos) if (!LuaScriptInterface::reserveScriptEnv()) { g_logger().error("[MoveEvent::executeAddRemItem - " - "Item {} item on tile x: {} y: {} z: {}] " + "Item {} item on position: {}] " "Call stack overflow. Too many lua script calls being nested.", - item->getName(), pos.getX(), pos.getY(), pos.getZ()); + item->getName(), pos.toString()); return false; } From b8ce7fc1afefbd1b16cd8830a620e4831ac2f688 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Sat, 4 Jan 2025 21:09:21 -0300 Subject: [PATCH 23/32] fix: crash on daily reward (#3215) --- src/creatures/combat/condition.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index e5219126025..adbb31606e9 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -1273,7 +1273,10 @@ bool ConditionRegeneration::executeCondition(const std::shared_ptr &cr const auto &player = creature->getPlayer(); int32_t dailyStreak = 0; if (player) { - dailyStreak = static_cast(player->kv()->scoped("daily-reward")->get("streak")->getNumber()); + auto optStreak = player->kv()->scoped("daily-reward")->get("streak"); + if (optStreak) { + dailyStreak = static_cast(optStreak->getNumber()); + } } if (creature->getZoneType() != ZONE_PROTECTION || dailyStreak >= DAILY_REWARD_HP_REGENERATION) { if (internalHealthTicks >= getHealthTicks(creature)) { From cc3a2465b2258c9b2493b33d5c4ee05869c6216d Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Sun, 5 Jan 2025 12:51:38 -0300 Subject: [PATCH 24/32] perf: replace SELECT * with specific columns in SQL queries (#3214) This replaces all occurrences of `SELECT *` in SQL queries with explicit column names. The use of `SELECT *` often retrieves unnecessary data, increasing query processing time and bandwidth usage. By specifying the required columns, this change improves performance and reduces resource usage. --- src/account/account_repository_db.cpp | 2 +- .../players/cyclopedia/player_title.cpp | 4 +- src/game/game.cpp | 62 ++++++++++++------ src/io/functions/iologindata_load_player.cpp | 14 ++-- src/io/io_bosstiary.cpp | 64 ++++++++++++------- 5 files changed, 97 insertions(+), 49 deletions(-) diff --git a/src/account/account_repository_db.cpp b/src/account/account_repository_db.cpp index b2e8fd80754..b46d1941779 100644 --- a/src/account/account_repository_db.cpp +++ b/src/account/account_repository_db.cpp @@ -76,7 +76,7 @@ bool AccountRepositoryDB::getCharacterByAccountIdAndName(const uint32_t &id, con } bool AccountRepositoryDB::getPassword(const uint32_t &id, std::string &password) { - auto result = g_database().storeQuery(fmt::format("SELECT * FROM `accounts` WHERE `id` = {}", id)); + auto result = g_database().storeQuery(fmt::format("SELECT `password` FROM `accounts` WHERE `id` = {}", id)); if (!result) { g_logger().error("Failed to get account:[{}] password!", id); return false; diff --git a/src/creatures/players/cyclopedia/player_title.cpp b/src/creatures/players/cyclopedia/player_title.cpp index 089d02f281c..c7e1c8f04f6 100644 --- a/src/creatures/players/cyclopedia/player_title.cpp +++ b/src/creatures/players/cyclopedia/player_title.cpp @@ -224,7 +224,9 @@ bool PlayerTitle::checkHighscore(uint8_t skill) const { default: std::string skillName = g_game().getSkillNameById(skill); query = fmt::format( - "SELECT * FROM `players` WHERE `group_id` < {} AND `{}` > 10 ORDER BY `{}` DESC LIMIT 1", + "SELECT `id` FROM `players` " + "WHERE `group_id` < {} AND `{}` > 10 " + "ORDER BY `{}` DESC LIMIT 1", static_cast(GROUP_TYPE_GAMEMASTER), skillName, skillName ); break; diff --git a/src/game/game.cpp b/src/game/game.cpp index 54a666337d3..b3d558a64d1 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -440,7 +440,10 @@ void Game::resetNpcs() const { void Game::loadBoostedCreature() { auto &db = Database::getInstance(); - const auto result = db.storeQuery("SELECT * FROM `boosted_creature`"); + const auto result = db.storeQuery( + "SELECT `date`, `boostname`, `raceid`, `looktype`, `lookfeet`, `looklegs`, `lookhead`, `lookbody`, `lookaddons`, `lookmount` " + "FROM `boosted_creature`" + ); if (!result) { g_logger().warn("[Game::loadBoostedCreature] - " "Failed to detect boosted creature database. (CODE 01)"); @@ -8514,38 +8517,61 @@ void Game::playerCyclopediaCharacterInfo(const std::shared_ptr &player, } std::string Game::generateHighscoreQueryForEntries(const std::string &categoryName, uint32_t page, uint8_t entriesPerPage, uint32_t vocation) { - std::ostringstream query; - uint32_t startPage = (static_cast(page - 1) * static_cast(entriesPerPage)); + uint32_t startPage = (page - 1) * static_cast(entriesPerPage); uint32_t endPage = startPage + static_cast(entriesPerPage); - query << "SELECT *, @row AS `entries`, " << page << " AS `page` FROM (SELECT *, (@row := @row + 1) AS `rn` FROM (SELECT `id`, `name`, `level`, `vocation`, `" - << categoryName << "` AS `points`, @curRank := IF(@prevRank = `" << categoryName << "`, @curRank, IF(@prevRank := `" << categoryName - << "`, @curRank + 1, @curRank + 1)) AS `rank` FROM `players` `p`, (SELECT @curRank := 0, @prevRank := NULL, @row := 0) `r` WHERE `group_id` < " - << static_cast(GROUP_TYPE_GAMEMASTER) << " ORDER BY `" << categoryName << "` DESC) `t`"; + Database &db = Database::getInstance(); + std::string escapedCategoryName = db.escapeString(categoryName); + + std::string query = fmt::format( + "SELECT `id`, `name`, `level`, `vocation`, `points`, `rank`, `entries`, {} AS `page` FROM (" + "SELECT `id`, `name`, `level`, `vocation`, `{}` AS `points`, " + "@curRank := IF(@prevRank = `{}`, @curRank, IF(@prevRank := `{}`, @curRank + 1, @curRank + 1)) AS `rank`, " + "(@row := @row + 1) AS `entries` FROM (" + "SELECT `id`, `name`, `level`, `vocation`, `{}` FROM `players` `p`, " + "(SELECT @curRank := 0, @prevRank := NULL, @row := 0) `r` " + "WHERE `group_id` < {} ORDER BY `{}` DESC" + ") `t`", + page, escapedCategoryName, escapedCategoryName, escapedCategoryName, escapedCategoryName, static_cast(GROUP_TYPE_GAMEMASTER), escapedCategoryName + ); if (vocation != 0xFFFFFFFF) { - query << generateVocationConditionHighscore(vocation); + query += generateVocationConditionHighscore(vocation); } - query << ") `T` WHERE `rn` > " << startPage << " AND `rn` <= " << endPage; - return query.str(); + query += fmt::format(") `T` WHERE `entries` > {} AND `entries` <= {}", startPage, endPage); + + return query; } std::string Game::generateHighscoreQueryForOurRank(const std::string &categoryName, uint8_t entriesPerPage, uint32_t playerGUID, uint32_t vocation) { - std::ostringstream query; + Database &db = Database::getInstance(); + std::string escapedCategoryName = db.escapeString(categoryName); std::string entriesStr = std::to_string(entriesPerPage); - query << "SELECT *, @row AS `entries`, (@ourRow DIV " << entriesStr << ") + 1 AS `page` FROM (SELECT *, (@row := @row + 1) AS `rn`, @ourRow := IF(`id` = " - << playerGUID << ", @row - 1, @ourRow) AS `rw` FROM (SELECT `id`, `name`, `level`, `vocation`, `" << categoryName << "` AS `points`, @curRank := IF(@prevRank = `" - << categoryName << "`, @curRank, IF(@prevRank := `" << categoryName << "`, @curRank + 1, @curRank + 1)) AS `rank` FROM `players` `p`, (SELECT @curRank := 0, @prevRank := NULL, @row := 0, @ourRow := 0) `r` WHERE `group_id` < " - << static_cast(GROUP_TYPE_GAMEMASTER) << " ORDER BY `" << categoryName << "` DESC) `t`"; + std::string query = fmt::format( + "SELECT `id`, `name`, `level`, `vocation`, `points`, `rank`, @row AS `entries`, " + "(@ourRow DIV {0}) + 1 AS `page` FROM (" + "SELECT `id`, `name`, `level`, `vocation`, `{1}` AS `points`, " + "@curRank := IF(@prevRank = `{1}`, @curRank, IF(@prevRank := `{1}`, @curRank + 1, @curRank + 1)) AS `rank`, " + "(@row := @row + 1) AS `rn`, @ourRow := IF(`id` = {2}, @row - 1, @ourRow) AS `rw` FROM (" + "SELECT `id`, `name`, `level`, `vocation`, `{1}` FROM `players` `p`, " + "(SELECT @curRank := 0, @prevRank := NULL, @row := 0, @ourRow := 0) `r` " + "WHERE `group_id` < {3} ORDER BY `{1}` DESC" + ") `t`", + entriesStr, escapedCategoryName, playerGUID, static_cast(GROUP_TYPE_GAMEMASTER) + ); if (vocation != 0xFFFFFFFF) { - query << generateVocationConditionHighscore(vocation); + query += generateVocationConditionHighscore(vocation); } - query << ") `T` WHERE `rn` > ((@ourRow DIV " << entriesStr << ") * " << entriesStr << ") AND `rn` <= (((@ourRow DIV " << entriesStr << ") * " << entriesStr << ") + " << entriesStr << ")"; - return query.str(); + query += fmt::format( + ") `T` WHERE `rn` > ((@ourRow DIV {0}) * {0}) AND `rn` <= (((@ourRow DIV {0}) * {0}) + {0})", + entriesStr + ); + + return query; } std::string Game::generateVocationConditionHighscore(uint32_t vocation) { diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index e724d0ceda9..21d251c6dbe 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -869,14 +869,18 @@ void IOLoginDataLoad::loadPlayerTaskHuntingClass(const std::shared_ptr & } void IOLoginDataLoad::loadPlayerForgeHistory(const std::shared_ptr &player, DBResult_ptr result) { - if (!result || !player) { - g_logger().warn("[{}] - Player or Result nullptr", __FUNCTION__); + if (!player) { + g_logger().warn("[{}] - Player nullptr", __FUNCTION__); return; } - std::ostringstream query; - query << "SELECT * FROM `forge_history` WHERE `player_id` = " << player->getGUID(); - if ((result = Database::getInstance().storeQuery(query.str()))) { + auto playerGUID = player->getGUID(); + + auto query = fmt::format( + "SELECT id, action_type, description, done_at, is_success FROM forge_history WHERE player_id = {}", + playerGUID + ); + if ((result = Database::getInstance().storeQuery(query))) { do { auto actionEnum = magic_enum::enum_value(result->getNumber("action_type")); ForgeHistory history; diff --git a/src/io/io_bosstiary.cpp b/src/io/io_bosstiary.cpp index 57a9a85e294..80634d11b42 100644 --- a/src/io/io_bosstiary.cpp +++ b/src/io/io_bosstiary.cpp @@ -20,14 +20,15 @@ IOBosstiary &IOBosstiary::getInstance() { } void IOBosstiary::loadBoostedBoss() { - 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); + Database &database = Database::getInstance(); + auto query = fmt::format("SELECT `date`, `boostname`, `raceid` FROM `boosted_boss`"); + DBResult_ptr result = database.storeQuery(query); + if (!result) { + g_logger().error("[{}] Failed to detect boosted boss database. (CODE 01)", __FUNCTION__); + return; + } + + auto date = result->getNumber("date"); auto timeNow = getTimeNow(); auto time = localtime(&timeNow); auto today = time->tm_mday; @@ -73,34 +74,49 @@ void IOBosstiary::loadBoostedBoss() { uint16_t bossId = randomBossId; query = fmt::format( - "UPDATE `boosted_boss` SET `date` = '{}', `boostname` = {}, ", - today, g_database().escapeString(bossName) + "UPDATE `boosted_boss` SET `date` = '{}', `boostname` = {}, `raceid` = '{}', ", + today, database.escapeString(bossName), bossId ); - if (const auto &bossType = getMonsterTypeByBossRaceId(bossId); bossType) { + + 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 + static_cast(bossType->info.outfit.lookTypeEx), + static_cast(bossType->info.outfit.lookType), + static_cast(bossType->info.outfit.lookFeet), + static_cast(bossType->info.outfit.lookLegs), + static_cast(bossType->info.outfit.lookHead), + static_cast(bossType->info.outfit.lookBody), + static_cast(bossType->info.outfit.lookAddons), + static_cast(bossType->info.outfit.lookMount) ); } - query += fmt::format("`raceid` = {}", bossId); - if (!g_database().executeQuery(query)) { - g_logger().error("[{}] Failed to update boosted boss in g_database(). (CODE 03)", __FUNCTION__); + query += fmt::format("`raceid` = '{}'", bossId); + + if (!database.executeQuery(query)) { + g_logger().error("[{}] Failed to detect boosted boss database. (CODE 03)", __FUNCTION__); return; } - 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 = fmt::format( + "UPDATE `player_bosstiary` SET `bossIdSlotOne` = 0 WHERE `bossIdSlotOne` = {}", + bossId + ); + + if (!database.executeQuery(query)) { + 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__); + query = fmt::format( + "UPDATE `player_bosstiary` SET `bossIdSlotTwo` = 0 WHERE `bossIdSlotTwo` = {}", + bossId + ); + + if (!database.executeQuery(query)) { + g_logger().error("[{}] Failed to reset players selected boss slot 2. (CODE 03)", __FUNCTION__); + return; } setBossBoostedName(bossName); From 0e188a995639d234a5fc2a11773f80eea88742ee Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Sun, 5 Jan 2025 15:08:09 -0300 Subject: [PATCH 25/32] perf: fixes exhaustion to 'playerEquipItem' (#3165) --- src/game/game.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/game/game.cpp b/src/game/game.cpp index b3d558a64d1..e692a3ee3ff 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -3383,6 +3383,21 @@ void Game::playerEquipItem(uint32_t playerId, uint16_t itemId, bool hasTier /* = return; } + if (!player->canDoAction()) { + uint32_t delay = player->getNextActionTime() - OTSYS_TIME(); + if (delay > 0) { + const auto &task = createPlayerTask( + delay, + [this, playerId, itemId, hasTier, tier] { + playerEquipItem(playerId, itemId, hasTier, tier); + }, + __FUNCTION__ + ); + player->setNextActionTask(task); + } + return; + } + if (player->hasCondition(CONDITION_FEARED)) { /* * When player is feared the player can´t equip any items. @@ -3480,6 +3495,8 @@ void Game::playerEquipItem(uint32_t playerId, uint16_t itemId, bool hasTier /* = if (ret != RETURNVALUE_NOERROR) { player->sendCancelMessage(ret); } + + player->setNextAction(OTSYS_TIME() + g_configManager().getNumber(ACTIONS_DELAY_INTERVAL)); } void Game::playerMove(uint32_t playerId, Direction direction) { From 8aac2a02975c97e8c94738f674fa963534f392d5 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Sun, 5 Jan 2025 15:15:54 -0300 Subject: [PATCH 26/32] fix: shadowing "date", move to the correct place (#3219) --- src/io/io_bosstiary.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/io/io_bosstiary.cpp b/src/io/io_bosstiary.cpp index 80634d11b42..351f8017718 100644 --- a/src/io/io_bosstiary.cpp +++ b/src/io/io_bosstiary.cpp @@ -28,17 +28,16 @@ void IOBosstiary::loadBoostedBoss() { return; } - auto date = result->getNumber("date"); - auto timeNow = getTimeNow(); - auto time = localtime(&timeNow); - auto today = time->tm_mday; - 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; } + auto timeNow = getTimeNow(); + auto time = localtime(&timeNow); + auto today = time->tm_mday; + if (!result) { g_logger().warn("[{}] No boosted boss found in g_database(). A new one will be selected.", __FUNCTION__); } else { From f2533739c1f089092283080371f8b9ea38474e67 Mon Sep 17 00:00:00 2001 From: Marco Date: Tue, 7 Jan 2025 13:40:56 -0300 Subject: [PATCH 27/32] fix: ensure isPodium check works correctly (#3221) --- src/lua/functions/items/item_type_functions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lua/functions/items/item_type_functions.cpp b/src/lua/functions/items/item_type_functions.cpp index 9c12f80e586..a1751ed4f15 100644 --- a/src/lua/functions/items/item_type_functions.cpp +++ b/src/lua/functions/items/item_type_functions.cpp @@ -34,6 +34,7 @@ void ItemTypeFunctions::init(lua_State* L) { Lua::registerMethod(L, "ItemType", "isPickupable", ItemTypeFunctions::luaItemTypeIsPickupable); Lua::registerMethod(L, "ItemType", "isKey", ItemTypeFunctions::luaItemTypeIsKey); Lua::registerMethod(L, "ItemType", "isQuiver", ItemTypeFunctions::luaItemTypeIsQuiver); + Lua::registerMethod(L, "ItemType", "isPodium", ItemTypeFunctions::luaItemTypeIsPodium); Lua::registerMethod(L, "ItemType", "getType", ItemTypeFunctions::luaItemTypeGetType); Lua::registerMethod(L, "ItemType", "getId", ItemTypeFunctions::luaItemTypeGetId); From 015169e07367880317f67000ec920cc238315d3f Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Wed, 8 Jan 2025 08:28:20 -0300 Subject: [PATCH 28/32] fix: highscore sql query (#3222) --- src/game/game.cpp | 80 +++++++++++++++++++++++++---------------------- src/game/game.hpp | 9 ++++-- 2 files changed, 49 insertions(+), 40 deletions(-) diff --git a/src/game/game.cpp b/src/game/game.cpp index e692a3ee3ff..5ce501c8288 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -8533,60 +8533,64 @@ void Game::playerCyclopediaCharacterInfo(const std::shared_ptr &player, } } -std::string Game::generateHighscoreQueryForEntries(const std::string &categoryName, uint32_t page, uint8_t entriesPerPage, uint32_t vocation) { +std::string Game::generateHighscoreQuery( + const std::string &categoryName, + uint32_t page, + uint8_t entriesPerPage, + uint32_t vocation, + uint32_t playerGUID /*= 0*/ +) { uint32_t startPage = (page - 1) * static_cast(entriesPerPage); uint32_t endPage = startPage + static_cast(entriesPerPage); + std::string entriesStr = std::to_string(entriesPerPage); - Database &db = Database::getInstance(); - std::string escapedCategoryName = db.escapeString(categoryName); + if (categoryName.empty()) { + g_logger().error("Category name cannot be empty."); + return ""; + } std::string query = fmt::format( - "SELECT `id`, `name`, `level`, `vocation`, `points`, `rank`, `entries`, {} AS `page` FROM (" - "SELECT `id`, `name`, `level`, `vocation`, `{}` AS `points`, " - "@curRank := IF(@prevRank = `{}`, @curRank, IF(@prevRank := `{}`, @curRank + 1, @curRank + 1)) AS `rank`, " - "(@row := @row + 1) AS `entries` FROM (" - "SELECT `id`, `name`, `level`, `vocation`, `{}` FROM `players` `p`, " - "(SELECT @curRank := 0, @prevRank := NULL, @row := 0) `r` " - "WHERE `group_id` < {} ORDER BY `{}` DESC" - ") `t`", - page, escapedCategoryName, escapedCategoryName, escapedCategoryName, escapedCategoryName, static_cast(GROUP_TYPE_GAMEMASTER), escapedCategoryName + "SELECT `id`, `name`, `level`, `vocation`, `points`, `rank`, `rn` AS `entries`, " ); - if (vocation != 0xFFFFFFFF) { - query += generateVocationConditionHighscore(vocation); + if (playerGUID != 0) { + query += fmt::format("(@ourRow DIV {0}) + 1 AS `page` FROM (", entriesStr); + } else { + query += fmt::format("{} AS `page` FROM (", page); } - query += fmt::format(") `T` WHERE `entries` > {} AND `entries` <= {}", startPage, endPage); - - return query; -} + query += fmt::format( + "SELECT `id`, `name`, `level`, `vocation`, `{}` AS `points`, " + "@curRank := IF(@prevRank = `{}`, @curRank, IF(@prevRank := `{}`, @curRank + 1, @curRank + 1)) AS `rank`, " + "(@row := @row + 1) AS `rn`", + categoryName, categoryName, categoryName + ); -std::string Game::generateHighscoreQueryForOurRank(const std::string &categoryName, uint8_t entriesPerPage, uint32_t playerGUID, uint32_t vocation) { - Database &db = Database::getInstance(); - std::string escapedCategoryName = db.escapeString(categoryName); - std::string entriesStr = std::to_string(entriesPerPage); + if (playerGUID != 0) { + query += fmt::format(", @ourRow := IF(`id` = {}, @row - 1, @ourRow) AS `rw`", playerGUID); + } - std::string query = fmt::format( - "SELECT `id`, `name`, `level`, `vocation`, `points`, `rank`, @row AS `entries`, " - "(@ourRow DIV {0}) + 1 AS `page` FROM (" - "SELECT `id`, `name`, `level`, `vocation`, `{1}` AS `points`, " - "@curRank := IF(@prevRank = `{1}`, @curRank, IF(@prevRank := `{1}`, @curRank + 1, @curRank + 1)) AS `rank`, " - "(@row := @row + 1) AS `rn`, @ourRow := IF(`id` = {2}, @row - 1, @ourRow) AS `rw` FROM (" - "SELECT `id`, `name`, `level`, `vocation`, `{1}` FROM `players` `p`, " + query += fmt::format( + " FROM (SELECT `id`, `name`, `level`, `vocation`, `{}` FROM `players` `p`, " "(SELECT @curRank := 0, @prevRank := NULL, @row := 0, @ourRow := 0) `r` " - "WHERE `group_id` < {3} ORDER BY `{1}` DESC" - ") `t`", - entriesStr, escapedCategoryName, playerGUID, static_cast(GROUP_TYPE_GAMEMASTER) + "WHERE `group_id` < {} ORDER BY `{}` DESC) `t`", + categoryName, static_cast(GROUP_TYPE_GAMEMASTER), categoryName ); if (vocation != 0xFFFFFFFF) { query += generateVocationConditionHighscore(vocation); } - query += fmt::format( - ") `T` WHERE `rn` > ((@ourRow DIV {0}) * {0}) AND `rn` <= (((@ourRow DIV {0}) * {0}) + {0})", - entriesStr - ); + query += ") `T` WHERE "; + + if (playerGUID != 0) { + query += fmt::format( + "`rn` > ((@ourRow DIV {0}) * {0}) AND `rn` <= (((@ourRow DIV {0}) * {0}) + {0})", + entriesStr + ); + } else { + query += fmt::format("`rn` > {} AND `rn` <= {}", startPage, endPage); + } return query; } @@ -8676,7 +8680,7 @@ std::string Game::generateHighscoreOrGetCachedQueryForEntries(const std::string } } - std::string newQuery = generateHighscoreQueryForEntries(categoryName, page, entriesPerPage, vocation); + std::string newQuery = generateHighscoreQuery(categoryName, page, entriesPerPage, vocation); cacheQueryHighscore(cacheKey, newQuery, page, entriesPerPage); return newQuery; @@ -8694,7 +8698,7 @@ std::string Game::generateHighscoreOrGetCachedQueryForOurRank(const std::string } } - std::string newQuery = generateHighscoreQueryForOurRank(categoryName, entriesPerPage, playerGUID, vocation); + std::string newQuery = generateHighscoreQuery(categoryName, 0, entriesPerPage, vocation, playerGUID); cacheQueryHighscore(cacheKey, newQuery, entriesPerPage, entriesPerPage); return newQuery; diff --git a/src/game/game.hpp b/src/game/game.hpp index a59c7d13a70..9a4b59f8a55 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -957,8 +957,13 @@ class Game { void processHighscoreResults(const DBResult_ptr &result, uint32_t playerID, uint8_t category, uint32_t vocation, uint8_t entriesPerPage); std::string generateVocationConditionHighscore(uint32_t vocation); - std::string generateHighscoreQueryForEntries(const std::string &categoryName, uint32_t page, uint8_t entriesPerPage, uint32_t vocation); - std::string generateHighscoreQueryForOurRank(const std::string &categoryName, uint8_t entriesPerPage, uint32_t playerGUID, uint32_t vocation); + std::string generateHighscoreQuery( + const std::string &categoryName, + uint32_t page, + uint8_t entriesPerPage, + uint32_t vocation, + uint32_t playerGUID = 0 + ); std::string generateHighscoreOrGetCachedQueryForEntries(const std::string &categoryName, uint32_t page, uint8_t entriesPerPage, uint32_t vocation); std::string generateHighscoreOrGetCachedQueryForOurRank(const std::string &categoryName, uint8_t entriesPerPage, uint32_t playerGUID, uint32_t vocation); From 7c10e50f47bf3c5f05489179047984798211d32d Mon Sep 17 00:00:00 2001 From: "Leilani A." <168607226+kaleohanopahala@users.noreply.github.com> Date: Wed, 8 Jan 2025 18:02:31 -0300 Subject: [PATCH 29/32] feat: add morguthis wall action (#3226) --- .../action_morguthis_wall.lua | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 data-otservbr-global/scripts/quests/the_ancient_tombs/action_morguthis_wall.lua diff --git a/data-otservbr-global/scripts/quests/the_ancient_tombs/action_morguthis_wall.lua b/data-otservbr-global/scripts/quests/the_ancient_tombs/action_morguthis_wall.lua new file mode 100644 index 00000000000..420a2f3fbc2 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_ancient_tombs/action_morguthis_wall.lua @@ -0,0 +1,39 @@ +local morguthisWall = Action() + +function morguthisWall.onUse(player, item, fromPosition, target, toPosition) + local wallPosition = Position(33211, 32698, 13) + local wallId = 1306 + + local tile = Tile(wallPosition) + if not tile then + return false + end + + local wall = tile:getItemById(wallId) + if wall then + wall:remove() + else + local creatures = tile:getCreatures() + if creatures then + for _, creature in ipairs(creatures) do + local newPosition = Position(wallPosition.x, wallPosition.y + 1, wallPosition.z) + creature:teleportTo(newPosition) + end + end + + local items = tile:getItems() + if items then + for _, tileItem in ipairs(items) do + local newPosition = Position(wallPosition.x, wallPosition.y + 1, wallPosition.z) + tileItem:moveTo(newPosition) + end + end + + Game.createItem(wallId, 1, wallPosition) + end + + return true +end + +morguthisWall:position(Position(33212, 32693, 13)) +morguthisWall:register() From 15c60e225775dd4bd6e8df7d25ad2c36e5b53877 Mon Sep 17 00:00:00 2001 From: murilo09 <78226931+murilo09@users.noreply.github.com> Date: Thu, 9 Jan 2025 12:41:50 -0300 Subject: [PATCH 30/32] fix: negative absorb percent (#3228) Fixes #3218 --- src/creatures/players/player.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 979f5d9a74d..a78427f8039 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -3371,7 +3371,7 @@ BlockType_t Player::blockHit(const std::shared_ptr &attacker, const Co } } - // + // Absorb Percent const ItemType &it = Item::items[item->getID()]; if (it.abilities) { int totalAbsorbPercent = 0; @@ -3387,7 +3387,7 @@ BlockType_t Player::blockHit(const std::shared_ptr &attacker, const Co } } - if (totalAbsorbPercent > 0) { + if (totalAbsorbPercent != 0) { damage -= std::round(damage * (totalAbsorbPercent / 100.0)); const auto charges = item->getAttribute(ItemAttribute_t::CHARGES); @@ -9938,9 +9938,7 @@ void Player::onFollowCreatureDisappear(bool isLogout) { } } -// container -// container - +// Container void Player::onAddContainerItem(const std::shared_ptr &item) { checkTradeState(item); } From 1ed5cd8e010f02de3341a1ee86221794026a7cd2 Mon Sep 17 00:00:00 2001 From: murilo09 <78226931+murilo09@users.noreply.github.com> Date: Thu, 9 Jan 2025 13:32:16 -0300 Subject: [PATCH 31/32] fix: fields without owner (#3229) Fixes #2696 --- src/creatures/combat/condition.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index adbb31606e9..4d4796ec2d5 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -1775,13 +1775,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)) { + // Only perform checks and assign attacker if owner is not 0, keeping a const reference to the shared_ptr + const auto &attacker = (owner != 0) ? (g_game().getPlayerByGUID(owner) ? g_game().getPlayerByGUID(owner)->getCreature() : g_game().getCreatureByID(owner)) : nullptr; + const auto &attackerPlayer = attacker ? attacker->getPlayer() : nullptr; + if (creature->isSuppress(getType(), attackerPlayer != nullptr)) { return true; } @@ -1790,7 +1787,7 @@ bool ConditionDamage::doDamage(const std::shared_ptr &creature, int32_ damage.primary.value = healthChange; damage.primary.type = Combat::ConditionToDamageType(conditionType); - if (field && creature->getPlayer() && attacker && attacker->getPlayer()) { + if (field && creature->getPlayer() && attackerPlayer) { damage.primary.value = static_cast(std::round(damage.primary.value / 2.)); } From 13dbc4f4d3f1d8343d16787acf17b9bbf50a3914 Mon Sep 17 00:00:00 2001 From: murilo09 <78226931+murilo09@users.noreply.github.com> Date: Thu, 9 Jan 2025 13:39:40 -0300 Subject: [PATCH 32/32] feat: login and disconnect protection (#3223) Tibia 9.50: https://www.tibia.com/news/?subtopic=newsarchive&id=2009 X-Logging: https://tibia.fandom.com/wiki/X-Logging --- config.lua.dist | 2 + data/events/scripts/creature.lua | 52 ------------------- data/scripts/creaturescripts/player/login.lua | 22 -------- src/config/config_enums.hpp | 1 + src/config/configmanager.cpp | 1 + src/creatures/creature.cpp | 8 +++ src/creatures/monsters/monster.cpp | 24 +++++---- src/creatures/players/player.cpp | 33 +++++++++--- src/creatures/players/player.hpp | 10 ++++ src/game/game.cpp | 10 ++++ src/server/network/protocol/protocolgame.cpp | 7 +++ 11 files changed, 81 insertions(+), 89 deletions(-) diff --git a/config.lua.dist b/config.lua.dist index 60c4b770063..b29fa2f593a 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -26,6 +26,7 @@ maintainModeMessage = "" -- NOTE: valid values for worldType are: "pvp", "no-pvp" and "pvp-enforced" -- NOTE: removeBeginningWeaponAmmunition: spears, arrows, bolt have endless ammo (allows training for paladins) -- NOTE: refundManaOnBeginningWeapons: wand of vortex and snakebite refund mana used (allows training for mages) +-- NOTE: loginProtectionTime in MS worldType = "pvp" hotkeyAimbotEnabled = true protectionLevel = 7 @@ -47,6 +48,7 @@ monthKillsToRedSkull = 10 redSkullDuration = 1 blackSkullDuration = 3 orangeSkullDuration = 7 +loginProtectionTime = 10 * 1000 cleanProtectionZones = false diff --git a/data/events/scripts/creature.lua b/data/events/scripts/creature.lua index e7a74a33191..639fe52ca0b 100644 --- a/data/events/scripts/creature.lua +++ b/data/events/scripts/creature.lua @@ -1,60 +1,8 @@ -local function removeCombatProtection(playerUid) - local player = Player(playerUid) - if not player then - return true - end - - local time = 0 - if player:isMage() then - time = 10 - elseif player:isPaladin() then - time = 20 - else - time = 30 - end - - player:kv():set("combat-protection", 2) - addEvent(function(playerFuncUid) - local playerEvent = Player(playerFuncUid) - if not playerEvent then - return - end - - playerEvent:kv():remove("combat-protection") - playerEvent:remove() - end, time * 1000, playerUid) -end - function Creature:onTargetCombat(target) if not self then return true end - if target:isPlayer() then - if self:isMonster() then - local isProtected = target:kv():get("combat-protection") or 0 - - if target:getIp() == 0 then -- If player is disconnected, monster shall ignore to attack the player - if target:isPzLocked() then - return true - end - if isProtected <= 0 then - addEvent(removeCombatProtection, 30 * 1000, target.uid) - target:kv():set("combat-protection", 1) - elseif isProtected == 1 then - self:searchTarget() - return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER - end - - return true - end - - if isProtected >= os.time() then - return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER - end - end - end - if (target:isMonster() and self:isPlayer() and target:getMaster() == self) or (self:isMonster() and target:isPlayer() and self:getMaster() == target) then return RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE end diff --git a/data/scripts/creaturescripts/player/login.lua b/data/scripts/creaturescripts/player/login.lua index fe52be71b06..2f33879acb3 100644 --- a/data/scripts/creaturescripts/player/login.lua +++ b/data/scripts/creaturescripts/player/login.lua @@ -2,21 +2,6 @@ local function sendBoostMessage(player, category, isIncreased) return player:sendTextMessage(MESSAGE_BOOSTED_CREATURE, string.format("Event! %s is %screased. Happy Hunting!", category, isIncreased and "in" or "de")) end -local function onMovementRemoveProtection(playerId, oldPos, time) - local player = Player(playerId) - if not player then - return true - end - - local playerPos = player:getPosition() - if (playerPos.x ~= oldPos.x or playerPos.y ~= oldPos.y or playerPos.z ~= oldPos.z) or player:getTarget() then - player:kv():remove("combat-protection") - return true - end - - addEvent(onMovementRemoveProtection, 1000, playerId, oldPos, time - 1) -end - local playerLoginGlobal = CreatureEvent("PlayerLoginGlobal") function playerLoginGlobal.onLogin(player) @@ -162,13 +147,6 @@ function playerLoginGlobal.onLogin(player) player:setRemoveBossTime(1) end - -- Remove combat protection - local isProtected = player:kv():get("combat-protection") or 0 - if isProtected < 1 then - player:kv():set("combat-protection", 1) - onMovementRemoveProtection(playerId, player:getPosition(), 10) - end - -- Change support outfit to a normal outfit to open customize character without crashes local playerOutfit = player:getOutfit() if table.contains({ 75, 266, 302 }, playerOutfit.lookType) then diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp index d180cc668c2..16b91289480 100644 --- a/src/config/config_enums.hpp +++ b/src/config/config_enums.hpp @@ -181,6 +181,7 @@ enum ConfigKey_t : uint16_t { ONSLAUGHT_CHANCE_FORMULA_C, OPTIMIZE_DATABASE, ORANGE_SKULL_DURATION, + LOGIN_PROTECTION_TIME, OWNER_EMAIL, OWNER_NAME, PARALLELISM, diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index 5ef93d5461b..228b93426b7 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -287,6 +287,7 @@ bool ConfigManager::load() { loadIntConfig(L, MONTH_KILLS_TO_RED, "monthKillsToRedSkull", 10); loadIntConfig(L, MULTIPLIER_ATTACKONFIST, "multiplierSpeedOnFist", 5); loadIntConfig(L, ORANGE_SKULL_DURATION, "orangeSkullDuration", 7); + loadIntConfig(L, LOGIN_PROTECTION_TIME, "loginProtectionTime", 10000); loadIntConfig(L, PARALLELISM, "parallelism", 2); loadIntConfig(L, PARTY_LIST_MAX_DISTANCE, "partyListMaxDistance", 0); loadIntConfig(L, PREY_BONUS_REROLL_PRICE, "preyBonusRerollPrice", 1); diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index 4d02f54c364..2ed8b892e5e 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -160,6 +160,14 @@ void Creature::onAttacking(uint32_t interval) { return; } + if (attackedCreature->getType() == CreatureType_t::CREATURETYPE_PLAYER) { + const auto &player = attackedCreature->getPlayer(); + if (player && player->isDisconnected() && !player->isProtected()) { + player->setProtection(true); + player->setLoginProtection(30000); + } + } + onAttacked(); attackedCreature->onAttacked(); diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index 845544d61e9..95412cc6751 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -669,7 +669,8 @@ bool Monster::isOpponent(const std::shared_ptr &creature) const { return creature != master; } - if (creature->getPlayer() && creature->getPlayer()->hasFlag(PlayerFlags_t::IgnoredByMonsters)) { + const auto &player = creature ? creature->getPlayer() : nullptr; + if (player && player->hasFlag(PlayerFlags_t::IgnoredByMonsters)) { return false; } @@ -679,7 +680,7 @@ bool Monster::isOpponent(const std::shared_ptr &creature) const { const auto &creatureMaster = creature->getMaster(); const auto &creaturePlayer = creatureMaster ? creatureMaster->getPlayer() : nullptr; - if (creature->getPlayer() || creaturePlayer) { + if (player || creaturePlayer) { return true; } @@ -913,10 +914,6 @@ bool Monster::isTarget(const std::shared_ptr &creature) { } if (!isSummon()) { - if (creature->getPlayer() && creature->getPlayer()->isDisconnected()) { - return false; - } - if (getFaction() != FACTION_DEFAULT) { return isEnemyFaction(creature->getFaction()); } @@ -934,6 +931,11 @@ bool Monster::selectTarget(const std::shared_ptr &creature) { return false; } + const auto &player = creature ? creature->getPlayer() : nullptr; + if (player && player->isLoginProtected()) { + return false; + } + auto it = getTargetIterator(creature); if (it == targetList.end()) { // Target not found in our target list. @@ -1088,11 +1090,10 @@ void Monster::onThink_async() { setFollowCreature(master); } } else if (!targetList.empty()) { - const bool attackedCreatureIsDisconnected = attackedCreature && attackedCreature->getPlayer() && attackedCreature->getPlayer()->isDisconnected(); const bool attackedCreatureIsUnattackable = attackedCreature && !canUseAttack(getPosition(), attackedCreature); const bool attackedCreatureIsUnreachable = targetDistance <= 1 && attackedCreature && followCreature && !hasFollowPath; - if (!attackedCreature || attackedCreatureIsDisconnected || attackedCreatureIsUnattackable || attackedCreatureIsUnreachable) { - if (!followCreature || !hasFollowPath || attackedCreatureIsDisconnected) { + if (!attackedCreature || attackedCreatureIsUnattackable || attackedCreatureIsUnreachable) { + if (!followCreature || !hasFollowPath) { searchTarget(TARGETSEARCH_NEAREST); } else if (attackedCreature && isFleeing() && !canUseAttack(getPosition(), attackedCreature)) { searchTarget(TARGETSEARCH_DEFAULT); @@ -1115,6 +1116,11 @@ void Monster::doAttacking(uint32_t interval) { return; } + const auto &player = attackedCreature->getPlayer(); + if (player && player->isLoginProtected()) { + return; + } + bool updateLook = true; bool resetTicks = interval != 0; attackTicks += interval; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index a78427f8039..afddebb799a 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -2045,16 +2045,20 @@ void Player::sendPing() { const int64_t noPongTime = timeNow - lastPong; const auto &attackedCreature = getAttackedCreature(); - if ((hasLostConnection || noPongTime >= 7000) && attackedCreature && attackedCreature->getPlayer()) { + if ((hasLostConnection || noPongTime >= 10000) && attackedCreature) { setAttackedCreature(nullptr); } - if (noPongTime >= 60000 && canLogout() && g_creatureEvents().playerLogout(static_self_cast())) { - g_logger().info("Player {} has been kicked due to ping timeout. (has client: {})", getName(), client != nullptr); - if (client) { - client->logout(true, true); + if (noPongTime >= 60000 && shouldForceLogout) { + if (canLogout() && g_creatureEvents().playerLogout(static_self_cast())) { + g_logger().info("Player {} has been kicked due to ping timeout. (has client: {})", getName(), client != nullptr); + if (client) { + client->logout(true, true); + } else { + g_game().removeCreature(static_self_cast(), true); + } } else { - g_game().removeCreature(static_self_cast(), true); + shouldForceLogout = false; } } } @@ -2930,6 +2934,23 @@ bool Player::canDoPotionAction() const { return nextPotionAction <= OTSYS_TIME(); } +void Player::setLoginProtection(int64_t time) { + loginProtectionTime = OTSYS_TIME() + time; +} +bool Player::isLoginProtected() const { + return loginProtectionTime > OTSYS_TIME(); +} +void Player::resetLoginProtection() { + loginProtectionTime = 0; +} + +void Player::setProtection(bool status) { + connProtected = status; +} +bool Player::isProtected() { + return connProtected; +} + void Player::cancelPush() { if (actionTaskEventPush != 0) { g_dispatcher().stopEvent(actionTaskEventPush); diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 1b52607f2d8..371048fbe9a 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -945,6 +945,13 @@ class Player final : public Creature, public Cylinder, public Bankable { void setNextPotionAction(int64_t time); bool canDoPotionAction() const; + void setLoginProtection(int64_t time); + bool isLoginProtected() const; + void resetLoginProtection(); + + void setProtection(bool status); + bool isProtected(); + void cancelPush(); void setModuleDelay(uint8_t byteortype, int16_t delay); @@ -1421,6 +1428,7 @@ class Player final : public Creature, public Cylinder, public Bankable { int64_t nextPotionAction = 0; int64_t lastQuickLootNotification = 0; int64_t lastWalking = 0; + int64_t loginProtectionTime = 0; uint64_t asyncOngoingTasks = 0; std::vector unjustifiedKills; @@ -1560,6 +1568,8 @@ class Player final : public Creature, public Cylinder, public Bankable { bool moved = false; bool m_isDead = false; bool imbuementTrackerWindowOpen = false; + bool shouldForceLogout = true; + bool connProtected = false; // Hazard system int64_t lastHazardSystemCriticalHit = 0; diff --git a/src/game/game.cpp b/src/game/game.cpp index 5ce501c8288..80aea6c8b9b 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -3505,6 +3505,7 @@ void Game::playerMove(uint32_t playerId, Direction direction) { return; } + player->resetLoginProtection(); player->resetIdleTime(); player->setNextWalkActionTask(nullptr); player->cancelPush(); @@ -3518,6 +3519,7 @@ void Game::forcePlayerMove(uint32_t playerId, Direction direction) { return; } + player->resetLoginProtection(); player->resetIdleTime(); player->setNextWalkActionTask(nullptr); player->cancelPush(); @@ -3693,6 +3695,7 @@ void Game::playerAutoWalk(uint32_t playerId, const std::vector &listD return; } + player->resetLoginProtection(); player->resetIdleTime(); player->setNextWalkTask(nullptr); player->startAutoWalk(listDir, false); @@ -3709,6 +3712,7 @@ void Game::forcePlayerAutoWalk(uint32_t playerId, const std::vector & player->sendCancelTarget(); player->setFollowCreature(nullptr); + player->resetLoginProtection(); player->resetIdleTime(); player->setNextWalkTask(nullptr); @@ -3842,6 +3846,7 @@ void Game::playerUseItemEx(uint32_t playerId, const Position &fromPos, uint8_t f return; } + player->resetLoginProtection(); player->resetIdleTime(); if (it.isRune() || it.type == ITEM_TYPE_POTION) { player->setNextPotionActionTask(nullptr); @@ -3963,6 +3968,7 @@ void Game::playerUseItem(uint32_t playerId, const Position &pos, uint8_t stackPo return; } + player->resetLoginProtection(); player->resetIdleTime(); player->setNextActionTask(nullptr); @@ -4127,6 +4133,7 @@ void Game::playerUseWithCreature(uint32_t playerId, const Position &fromPos, uin return; } + player->resetLoginProtection(); player->resetIdleTime(); if (it.isRune() || it.type == ITEM_TYPE_POTION) { player->setNextPotionActionTask(nullptr); @@ -5911,6 +5918,7 @@ void Game::playerSetAttackedCreature(uint32_t playerId, uint32_t creatureId) { } if (player->getAttackedCreature() && creatureId == 0) { + player->resetLoginProtection(); player->setAttackedCreature(nullptr); player->sendCancelTarget(); return; @@ -6085,6 +6093,7 @@ void Game::playerTurn(uint32_t playerId, Direction dir) { return; } + player->resetLoginProtection(); player->resetIdleTime(); internalCreatureTurn(player, dir); } @@ -6208,6 +6217,7 @@ void Game::playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type, c return; } + player->resetLoginProtection(); player->resetIdleTime(); if (playerSaySpell(player, type, text)) { diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 60e29847477..9d385be58c7 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -614,6 +614,7 @@ void ProtocolGame::login(const std::string &name, uint32_t accountId, OperatingS player->lastIP = player->getIP(); player->lastLoginSaved = std::max(time(nullptr), player->lastLoginSaved + 1); + player->loginProtectionTime = OTSYS_TIME() + g_configManager().getNumber(LOGIN_PROTECTION_TIME); acceptPackets = true; } else { if (eventConnect != 0 || !g_configManager().getBoolean(REPLACE_KICK_ON_LOGIN)) { @@ -666,6 +667,12 @@ void ProtocolGame::connect(const std::string &playerName, OperatingSystem_t oper sendAddCreature(player, player->getPosition(), 0, true); player->lastIP = player->getIP(); player->lastLoginSaved = std::max(time(nullptr), player->lastLoginSaved + 1); + if (player->isProtected()) { + player->setProtection(false); + player->resetLoginProtection(); + } else { + player->setLoginProtection(g_configManager().getNumber(LOGIN_PROTECTION_TIME)); + } player->resetIdleTime(); acceptPackets = true; }