diff --git a/.clang-format b/.clang-format
index de45e1b2e7a..289f5508316 100644
--- a/.clang-format
+++ b/.clang-format
@@ -111,7 +111,7 @@ QualifierAlignment: Left
ReferenceAlignment: Right
ReflowComments: true
RemoveBracesLLVM: false
-SortIncludes: false
+SortIncludes: Never
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
@@ -138,4 +138,4 @@ StatementMacros:
- QT_REQUIRE_VERSION
TabWidth: 4
UseCRLF: false
-UseTab: true
+UseTab: AlignWithSpaces
diff --git a/.github/workflows/clang-lint.yml b/.github/workflows/clang-lint.yml
index 67e6427d853..b57e407cbb9 100644
--- a/.github/workflows/clang-lint.yml
+++ b/.github/workflows/clang-lint.yml
@@ -37,17 +37,17 @@ jobs:
- name: Run clang format lint
if: ${{ github.ref != 'refs/heads/main' }}
- uses: DoozyX/clang-format-lint-action@v0.16.2
+ uses: DoozyX/clang-format-lint-action@v0.17
with:
source: "src"
exclude: "src/protobuf"
extensions: "cpp,hpp,h"
- clangFormatVersion: 16
+ clangFormatVersion: 17
inplace: true
- name: Run add and commit
if: ${{ github.ref != 'refs/heads/main' }}
- uses: EndBug/add-and-commit@v9
+ uses: EndBug/add-and-commit@v9.1.4
with:
author_name: GitHub Actions
author_email: github-actions[bot]@users.noreply.github.com
diff --git a/.github/workflows/mysql-schema-check.yml b/.github/workflows/mysql-schema-check.yml
new file mode 100644
index 00000000000..b0291956edc
--- /dev/null
+++ b/.github/workflows/mysql-schema-check.yml
@@ -0,0 +1,43 @@
+---
+name: MySQL Schema Check
+on:
+ workflow_dispatch:
+ pull_request:
+ types: [opened, synchronize, reopened, ready_for_review]
+ paths:
+ - "schema.sql"
+ merge_group:
+ push:
+ paths:
+ - "schema.sql"
+ branches:
+ - main
+
+jobs:
+ mysql-schema-check:
+ runs-on: ubuntu-latest
+ services:
+ mysql:
+ image: mysql:8.0
+ env:
+ MYSQL_ROOT_PASSWORD: root
+ MYSQL_DATABASE: canary
+ MYSQL_USER: canary
+ MYSQL_PASSWORD: canary
+ ports:
+ - 3306/tcp
+ options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
+ strategy:
+ fail-fast: false
+ name: Check
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@main
+ - name: 📌 MySQL Start & init & show db
+ run: |
+ sudo /etc/init.d/mysql start
+ mysql -e 'CREATE DATABASE canary;' -uroot -proot
+ mysql -e "SHOW DATABASES" -uroot -proot
+ - name: Import Canary Schema
+ run: |
+ mysql -uroot -proot canary < schema.sql
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 52c35a471f7..07701451aad 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,7 +8,7 @@ cmake_minimum_required(VERSION 3.22 FATAL_ERROR)
# VCPKG
# cmake -DCMAKE_TOOLCHAIN_FILE=/opt/workspace/vcpkg/scripts/buildsystems/vcpkg.cmake ..
# Needed libs is in file vcpkg.json
-# Windows required libs: .\vcpkg install --triplet x64-windows asio pugixml spdlog curl protobuf parallel-hashmap magic-enum mio luajit libmariadb mpir abseil
+# Windows required libs: .\vcpkg install --triplet x64-windows asio pugixml spdlog curl protobuf parallel-hashmap magic-enum mio luajit libmariadb mpir abseil bshoshany-thread-pool
if(DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE)
set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
CACHE STRING "")
@@ -124,4 +124,4 @@ add_subdirectory(src)
if(BUILD_TESTS)
add_subdirectory(tests)
-endif()
\ No newline at end of file
+endif()
diff --git a/CMakePresets.json b/CMakePresets.json
index 92ef2d33c6a..49f631e1921 100644
--- a/CMakePresets.json
+++ b/CMakePresets.json
@@ -54,6 +54,7 @@
"DEBUG_LOG": "ON",
"ASAN_ENABLED": "OFF",
"BUILD_STATIC_LIBRARY": "OFF",
+ "SPEED_UP_BUILD_UNITY": "OFF",
"VCPKG_TARGET_TRIPLET": "x64-windows"
}
},
diff --git a/config.lua.dist b/config.lua.dist
index 447a90647c2..9d1ed2fa681 100644
--- a/config.lua.dist
+++ b/config.lua.dist
@@ -511,6 +511,7 @@ bossDefaultTimeToFightAgain = 20 * 60 * 60 -- 20 hours
bossDefaultTimeToDefeat = 20 * 60 -- 20 minutes
-- Monsters
+defaultRespawnTime = 60
deSpawnRange = 2
deSpawnRadius = 50
diff --git a/data-canary/monster/demons/fury.lua b/data-canary/monster/demons/fury.lua
index 3835050ce67..a01553afeed 100644
--- a/data-canary/monster/demons/fury.lua
+++ b/data-canary/monster/demons/fury.lua
@@ -125,7 +125,7 @@ monster.elements = {
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
{ type = COMBAT_DROWNDAMAGE, percent = 0 },
- { type = COMBAT_ICEDAMAGE, percent = 30 },
+ { type = COMBAT_ICEDAMAGE, percent = 5 },
{ type = COMBAT_HOLYDAMAGE, percent = 30 },
{ type = COMBAT_DEATHDAMAGE, percent = -10 },
}
diff --git a/data-canary/monster/demons/juggernaut.lua b/data-canary/monster/demons/juggernaut.lua
index 7b3e3d1795b..704ce847f13 100644
--- a/data-canary/monster/demons/juggernaut.lua
+++ b/data-canary/monster/demons/juggernaut.lua
@@ -27,8 +27,8 @@ monster.Bestiary = {
The Blood Halls, The Vats, The Hive, The Shadow Nexus, a room deep in Formorgar Mines, Roshamuul Prison, Oramond Dungeon, Grounds of Destruction.",
}
-monster.health = 20000
-monster.maxHealth = 20000
+monster.health = 18000
+monster.maxHealth = 18000
monster.race = "blood"
monster.corpse = 6335
monster.speed = 170
diff --git a/data-canary/scripts/actions/other/large_sea_shell.lua b/data-canary/scripts/actions/other/large_sea_shell.lua
deleted file mode 100644
index b396bd70611..00000000000
--- a/data-canary/scripts/actions/other/large_sea_shell.lua
+++ /dev/null
@@ -1,28 +0,0 @@
-local largeSeaShell = Action()
-
-function largeSeaShell.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if player:getStorageValue(Storage.DelayLargeSeaShell) <= os.time() then
- local chance = math.random(100)
- local msg = ""
- if chance <= 16 then
- doTargetCombatHealth(0, player, COMBAT_PHYSICALDAMAGE, -200, -200, CONST_ME_NONE)
- msg = "Ouch! You squeezed your fingers."
- elseif chance > 16 and chance <= 64 then
- Game.createItem(math.random(281, 282), 1, player:getPosition())
- msg = "You found a beautiful pearl."
- else
- msg = "Nothing is inside."
- end
- player:say(msg, TALKTYPE_MONSTER_SAY, false, player, item:getPosition())
- item:transform(198)
- item:decay()
- player:setStorageValue(Storage.DelayLargeSeaShell, os.time() + 20 * 60 * 60)
- item:getPosition():sendMagicEffect(CONST_ME_BUBBLES)
- else
- player:say("You have already opened a shell today.", TALKTYPE_MONSTER_SAY, false, player, item:getPosition())
- end
- return true
-end
-
-largeSeaShell:id(197)
-largeSeaShell:register()
diff --git a/data-canary/scripts/weapons/scripted_weapons.lua b/data-canary/scripts/weapons/scripted_weapons.lua
deleted file mode 100644
index 93e677d43cd..00000000000
--- a/data-canary/scripts/weapons/scripted_weapons.lua
+++ /dev/null
@@ -1,101 +0,0 @@
-local burstArea = createCombatArea({
- { 1, 1, 1 },
- { 1, 3, 1 },
- { 1, 1, 1 },
-})
-
-local burstCombat = Combat()
-burstCombat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)
-burstCombat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_EXPLOSIONAREA)
-burstCombat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_BURSTARROW)
-burstCombat:setParameter(COMBAT_PARAM_BLOCKARMOR, true)
-burstCombat:setFormula(COMBAT_FORMULA_SKILL, 0, 0, 1, 0)
-burstCombat:setParameter(COMBAT_PARAM_IMPACTSOUND, SOUND_EFFECT_TYPE_BURST_ARROW_EFFECT)
-burstCombat:setParameter(COMBAT_PARAM_CASTSOUND, SOUND_EFFECT_TYPE_DIST_ATK_BOW)
-burstCombat:setArea(burstArea)
-
-local burstarrow = Weapon(WEAPON_AMMO)
-burstarrow.onUseWeapon = function(player, variant)
- if player:getSkull() == SKULL_BLACK then
- return false
- end
-
- return burstCombat:execute(player, variant)
-end
-
-burstarrow:id(3449)
-burstarrow:action("removecount")
-burstarrow:register()
-
-local poisonCombat = Combat()
-poisonCombat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)
-poisonCombat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_POISONARROW)
-poisonCombat:setParameter(COMBAT_PARAM_BLOCKARMOR, true)
-poisonCombat:setFormula(COMBAT_FORMULA_SKILL, 0, 0, 1, 0)
-
-local poisonarrow = Weapon(WEAPON_AMMO)
-poisonarrow.onUseWeapon = function(player, variant)
- if not poisonCombat:execute(player, variant) then
- return false
- end
-
- player:addDamageCondition(Creature(variant:getNumber()), CONDITION_POISON, DAMAGELIST_LOGARITHMIC_DAMAGE, 3)
- return true
-end
-
-poisonarrow:id(3448)
-poisonarrow:action("removecount")
-poisonarrow:register()
-
-local viperCombat = Combat()
-viperCombat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)
-viperCombat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_GREENSTAR)
-viperCombat:setParameter(COMBAT_PARAM_BLOCKARMOR, true)
-viperCombat:setFormula(COMBAT_FORMULA_SKILL, 0, 0, 1, 0)
-
-local viperstar = Weapon(WEAPON_DISTANCE)
-viperstar.onUseWeapon = function(player, variant)
- if not viperCombat:execute(player, variant) then
- return false
- end
-
- if math.random(1, 100) <= 90 then
- return false
- end
-
- player:addDamageCondition(Creature(variant:getNumber()), CONDITION_POISON, DAMAGELIST_LOGARITHMIC_DAMAGE, 2)
- return true
-end
-
-viperstar:id(7366)
-viperstar:breakChance(9)
-viperstar:register()
-
-local diamondArea = createCombatArea({
- { 0, 1, 1, 1, 0 },
- { 1, 1, 1, 1, 1 },
- { 1, 1, 3, 1, 1 },
- { 1, 1, 1, 1, 1 },
- { 0, 1, 1, 1, 0 },
-})
-
-local diamondCombat = Combat()
-diamondCombat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)
-diamondCombat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_ENERGYHIT)
-diamondCombat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_DIAMONDARROW)
-diamondCombat:setParameter(COMBAT_PARAM_IMPACTSOUND, SOUND_EFFECT_TYPE_DIAMOND_ARROW_EFFECT)
-diamondCombat:setParameter(COMBAT_PARAM_CASTSOUND, SOUND_EFFECT_TYPE_DIST_ATK_BOW)
-diamondCombat:setParameter(COMBAT_PARAM_BLOCKARMOR, true)
-diamondCombat:setFormula(COMBAT_FORMULA_SKILL, 0, 0, 1, 0)
-diamondCombat:setArea(diamondArea)
-
-local diamondarrow = Weapon(WEAPON_AMMO)
-diamondarrow.onUseWeapon = function(player, variant)
- return diamondCombat:execute(player, variant)
-end
-
-diamondarrow:id(ITEM_OLD_DIAMOND_ARROW)
-diamondarrow:action("removecount")
-diamondarrow:level(150)
-diamondarrow:wieldUnproperly(true)
-diamondarrow:register()
diff --git a/data-otservbr-global/lib/core/storages.lua b/data-otservbr-global/lib/core/storages.lua
index fce8059f7d1..2b8d9a7783d 100644
--- a/data-otservbr-global/lib/core/storages.lua
+++ b/data-otservbr-global/lib/core/storages.lua
@@ -3079,6 +3079,15 @@ GlobalStorage = {
DarashiaWest = 60193,
},
},
+ TheDreamCourts = {
+ -- Reserved storage from 60194 - 60196
+ FacelessBane = {
+ -- Global
+ StepsOn = 60194,
+ Deaths = 60195,
+ ResetSteps = 60196,
+ },
+ },
FuryGates = 65000,
Yakchal = 65001,
PitsOfInfernoLevers = 65002,
diff --git a/data-otservbr-global/lib/others/load.lua b/data-otservbr-global/lib/others/load.lua
index 1052efb7bd6..031c8fb2026 100644
--- a/data-otservbr-global/lib/others/load.lua
+++ b/data-otservbr-global/lib/others/load.lua
@@ -1,2 +1 @@
dofile(DATA_DIRECTORY .. "/lib/others/dawnport.lua")
-dofile(DATA_DIRECTORY .. "/lib/others/vip_system.lua")
diff --git a/data-otservbr-global/migrations/45.lua b/data-otservbr-global/migrations/45.lua
index c606f18522e..4ceb5f7e3fd 100644
--- a/data-otservbr-global/migrations/45.lua
+++ b/data-otservbr-global/migrations/45.lua
@@ -3,7 +3,7 @@ function onUpdateDatabase()
db.query([[
CREATE TABLE IF NOT EXISTS `account_vipgroups` (
- `id` tinyint(3) UNSIGNED NOT NULL,
+ `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`account_id` int(11) UNSIGNED NOT NULL COMMENT 'id of account whose vip group entry it is',
`name` varchar(128) NOT NULL,
`customizable` BOOLEAN NOT NULL DEFAULT '1',
@@ -13,9 +13,9 @@ function onUpdateDatabase()
db.query([[
CREATE TRIGGER `oncreate_accounts` AFTER INSERT ON `accounts` FOR EACH ROW BEGIN
- INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES (1, NEW.`id`, 'Enemies', 0);
- INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES (2, NEW.`id`, 'Friends', 0);
- INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES (3, NEW.`id`, 'Trading Partner', 0);
+ INSERT INTO `account_vipgroups` (`account_id`, `name`, `customizable`) VALUES (NEW.`id`, 'Enemies', 0);
+ INSERT INTO `account_vipgroups` (`account_id`, `name`, `customizable`) VALUES (NEW.`id`, 'Friends', 0);
+ INSERT INTO `account_vipgroups` (`account_id`, `name`, `customizable`) VALUES (NEW.`id`, 'Trading Partner', 0);
END;
]])
@@ -23,7 +23,7 @@ function onUpdateDatabase()
CREATE TABLE IF NOT EXISTS `account_vipgrouplist` (
`account_id` int(11) UNSIGNED NOT NULL COMMENT 'id of account whose viplist entry it is',
`player_id` int(11) NOT NULL COMMENT 'id of target player of viplist entry',
- `vipgroup_id` tinyint(3) UNSIGNED NOT NULL COMMENT 'id of vip group that player belongs',
+ `vipgroup_id` int(11) UNSIGNED NOT NULL COMMENT 'id of vip group that player belongs',
INDEX `account_id` (`account_id`),
INDEX `player_id` (`player_id`),
INDEX `vipgroup_id` (`vipgroup_id`),
diff --git a/data-otservbr-global/monster/aquatics/quara_constrictor.lua b/data-otservbr-global/monster/aquatics/quara_constrictor.lua
index 02b4877620e..c04460f1f50 100644
--- a/data-otservbr-global/monster/aquatics/quara_constrictor.lua
+++ b/data-otservbr-global/monster/aquatics/quara_constrictor.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Quara Constrictor")
local monster = {}
monster.description = "a quara constrictor"
-monster.experience = 250
+monster.experience = 380
monster.outfit = {
lookType = 46,
lookHead = 0,
diff --git a/data-otservbr-global/monster/aquatics/quara_hydromancer.lua b/data-otservbr-global/monster/aquatics/quara_hydromancer.lua
index 4129dcfe81c..07c3ffbfda3 100644
--- a/data-otservbr-global/monster/aquatics/quara_hydromancer.lua
+++ b/data-otservbr-global/monster/aquatics/quara_hydromancer.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Quara Hydromancer")
local monster = {}
monster.description = "a quara hydromancer"
-monster.experience = 800
+monster.experience = 950
monster.outfit = {
lookType = 47,
lookHead = 0,
diff --git a/data-otservbr-global/monster/aquatics/quara_mantassin.lua b/data-otservbr-global/monster/aquatics/quara_mantassin.lua
index 53594881d50..3e857cab7f2 100644
--- a/data-otservbr-global/monster/aquatics/quara_mantassin.lua
+++ b/data-otservbr-global/monster/aquatics/quara_mantassin.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Quara Mantassin")
local monster = {}
monster.description = "a quara mantassin"
-monster.experience = 400
+monster.experience = 600
monster.outfit = {
lookType = 72,
lookHead = 0,
diff --git a/data-otservbr-global/monster/aquatics/quara_pincher.lua b/data-otservbr-global/monster/aquatics/quara_pincher.lua
index 200aed87628..09fea436314 100644
--- a/data-otservbr-global/monster/aquatics/quara_pincher.lua
+++ b/data-otservbr-global/monster/aquatics/quara_pincher.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Quara Pincher")
local monster = {}
monster.description = "a quara pincher"
-monster.experience = 1200
+monster.experience = 1500
monster.outfit = {
lookType = 77,
lookHead = 0,
diff --git a/data-otservbr-global/monster/aquatics/quara_predator.lua b/data-otservbr-global/monster/aquatics/quara_predator.lua
index e513aa5712c..b12719e26f8 100644
--- a/data-otservbr-global/monster/aquatics/quara_predator.lua
+++ b/data-otservbr-global/monster/aquatics/quara_predator.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Quara Predator")
local monster = {}
monster.description = "a quara predator"
-monster.experience = 1600
+monster.experience = 1850
monster.outfit = {
lookType = 20,
lookHead = 0,
diff --git a/data-otservbr-global/monster/bosses/splasher.lua b/data-otservbr-global/monster/bosses/splasher.lua
index 60b89710c4f..426d3be3c26 100644
--- a/data-otservbr-global/monster/bosses/splasher.lua
+++ b/data-otservbr-global/monster/bosses/splasher.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Splasher")
local monster = {}
monster.description = "Splasher"
-monster.experience = 500
+monster.experience = 1500
monster.outfit = {
lookType = 47,
lookHead = 0,
diff --git a/data-otservbr-global/monster/constructs/clay_guardian.lua b/data-otservbr-global/monster/constructs/clay_guardian.lua
index 804d671bdf9..fb28abe50c3 100644
--- a/data-otservbr-global/monster/constructs/clay_guardian.lua
+++ b/data-otservbr-global/monster/constructs/clay_guardian.lua
@@ -107,7 +107,7 @@ monster.elements = {
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
{ type = COMBAT_DROWNDAMAGE, percent = 0 },
- { type = COMBAT_ICEDAMAGE, percent = 35 },
+ { type = COMBAT_ICEDAMAGE, percent = 20 },
{ type = COMBAT_HOLYDAMAGE, percent = 0 },
{ type = COMBAT_DEATHDAMAGE, percent = 40 },
}
diff --git a/data-otservbr-global/monster/constructs/infected_weeper.lua b/data-otservbr-global/monster/constructs/infected_weeper.lua
index e302618f3eb..2b7d97f0479 100644
--- a/data-otservbr-global/monster/constructs/infected_weeper.lua
+++ b/data-otservbr-global/monster/constructs/infected_weeper.lua
@@ -105,7 +105,7 @@ monster.elements = {
{ type = COMBAT_PHYSICALDAMAGE, percent = 50 },
{ type = COMBAT_ENERGYDAMAGE, percent = 25 },
{ type = COMBAT_EARTHDAMAGE, percent = 100 },
- { type = COMBAT_FIREDAMAGE, percent = 100 },
+ { type = COMBAT_FIREDAMAGE, percent = -100 },
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
{ type = COMBAT_DROWNDAMAGE, percent = 0 },
diff --git a/data-otservbr-global/monster/constructs/lava_golem.lua b/data-otservbr-global/monster/constructs/lava_golem.lua
index aed9ad864c0..5c3f695cf32 100644
--- a/data-otservbr-global/monster/constructs/lava_golem.lua
+++ b/data-otservbr-global/monster/constructs/lava_golem.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Lava Golem")
local monster = {}
monster.description = "a lava golem"
-monster.experience = 6200
+monster.experience = 7900
monster.outfit = {
lookType = 491,
lookHead = 0,
@@ -109,7 +109,6 @@ monster.loot = {
monster.attacks = {
{ name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -400 },
{ name = "combat", interval = 2000, chance = 15, type = COMBAT_FIREDAMAGE, minDamage = -350, maxDamage = -700, length = 8, spread = 0, effect = CONST_ME_FIREATTACK, target = false },
- { name = "combat", interval = 2000, chance = 10, type = COMBAT_MANADRAIN, minDamage = -600, maxDamage = -1300, length = 8, spread = 3, effect = CONST_ME_MORTAREA, target = false },
{ name = "lava golem soulfire", interval = 2000, chance = 15, target = false },
{ name = "combat", interval = 2000, chance = 15, type = COMBAT_FIREDAMAGE, minDamage = -220, maxDamage = -350, radius = 4, effect = CONST_ME_FIREAREA, target = true },
{ name = "speed", interval = 2000, chance = 10, speedChange = -800, length = 5, spread = 3, effect = CONST_ME_BLOCKHIT, target = false, duration = 30000 },
diff --git a/data-otservbr-global/monster/constructs/magma_crawler.lua b/data-otservbr-global/monster/constructs/magma_crawler.lua
index 39c58ab8838..bf5f7ac4feb 100644
--- a/data-otservbr-global/monster/constructs/magma_crawler.lua
+++ b/data-otservbr-global/monster/constructs/magma_crawler.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Magma Crawler")
local monster = {}
monster.description = "a magma crawler"
-monster.experience = 2700
+monster.experience = 3900
monster.outfit = {
lookType = 492,
lookHead = 0,
@@ -35,7 +35,7 @@ monster.manaCost = 0
monster.changeTarget = {
interval = 4000,
- chance = 10,
+ chance = 5,
}
monster.strategiesTarget = {
@@ -118,7 +118,7 @@ monster.defenses = {
defense = 45,
armor = 84,
mitigation = 2.51,
- { name = "invisible", interval = 2000, chance = 10, effect = CONST_ME_MAGIC_BLUE },
+ { name = "invisible", interval = 2000, chance = 5, effect = CONST_ME_MAGIC_BLUE },
}
monster.elements = {
@@ -129,7 +129,7 @@ monster.elements = {
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
{ type = COMBAT_DROWNDAMAGE, percent = 0 },
- { type = COMBAT_ICEDAMAGE, percent = 10 },
+ { type = COMBAT_ICEDAMAGE, percent = 0 },
{ type = COMBAT_HOLYDAMAGE, percent = 0 },
{ type = COMBAT_DEATHDAMAGE, percent = 25 },
}
diff --git a/data-otservbr-global/monster/constructs/orewalker.lua b/data-otservbr-global/monster/constructs/orewalker.lua
index 767b8a049ae..3e1dc4deedd 100644
--- a/data-otservbr-global/monster/constructs/orewalker.lua
+++ b/data-otservbr-global/monster/constructs/orewalker.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Orewalker")
local monster = {}
monster.description = "an orewalker"
-monster.experience = 4800
+monster.experience = 5900
monster.outfit = {
lookType = 490,
lookHead = 0,
@@ -112,7 +112,6 @@ monster.attacks = {
{ name = "combat", interval = 2000, chance = 10, type = COMBAT_PHYSICALDAMAGE, minDamage = 0, maxDamage = -1500, length = 6, spread = 3, effect = CONST_ME_GROUNDSHAKER, target = false },
-- poison
{ name = "condition", type = CONDITION_POISON, interval = 2000, chance = 10, minDamage = -800, maxDamage = -1080, radius = 3, shootEffect = CONST_ANI_SMALLEARTH, effect = CONST_ME_SMALLPLANTS, target = true },
- { name = "drunk", interval = 2000, chance = 15, radius = 4, effect = CONST_ME_SOUND_PURPLE, target = false, duration = 6000 },
{ name = "speed", interval = 2000, chance = 15, speedChange = -800, radius = 2, effect = CONST_ME_MAGIC_RED, target = false, duration = 20000 },
}
diff --git a/data-otservbr-global/monster/constructs/stone_devourer.lua b/data-otservbr-global/monster/constructs/stone_devourer.lua
index 5a9174c3129..39c85402870 100644
--- a/data-otservbr-global/monster/constructs/stone_devourer.lua
+++ b/data-otservbr-global/monster/constructs/stone_devourer.lua
@@ -118,7 +118,7 @@ monster.elements = {
{ type = COMBAT_PHYSICALDAMAGE, percent = 10 },
{ type = COMBAT_ENERGYDAMAGE, percent = 30 },
{ type = COMBAT_EARTHDAMAGE, percent = 100 },
- { type = COMBAT_FIREDAMAGE, percent = 100 },
+ { type = COMBAT_FIREDAMAGE, percent = -5 },
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
{ type = COMBAT_DROWNDAMAGE, percent = 0 },
diff --git a/data-otservbr-global/monster/constructs/weeper.lua b/data-otservbr-global/monster/constructs/weeper.lua
index 75a8d7ebb42..cfc7c6b0d68 100644
--- a/data-otservbr-global/monster/constructs/weeper.lua
+++ b/data-otservbr-global/monster/constructs/weeper.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Weeper")
local monster = {}
monster.description = "a weeper"
-monster.experience = 4800
+monster.experience = 5800
monster.outfit = {
lookType = 489,
lookHead = 0,
@@ -55,7 +55,7 @@ monster.flags = {
canPushCreatures = true,
staticAttackChance = 70,
targetDistance = 1,
- runHealth = 570,
+ runHealth = 0,
healthHidden = false,
isBlockable = false,
canWalkOnEnergy = false,
@@ -101,7 +101,7 @@ monster.attacks = {
{ name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -450 },
{ name = "combat", interval = 2000, chance = 15, type = COMBAT_FIREDAMAGE, minDamage = -400, maxDamage = -1000, length = 8, spread = 0, effect = CONST_ME_FIREATTACK, target = false },
{ name = "combat", interval = 3000, chance = 100, type = COMBAT_FIREDAMAGE, minDamage = -80, maxDamage = -250, radius = 3, effect = CONST_ME_HITBYFIRE, target = false },
- { name = "speed", interval = 2000, chance = 10, speedChange = -800, length = 5, spread = 0, effect = CONST_ME_BLOCKHIT, target = false, duration = 30000 },
+ { name = "speed", interval = 2000, chance = 10, speedChange = -600, length = 5, spread = 0, effect = CONST_ME_BLOCKHIT, target = false, duration = 30000 },
}
monster.defenses = {
diff --git a/data-otservbr-global/monster/demons/hellfire_fighter.lua b/data-otservbr-global/monster/demons/hellfire_fighter.lua
index 6a90b5af06d..2ae5b00303a 100644
--- a/data-otservbr-global/monster/demons/hellfire_fighter.lua
+++ b/data-otservbr-global/monster/demons/hellfire_fighter.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Hellfire Fighter")
local monster = {}
monster.description = "a hellfire fighter"
-monster.experience = 3400
+monster.experience = 3800
monster.outfit = {
lookType = 243,
lookHead = 0,
diff --git a/data-otservbr-global/monster/elementals/cliff_strider.lua b/data-otservbr-global/monster/elementals/cliff_strider.lua
index 2690f356f23..2e143d42796 100644
--- a/data-otservbr-global/monster/elementals/cliff_strider.lua
+++ b/data-otservbr-global/monster/elementals/cliff_strider.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Cliff Strider")
local monster = {}
monster.description = "a cliff strider"
-monster.experience = 5700
+monster.experience = 7100
monster.outfit = {
lookType = 497,
lookHead = 0,
@@ -119,7 +119,7 @@ monster.attacks = {
{ name = "cliff strider skill reducer", interval = 2000, chance = 10, target = false },
{ name = "cliff strider electrify", interval = 2000, chance = 15, range = 1, target = false },
{ name = "combat", interval = 2000, chance = 10, type = COMBAT_PHYSICALDAMAGE, minDamage = 0, maxDamage = -1000, length = 6, spread = 0, effect = CONST_ME_GROUNDSHAKER, target = false },
- { name = "combat", interval = 2000, chance = 15, type = COMBAT_MANADRAIN, minDamage = -100, maxDamage = -300, radius = 4, effect = CONST_ME_YELLOWENERGY, target = false },
+ { name = "combat", interval = 2000, chance = 10, type = COMBAT_MANADRAIN, minDamage = -100, maxDamage = -300, radius = 4, effect = CONST_ME_YELLOWENERGY, target = false },
}
monster.defenses = {
@@ -130,7 +130,7 @@ monster.defenses = {
monster.elements = {
{ type = COMBAT_PHYSICALDAMAGE, percent = 10 },
- { type = COMBAT_ENERGYDAMAGE, percent = 100 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 5 },
{ type = COMBAT_EARTHDAMAGE, percent = 100 },
{ type = COMBAT_FIREDAMAGE, percent = 20 },
{ type = COMBAT_LIFEDRAIN, percent = 0 },
diff --git a/data-otservbr-global/monster/elementals/earth_elemental.lua b/data-otservbr-global/monster/elementals/earth_elemental.lua
index f71d6a7964d..4c3b3e11aa3 100644
--- a/data-otservbr-global/monster/elementals/earth_elemental.lua
+++ b/data-otservbr-global/monster/elementals/earth_elemental.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Earth Elemental")
local monster = {}
monster.description = "an earth elemental"
-monster.experience = 450
+monster.experience = 550
monster.outfit = {
lookType = 301,
lookHead = 0,
@@ -117,7 +117,7 @@ monster.elements = {
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
{ type = COMBAT_DROWNDAMAGE, percent = 0 },
- { type = COMBAT_ICEDAMAGE, percent = 85 },
+ { type = COMBAT_ICEDAMAGE, percent = 5 },
{ type = COMBAT_HOLYDAMAGE, percent = 50 },
{ type = COMBAT_DEATHDAMAGE, percent = 40 },
}
diff --git a/data-otservbr-global/monster/elementals/high_voltage_elemental.lua b/data-otservbr-global/monster/elementals/high_voltage_elemental.lua
index ca99e57527e..3d6806a707b 100644
--- a/data-otservbr-global/monster/elementals/high_voltage_elemental.lua
+++ b/data-otservbr-global/monster/elementals/high_voltage_elemental.lua
@@ -102,7 +102,7 @@ monster.elements = {
{ type = COMBAT_PHYSICALDAMAGE, percent = 35 },
{ type = COMBAT_ENERGYDAMAGE, percent = 100 },
{ type = COMBAT_EARTHDAMAGE, percent = -15 },
- { type = COMBAT_FIREDAMAGE, percent = 100 },
+ { type = COMBAT_FIREDAMAGE, percent = -100 },
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
{ type = COMBAT_DROWNDAMAGE, percent = 0 },
diff --git a/data-otservbr-global/monster/elementals/ironblight.lua b/data-otservbr-global/monster/elementals/ironblight.lua
index 593069318ed..7cb7c7c58fc 100644
--- a/data-otservbr-global/monster/elementals/ironblight.lua
+++ b/data-otservbr-global/monster/elementals/ironblight.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Ironblight")
local monster = {}
monster.description = "an ironblight"
-monster.experience = 4400
+monster.experience = 5400
monster.outfit = {
lookType = 498,
lookHead = 0,
diff --git a/data-otservbr-global/monster/elementals/massive_earth_elemental.lua b/data-otservbr-global/monster/elementals/massive_earth_elemental.lua
index e894319b6ec..1b131e7c7ae 100644
--- a/data-otservbr-global/monster/elementals/massive_earth_elemental.lua
+++ b/data-otservbr-global/monster/elementals/massive_earth_elemental.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Massive Earth Elemental")
local monster = {}
monster.description = "a massive earth elemental"
-monster.experience = 950
+monster.experience = 1100
monster.outfit = {
lookType = 285,
lookHead = 0,
@@ -119,7 +119,7 @@ monster.elements = {
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
{ type = COMBAT_DROWNDAMAGE, percent = 0 },
- { type = COMBAT_ICEDAMAGE, percent = 100 },
+ { type = COMBAT_ICEDAMAGE, percent = 15 },
{ type = COMBAT_HOLYDAMAGE, percent = 50 },
{ type = COMBAT_DEATHDAMAGE, percent = 45 },
}
diff --git a/data-otservbr-global/monster/elementals/sulphur_spouter.lua b/data-otservbr-global/monster/elementals/sulphur_spouter.lua
index 6f574cc0a07..79ebcf8db4b 100644
--- a/data-otservbr-global/monster/elementals/sulphur_spouter.lua
+++ b/data-otservbr-global/monster/elementals/sulphur_spouter.lua
@@ -105,7 +105,7 @@ monster.elements = {
{ type = COMBAT_PHYSICALDAMAGE, percent = 0 },
{ type = COMBAT_ENERGYDAMAGE, percent = 0 },
{ type = COMBAT_EARTHDAMAGE, percent = 0 },
- { type = COMBAT_FIREDAMAGE, percent = 100 },
+ { type = COMBAT_FIREDAMAGE, percent = 25 },
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
{ type = COMBAT_DROWNDAMAGE, percent = 0 },
diff --git a/data-otservbr-global/monster/humanoids/lost_basher.lua b/data-otservbr-global/monster/humanoids/lost_basher.lua
index e19aef97f78..f8866bdd177 100644
--- a/data-otservbr-global/monster/humanoids/lost_basher.lua
+++ b/data-otservbr-global/monster/humanoids/lost_basher.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Lost Basher")
local monster = {}
monster.description = "a lost basher"
-monster.experience = 1800
+monster.experience = 2300
monster.outfit = {
lookType = 538,
lookHead = 0,
@@ -61,7 +61,7 @@ monster.flags = {
healthHidden = false,
isBlockable = false,
canWalkOnEnergy = false,
- canWalkOnFire = false,
+ canWalkOnFire = true,
canWalkOnPoison = true,
}
@@ -126,7 +126,7 @@ monster.elements = {
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
{ type = COMBAT_DROWNDAMAGE, percent = 0 },
- { type = COMBAT_ICEDAMAGE, percent = 20 },
+ { type = COMBAT_ICEDAMAGE, percent = 0 },
{ type = COMBAT_HOLYDAMAGE, percent = 0 },
{ type = COMBAT_DEATHDAMAGE, percent = 15 },
}
diff --git a/data-otservbr-global/monster/humanoids/lost_berserker.lua b/data-otservbr-global/monster/humanoids/lost_berserker.lua
index cd48787df95..cbc7012ff95 100644
--- a/data-otservbr-global/monster/humanoids/lost_berserker.lua
+++ b/data-otservbr-global/monster/humanoids/lost_berserker.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Lost Berserker")
local monster = {}
monster.description = "a lost berserker"
-monster.experience = 4400
+monster.experience = 4800
monster.outfit = {
lookType = 496,
lookHead = 0,
@@ -111,27 +111,25 @@ monster.attacks = {
{ name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -501 },
{ name = "combat", interval = 2000, chance = 15, type = COMBAT_PHYSICALDAMAGE, minDamage = 0, maxDamage = -300, range = 7, shootEffect = CONST_ANI_WHIRLWINDAXE, target = false },
{ name = "combat", interval = 2000, chance = 15, type = COMBAT_PHYSICALDAMAGE, minDamage = 0, maxDamage = -250, range = 7, radius = 3, shootEffect = CONST_ANI_EXPLOSION, effect = CONST_ME_EXPLOSIONAREA, target = true },
- { name = "combat", interval = 2000, chance = 10, type = COMBAT_MANADRAIN, minDamage = -150, maxDamage = -250, radius = 5, effect = CONST_ME_MAGIC_RED, target = false },
+ { name = "combat", interval = 2000, chance = 10, type = COMBAT_MANADRAIN, minDamage = -50, maxDamage = -100, radius = 5, effect = CONST_ME_MAGIC_RED, target = false },
{ name = "speed", interval = 2000, chance = 10, speedChange = -800, radius = 2, effect = CONST_ME_MAGIC_RED, target = false, duration = 20000 },
- { name = "drunk", interval = 2000, chance = 10, radius = 4, effect = CONST_ME_STUN, target = true, duration = 6000 },
}
monster.defenses = {
defense = 40,
armor = 80,
mitigation = 2.40,
- { name = "invisible", interval = 2000, chance = 5, effect = CONST_ME_TELEPORT },
}
monster.elements = {
{ type = COMBAT_PHYSICALDAMAGE, percent = 20 },
- { type = COMBAT_ENERGYDAMAGE, percent = 17 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 100 },
{ type = COMBAT_EARTHDAMAGE, percent = 100 },
{ 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_ICEDAMAGE, percent = 10 },
{ type = COMBAT_HOLYDAMAGE, percent = 0 },
{ type = COMBAT_DEATHDAMAGE, percent = 15 },
}
@@ -139,7 +137,7 @@ monster.elements = {
monster.immunities = {
{ type = "paralyze", condition = true },
{ type = "outfit", condition = false },
- { type = "invisible", condition = true },
+ { type = "invisible", condition = false },
{ type = "bleed", condition = false },
}
diff --git a/data-otservbr-global/monster/humanoids/lost_husher.lua b/data-otservbr-global/monster/humanoids/lost_husher.lua
index d8167f9710d..419bae1ab96 100644
--- a/data-otservbr-global/monster/humanoids/lost_husher.lua
+++ b/data-otservbr-global/monster/humanoids/lost_husher.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Lost Husher")
local monster = {}
monster.description = "a lost husher"
-monster.experience = 1800
+monster.experience = 1100
monster.outfit = {
lookType = 537,
lookHead = 0,
@@ -61,7 +61,7 @@ monster.flags = {
healthHidden = false,
isBlockable = false,
canWalkOnEnergy = false,
- canWalkOnFire = false,
+ canWalkOnFire = true,
canWalkOnPoison = true,
}
@@ -103,7 +103,6 @@ monster.loot = {
monster.attacks = {
{ name = "combat", interval = 2000, chance = 10, type = COMBAT_DEATHDAMAGE, minDamage = -150, maxDamage = -300, length = 6, spread = 0, effect = CONST_ME_BLACKSMOKE, target = false },
- { name = "combat", interval = 2000, chance = 10, type = COMBAT_MANADRAIN, minDamage = -150, maxDamage = -250, radius = 5, effect = CONST_ME_BLACKSMOKE, target = false },
{ name = "combat", interval = 2000, chance = 10, type = COMBAT_DEATHDAMAGE, minDamage = -150, maxDamage = -200, range = 7, shootEffect = CONST_ANI_SUDDENDEATH, effect = CONST_ME_MORTAREA, target = false },
{ name = "combat", interval = 2000, chance = 10, type = COMBAT_EARTHDAMAGE, minDamage = -150, maxDamage = -250, range = 7, radius = 2, shootEffect = CONST_ANI_SMALLEARTH, effect = CONST_ME_MAGIC_GREEN, target = true },
{ name = "drunk", interval = 2000, chance = 10, radius = 4, effect = CONST_ME_SOUND_RED, target = false, duration = 6000 },
@@ -125,7 +124,7 @@ monster.elements = {
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
{ type = COMBAT_DROWNDAMAGE, percent = 0 },
- { type = COMBAT_ICEDAMAGE, percent = 15 },
+ { type = COMBAT_ICEDAMAGE, percent = 0 },
{ type = COMBAT_HOLYDAMAGE, percent = -10 },
{ type = COMBAT_DEATHDAMAGE, percent = 20 },
}
@@ -133,7 +132,7 @@ monster.elements = {
monster.immunities = {
{ type = "paralyze", condition = true },
{ type = "outfit", condition = false },
- { type = "invisible", condition = true },
+ { type = "invisible", condition = false },
{ type = "bleed", condition = false },
}
diff --git a/data-otservbr-global/monster/humanoids/lost_thrower.lua b/data-otservbr-global/monster/humanoids/lost_thrower.lua
index c2a87f0d551..21a14bce085 100644
--- a/data-otservbr-global/monster/humanoids/lost_thrower.lua
+++ b/data-otservbr-global/monster/humanoids/lost_thrower.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Lost Thrower")
local monster = {}
monster.description = "a lost thrower"
-monster.experience = 1200
+monster.experience = 1500
monster.outfit = {
lookType = 539,
lookHead = 0,
@@ -61,7 +61,7 @@ monster.flags = {
healthHidden = false,
isBlockable = false,
canWalkOnEnergy = false,
- canWalkOnFire = false,
+ canWalkOnFire = true,
canWalkOnPoison = true,
}
@@ -117,7 +117,7 @@ monster.elements = {
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
{ type = COMBAT_DROWNDAMAGE, percent = 0 },
- { type = COMBAT_ICEDAMAGE, percent = 15 },
+ { type = COMBAT_ICEDAMAGE, percent = -5 },
{ type = COMBAT_HOLYDAMAGE, percent = 0 },
{ type = COMBAT_DEATHDAMAGE, percent = 10 },
}
diff --git a/data-otservbr-global/monster/magicals/armadile.lua b/data-otservbr-global/monster/magicals/armadile.lua
index beca89510f4..98665845d28 100644
--- a/data-otservbr-global/monster/magicals/armadile.lua
+++ b/data-otservbr-global/monster/magicals/armadile.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Armadile")
local monster = {}
monster.description = "an armadile"
-monster.experience = 2900
+monster.experience = 3200
monster.outfit = {
lookType = 487,
lookHead = 0,
@@ -56,8 +56,8 @@ monster.flags = {
canPushItems = false,
canPushCreatures = true,
staticAttackChance = 90,
- targetDistance = 4,
- runHealth = 300,
+ targetDistance = 1,
+ runHealth = 0,
healthHidden = false,
isBlockable = false,
canWalkOnEnergy = false,
@@ -102,9 +102,7 @@ monster.loot = {
monster.attacks = {
{ name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -150 },
- { name = "drunk", interval = 2000, chance = 15, radius = 4, effect = CONST_ME_FIREAREA, target = true, duration = 5000 },
- { name = "combat", interval = 2000, chance = 15, type = COMBAT_MANADRAIN, minDamage = -430, maxDamage = -550, range = 7, effect = CONST_ME_MAGIC_BLUE, target = false },
- -- poison
+ { name = "drunk", interval = 2000, chance = 10, radius = 4, effect = CONST_ME_FIREAREA, target = true, duration = 5000 },
{ name = "condition", type = CONDITION_POISON, interval = 2000, chance = 15, minDamage = -200, maxDamage = -400, radius = 4, effect = CONST_ME_POISONAREA, target = false },
}
@@ -112,14 +110,13 @@ monster.defenses = {
defense = 25,
armor = 66,
mitigation = 1.96,
- { name = "invisible", interval = 2000, chance = 15, effect = CONST_ME_MAGIC_RED },
}
monster.elements = {
{ type = COMBAT_PHYSICALDAMAGE, percent = 5 },
{ type = COMBAT_ENERGYDAMAGE, percent = 15 },
{ type = COMBAT_EARTHDAMAGE, percent = 100 },
- { type = COMBAT_FIREDAMAGE, percent = 20 },
+ { type = COMBAT_FIREDAMAGE, percent = 0 },
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
{ type = COMBAT_DROWNDAMAGE, percent = 0 },
diff --git a/data-otservbr-global/monster/magicals/choking_fear.lua b/data-otservbr-global/monster/magicals/choking_fear.lua
index 16361bab0e9..604ca5cdaa6 100644
--- a/data-otservbr-global/monster/magicals/choking_fear.lua
+++ b/data-otservbr-global/monster/magicals/choking_fear.lua
@@ -124,7 +124,7 @@ monster.defenses = {
monster.elements = {
{ type = COMBAT_PHYSICALDAMAGE, percent = 10 },
- { type = COMBAT_ENERGYDAMAGE, percent = 15 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 2 },
{ type = COMBAT_EARTHDAMAGE, percent = 100 },
{ type = COMBAT_FIREDAMAGE, percent = 100 },
{ type = COMBAT_LIFEDRAIN, percent = 0 },
diff --git a/data-otservbr-global/monster/magicals/phantasm.lua b/data-otservbr-global/monster/magicals/phantasm.lua
index 9dc266f8bb3..36c0baed405 100644
--- a/data-otservbr-global/monster/magicals/phantasm.lua
+++ b/data-otservbr-global/monster/magicals/phantasm.lua
@@ -70,7 +70,7 @@ monster.light = {
monster.summon = {
maxSummons = 4,
summons = {
- { name = "Phantasm Summon", chance = 20, interval = 2000, count = 4 },
+ { name = "Phantasm Summon", chance = 35, interval = 2000, count = 4 },
},
}
diff --git a/data-otservbr-global/monster/magicals/retching_horror.lua b/data-otservbr-global/monster/magicals/retching_horror.lua
index a6814343eaf..0479a7cb368 100644
--- a/data-otservbr-global/monster/magicals/retching_horror.lua
+++ b/data-otservbr-global/monster/magicals/retching_horror.lua
@@ -113,7 +113,7 @@ monster.defenses = {
monster.elements = {
{ type = COMBAT_PHYSICALDAMAGE, percent = 5 },
- { type = COMBAT_ENERGYDAMAGE, percent = 10 },
+ { type = COMBAT_ENERGYDAMAGE, percent = -3 },
{ type = COMBAT_EARTHDAMAGE, percent = 100 },
{ type = COMBAT_FIREDAMAGE, percent = 85 },
{ type = COMBAT_LIFEDRAIN, percent = 0 },
diff --git a/data-otservbr-global/monster/mammals/mutated_bat.lua b/data-otservbr-global/monster/mammals/mutated_bat.lua
index 211ca62d2ad..dfea480341b 100644
--- a/data-otservbr-global/monster/mammals/mutated_bat.lua
+++ b/data-otservbr-global/monster/mammals/mutated_bat.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Mutated Bat")
local monster = {}
monster.description = "a mutated bat"
-monster.experience = 615
+monster.experience = 750
monster.outfit = {
lookType = 307,
lookHead = 0,
diff --git a/data-otservbr-global/monster/plants/hideous_fungus.lua b/data-otservbr-global/monster/plants/hideous_fungus.lua
index a80ba080fd7..275adb03b2f 100644
--- a/data-otservbr-global/monster/plants/hideous_fungus.lua
+++ b/data-otservbr-global/monster/plants/hideous_fungus.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Hideous Fungus")
local monster = {}
monster.description = "a hideous fungus"
-monster.experience = 2900
+monster.experience = 3700
monster.outfit = {
lookType = 499,
lookHead = 0,
@@ -57,7 +57,7 @@ monster.flags = {
canPushCreatures = true,
staticAttackChance = 90,
targetDistance = 4,
- runHealth = 275,
+ runHealth = 0,
healthHidden = false,
isBlockable = false,
canWalkOnEnergy = true,
@@ -71,9 +71,9 @@ monster.light = {
}
monster.summon = {
- maxSummons = 2,
+ maxSummons = 1,
summons = {
- { name = "humorless fungus", chance = 10, interval = 2000, count = 2 },
+ { name = "humorless fungus", chance = 10, interval = 2000, count = 1 },
},
}
@@ -110,7 +110,6 @@ monster.attacks = {
{ name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -450 },
{ name = "combat", interval = 2000, chance = 15, type = COMBAT_EARTHDAMAGE, minDamage = -250, maxDamage = -430, range = 7, shootEffect = CONST_ANI_SMALLEARTH, effect = CONST_ME_SMALLPLANTS, target = false },
{ name = "combat", interval = 2000, chance = 15, type = COMBAT_ICEDAMAGE, minDamage = -250, maxDamage = -550, length = 8, spread = 0, shootEffect = CONST_ANI_SNOWBALL, effect = CONST_ME_ICEAREA, target = false },
- { name = "speed", interval = 2000, chance = 10, speedChange = -600, radius = 1, effect = CONST_ME_MAGIC_RED, target = true, duration = 60000 },
{ name = "drunk", interval = 2000, chance = 10, range = 7, radius = 5, shootEffect = CONST_ANI_SMALLSTONE, effect = CONST_ME_STUN, target = true, duration = 4000 },
-- poison
{ name = "condition", type = CONDITION_POISON, interval = 2000, chance = 10, minDamage = -400, maxDamage = -640, range = 7, radius = 3, effect = CONST_ME_HITBYPOISON, target = false },
@@ -121,14 +120,14 @@ monster.defenses = {
armor = 60,
mitigation = 1.74,
{ name = "combat", interval = 2000, chance = 15, type = COMBAT_HEALING, minDamage = 275, maxDamage = 350, effect = CONST_ME_MAGIC_BLUE, target = false },
- { name = "invisible", interval = 2000, chance = 10, effect = CONST_ME_MAGIC_BLUE },
+ { name = "invisible", interval = 2000, chance = 5, effect = CONST_ME_MAGIC_BLUE, duration = 2000 },
}
monster.elements = {
{ type = COMBAT_PHYSICALDAMAGE, percent = 0 },
{ type = COMBAT_ENERGYDAMAGE, percent = 15 },
{ type = COMBAT_EARTHDAMAGE, percent = 100 },
- { type = COMBAT_FIREDAMAGE, percent = 5 },
+ { type = COMBAT_FIREDAMAGE, percent = -5 },
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
{ type = COMBAT_DROWNDAMAGE, percent = 0 },
diff --git a/data-otservbr-global/monster/plants/humongous_fungus.lua b/data-otservbr-global/monster/plants/humongous_fungus.lua
index 942684e732c..09f301a6a59 100644
--- a/data-otservbr-global/monster/plants/humongous_fungus.lua
+++ b/data-otservbr-global/monster/plants/humongous_fungus.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Humongous Fungus")
local monster = {}
monster.description = "a humongous fungus"
-monster.experience = 2600
+monster.experience = 2900
monster.outfit = {
lookType = 488,
lookHead = 0,
@@ -104,7 +104,7 @@ monster.loot = {
monster.attacks = {
{ name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -330 },
{ name = "combat", interval = 2000, chance = 15, type = COMBAT_EARTHDAMAGE, minDamage = -180, maxDamage = -350, range = 7, shootEffect = CONST_ANI_SMALLEARTH, effect = CONST_ME_SMALLPLANTS, target = false },
- { name = "poisonfield", interval = 2000, chance = 20, radius = 4, target = false },
+ { name = "poisonfield", interval = 2000, chance = 10, radius = 4, target = false },
-- poison
{ name = "condition", type = CONDITION_POISON, interval = 2000, chance = 10, minDamage = -500, maxDamage = -1000, length = 8, spread = 0, effect = CONST_ME_GREEN_RINGS, target = false },
{ name = "combat", interval = 2000, chance = 10, type = COMBAT_LIFEDRAIN, minDamage = -130, maxDamage = -260, length = 5, spread = 0, effect = CONST_ME_MAGIC_RED, target = false },
@@ -117,14 +117,13 @@ monster.defenses = {
armor = 70,
mitigation = 2.02,
{ name = "combat", interval = 2000, chance = 10, type = COMBAT_HEALING, minDamage = 225, maxDamage = 380, effect = CONST_ME_MAGIC_BLUE, target = false },
- { name = "invisible", interval = 2000, chance = 15, effect = CONST_ME_MAGIC_BLUE },
}
monster.elements = {
{ type = COMBAT_PHYSICALDAMAGE, percent = 0 },
{ type = COMBAT_ENERGYDAMAGE, percent = 15 },
{ type = COMBAT_EARTHDAMAGE, percent = 100 },
- { type = COMBAT_FIREDAMAGE, percent = 5 },
+ { type = COMBAT_FIREDAMAGE, percent = -10 },
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
{ type = COMBAT_DROWNDAMAGE, percent = 0 },
diff --git a/data-otservbr-global/monster/quests/bigfoots_burden/bosses/abyssador.lua b/data-otservbr-global/monster/quests/bigfoots_burden/bosses/abyssador.lua
index 24c993fda5b..1ca0ff1ca75 100644
--- a/data-otservbr-global/monster/quests/bigfoots_burden/bosses/abyssador.lua
+++ b/data-otservbr-global/monster/quests/bigfoots_burden/bosses/abyssador.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Abyssador")
local monster = {}
monster.description = "Abyssador"
-monster.experience = 50000
+monster.experience = 400000
monster.outfit = {
lookType = 495,
lookHead = 0,
diff --git a/data-otservbr-global/monster/quests/bigfoots_burden/bosses/gnomevil.lua b/data-otservbr-global/monster/quests/bigfoots_burden/bosses/gnomevil.lua
index cd287d83709..8d410c85d26 100644
--- a/data-otservbr-global/monster/quests/bigfoots_burden/bosses/gnomevil.lua
+++ b/data-otservbr-global/monster/quests/bigfoots_burden/bosses/gnomevil.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Gnomevil")
local monster = {}
monster.description = "Gnomevil"
-monster.experience = 45000
+monster.experience = 400000
monster.outfit = {
lookType = 504,
lookHead = 0,
diff --git a/data-otservbr-global/monster/quests/bigfoots_burden/humorless_fungus.lua b/data-otservbr-global/monster/quests/bigfoots_burden/humorless_fungus.lua
index 19ab3230231..ead6b096fbf 100644
--- a/data-otservbr-global/monster/quests/bigfoots_burden/humorless_fungus.lua
+++ b/data-otservbr-global/monster/quests/bigfoots_burden/humorless_fungus.lua
@@ -13,8 +13,8 @@ monster.outfit = {
lookMount = 0,
}
-monster.health = 2500
-monster.maxHealth = 2500
+monster.health = 1600
+monster.maxHealth = 1600
monster.race = "venom"
monster.corpse = 16083
monster.speed = 115
@@ -70,17 +70,14 @@ monster.attacks = {
{ name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -475 },
{ name = "combat", interval = 2000, chance = 10, type = COMBAT_EARTHDAMAGE, minDamage = -40, maxDamage = -197, range = 7, shootEffect = CONST_ANI_SMALLEARTH, effect = CONST_ME_SMALLPLANTS, target = true },
{ name = "combat", interval = 2000, chance = 10, type = COMBAT_ICEDAMAGE, minDamage = 0, maxDamage = -525, range = 7, shootEffect = CONST_ANI_SNOWBALL, effect = CONST_ME_ICEAREA, target = true },
- -- poison
{ name = "condition", type = CONDITION_POISON, interval = 2000, chance = 10, minDamage = -400, maxDamage = -640, range = 7, radius = 3, effect = CONST_ME_HITBYPOISON, target = false },
- { name = "drunk", interval = 2000, chance = 10, range = 7, radius = 4, effect = CONST_ME_STUN, target = true, duration = 4000 },
}
monster.defenses = {
defense = 0,
armor = 0,
- -- mitigation = ???,
{ name = "combat", interval = 2000, chance = 5, type = COMBAT_HEALING, minDamage = 0, maxDamage = 230, effect = CONST_ME_MAGIC_BLUE, target = false },
- { name = "invisible", interval = 2000, chance = 10, effect = CONST_ME_MAGIC_BLUE },
+ { name = "invisible", interval = 2000, chance = 5, effect = CONST_ME_MAGIC_BLUE },
}
monster.elements = {
diff --git a/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_count_of_the_core.lua b/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_count_of_the_core.lua
index d611e78d84b..17cb6520d29 100644
--- a/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_count_of_the_core.lua
+++ b/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_count_of_the_core.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("The Count of the Core")
local monster = {}
monster.description = "The Count Of The Core"
-monster.experience = 40000
+monster.experience = 300000
monster.outfit = {
lookType = 1046,
lookHead = 0,
diff --git a/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_duke_of_the_depths.lua b/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_duke_of_the_depths.lua
index d654288c405..99053b93882 100644
--- a/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_duke_of_the_depths.lua
+++ b/data-otservbr-global/monster/quests/dangerous_depth/bosses/the_duke_of_the_depths.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("The Duke of the Depths")
local monster = {}
monster.description = "The Duke Of The Depths"
-monster.experience = 40000
+monster.experience = 300000
monster.outfit = {
lookType = 1047,
lookHead = 0,
diff --git a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/ferumbras_mortal_shell.lua b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/ferumbras_mortal_shell.lua
index 90b3124369a..7ad855bc88a 100644
--- a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/ferumbras_mortal_shell.lua
+++ b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/ferumbras_mortal_shell.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Ferumbras Mortal Shell")
local monster = {}
monster.description = "Ferumbras Mortal Shell"
-monster.experience = 500000
+monster.experience = 2000000
monster.outfit = {
lookType = 229,
lookHead = 0,
diff --git a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/mazoran.lua b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/mazoran.lua
index 0e0822e3d72..3f32bac95d4 100644
--- a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/mazoran.lua
+++ b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/mazoran.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Mazoran")
local monster = {}
monster.description = "Mazoran"
-monster.experience = 250000
+monster.experience = 500000
monster.outfit = {
lookType = 842,
lookHead = 77,
diff --git a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/plagirath.lua b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/plagirath.lua
index 625f10cf7fc..364b4f864a8 100644
--- a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/plagirath.lua
+++ b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/plagirath.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Plagirath")
local monster = {}
monster.description = "Plagirath"
-monster.experience = 250000
+monster.experience = 500000
monster.outfit = {
lookType = 862,
lookHead = 84,
diff --git a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/ragiaz.lua b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/ragiaz.lua
index 9701dc7d0f9..df016edfd84 100644
--- a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/ragiaz.lua
+++ b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/ragiaz.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Ragiaz")
local monster = {}
monster.description = "Ragiaz"
-monster.experience = 250000
+monster.experience = 500000
monster.outfit = {
lookType = 862,
lookHead = 76,
diff --git a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/razzagorn.lua b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/razzagorn.lua
index 67d218c5ee0..066e22237c0 100644
--- a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/razzagorn.lua
+++ b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/razzagorn.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Razzagorn")
local monster = {}
monster.description = "Razzagorn"
-monster.experience = 250000
+monster.experience = 500000
monster.outfit = {
lookType = 842,
lookHead = 78,
diff --git a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/shulgrax.lua b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/shulgrax.lua
index c0de2c0c362..72c908104af 100644
--- a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/shulgrax.lua
+++ b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/shulgrax.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Shulgrax")
local monster = {}
monster.description = "Shulgrax"
-monster.experience = 250000
+monster.experience = 500000
monster.outfit = {
lookType = 842,
lookHead = 0,
diff --git a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/tarbaz.lua b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/tarbaz.lua
index 583167460e1..30b99353cdf 100644
--- a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/tarbaz.lua
+++ b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/tarbaz.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Tarbaz")
local monster = {}
monster.description = "Tarbaz"
-monster.experience = 250000
+monster.experience = 500000
monster.outfit = {
lookType = 842,
lookHead = 0,
diff --git a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/zamulosh.lua b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/zamulosh.lua
index 5a441ff10f1..03fa92d3394 100644
--- a/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/zamulosh.lua
+++ b/data-otservbr-global/monster/quests/ferumbras_ascendant/bosses/zamulosh.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Zamulosh")
local monster = {}
monster.description = "Zamulosh"
-monster.experience = 250000
+monster.experience = 500000
monster.outfit = {
lookType = 862,
lookHead = 16,
diff --git a/data-otservbr-global/monster/quests/grave_danger/bosses/lord_azaram.lua b/data-otservbr-global/monster/quests/grave_danger/bosses/lord_azaram.lua
index 701a0b6f92b..8507c3a0c1c 100644
--- a/data-otservbr-global/monster/quests/grave_danger/bosses/lord_azaram.lua
+++ b/data-otservbr-global/monster/quests/grave_danger/bosses/lord_azaram.lua
@@ -17,8 +17,8 @@ monster.events = {
"GraveDangerBossDeath",
}
-monster.health = 75000
-monster.maxHealth = 75000
+monster.health = 300000
+monster.maxHealth = 300000
monster.race = "venom"
monster.corpse = 31599
monster.speed = 125
diff --git a/data-otservbr-global/monster/quests/in_service_of_yalahar/inky.lua b/data-otservbr-global/monster/quests/in_service_of_yalahar/inky.lua
index 49025f4c6c4..bbc58881ee4 100644
--- a/data-otservbr-global/monster/quests/in_service_of_yalahar/inky.lua
+++ b/data-otservbr-global/monster/quests/in_service_of_yalahar/inky.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Inky")
local monster = {}
monster.description = "Inky"
-monster.experience = 250
+monster.experience = 700
monster.outfit = {
lookType = 46,
lookHead = 0,
diff --git a/data-otservbr-global/monster/quests/in_service_of_yalahar/sharptooth.lua b/data-otservbr-global/monster/quests/in_service_of_yalahar/sharptooth.lua
index 583df08b8b5..0325642344c 100644
--- a/data-otservbr-global/monster/quests/in_service_of_yalahar/sharptooth.lua
+++ b/data-otservbr-global/monster/quests/in_service_of_yalahar/sharptooth.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Sharptooth")
local monster = {}
monster.description = "Sharptooth"
-monster.experience = 1600
+monster.experience = 3000
monster.outfit = {
lookType = 20,
lookHead = 0,
diff --git a/data-otservbr-global/monster/quests/the_dream_courts/bosses/faceless_bane.lua b/data-otservbr-global/monster/quests/the_dream_courts/bosses/faceless_bane.lua
index e4d8553c46a..868fe08e756 100644
--- a/data-otservbr-global/monster/quests/the_dream_courts/bosses/faceless_bane.lua
+++ b/data-otservbr-global/monster/quests/the_dream_courts/bosses/faceless_bane.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Faceless Bane")
local monster = {}
monster.description = "Faceless Bane"
-monster.experience = 30000
+monster.experience = 20000
monster.outfit = {
lookType = 1119,
lookHead = 0,
@@ -22,7 +22,11 @@ monster.manaCost = 0
monster.changeTarget = {
interval = 4000,
- chance = 10,
+ chance = 20,
+}
+
+monster.reflects = {
+ { type = COMBAT_DEATHDAMAGE, percent = 90 },
}
monster.bosstiary = {
@@ -131,11 +135,7 @@ monster.elements = {
{ type = COMBAT_DROWNDAMAGE, percent = 0 },
{ type = COMBAT_ICEDAMAGE, percent = 0 },
{ type = COMBAT_HOLYDAMAGE, percent = 0 },
- { type = COMBAT_DEATHDAMAGE, percent = 99 },
-}
-
-monster.heals = {
- { type = COMBAT_DEATHDAMAGE, percent = 100 },
+ { type = COMBAT_DEATHDAMAGE, percent = 50 },
}
monster.immunities = {
@@ -149,6 +149,11 @@ mType.onThink = function(monster, interval) end
mType.onAppear = function(monster, creature)
if monster:getType():isRewardBoss() then
+ -- reset global storage state to default / ensure sqm's reset for the next team
+ Game.setStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.Deaths, -1)
+ Game.setStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.StepsOn, -1)
+ Game.setStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.ResetSteps, 1)
+ monster:registerEvent("facelessBaneImmunity")
monster:setReward(true)
end
end
diff --git a/data-otservbr-global/monster/reptiles/seacrest_serpent.lua b/data-otservbr-global/monster/reptiles/seacrest_serpent.lua
index 9be39aaa571..04b7dba7416 100644
--- a/data-otservbr-global/monster/reptiles/seacrest_serpent.lua
+++ b/data-otservbr-global/monster/reptiles/seacrest_serpent.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Seacrest Serpent")
local monster = {}
monster.description = "a seacrest serpent"
-monster.experience = 2600
+monster.experience = 2900
monster.outfit = {
lookType = 675,
lookHead = 0,
diff --git a/data-otservbr-global/monster/reptiles/young_sea_serpent.lua b/data-otservbr-global/monster/reptiles/young_sea_serpent.lua
index 961ff8204c6..98e8744b7b1 100644
--- a/data-otservbr-global/monster/reptiles/young_sea_serpent.lua
+++ b/data-otservbr-global/monster/reptiles/young_sea_serpent.lua
@@ -108,7 +108,7 @@ monster.defenses = {
monster.elements = {
{ type = COMBAT_PHYSICALDAMAGE, percent = -20 },
{ type = COMBAT_ENERGYDAMAGE, percent = -10 },
- { type = COMBAT_EARTHDAMAGE, percent = 100 },
+ { type = COMBAT_EARTHDAMAGE, percent = -5 },
{ type = COMBAT_FIREDAMAGE, percent = 30 },
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
diff --git a/data-otservbr-global/monster/undeads/betrayed_wraith.lua b/data-otservbr-global/monster/undeads/betrayed_wraith.lua
index 0c0648acda5..201887a7712 100644
--- a/data-otservbr-global/monster/undeads/betrayed_wraith.lua
+++ b/data-otservbr-global/monster/undeads/betrayed_wraith.lua
@@ -112,7 +112,7 @@ monster.defenses = {
monster.elements = {
{ type = COMBAT_PHYSICALDAMAGE, percent = 0 },
- { type = COMBAT_ENERGYDAMAGE, percent = 100 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 10 },
{ type = COMBAT_EARTHDAMAGE, percent = 100 },
{ type = COMBAT_FIREDAMAGE, percent = 100 },
{ type = COMBAT_LIFEDRAIN, percent = 100 },
diff --git a/data-otservbr-global/monster/undeads/blightwalker.lua b/data-otservbr-global/monster/undeads/blightwalker.lua
index 28706adeb50..d900484b19f 100644
--- a/data-otservbr-global/monster/undeads/blightwalker.lua
+++ b/data-otservbr-global/monster/undeads/blightwalker.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Blightwalker")
local monster = {}
monster.description = "a blightwalker"
-monster.experience = 5850
+monster.experience = 6400
monster.outfit = {
lookType = 246,
lookHead = 0,
@@ -108,9 +108,8 @@ 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 },
+ { name = "speed", interval = 2000, chance = 10, speedChange = -300, range = 7, shootEffect = CONST_ANI_POISON, target = true, duration = 15000 },
}
monster.defenses = {
@@ -127,7 +126,7 @@ monster.elements = {
{ type = COMBAT_LIFEDRAIN, percent = 0 },
{ type = COMBAT_MANADRAIN, percent = 0 },
{ type = COMBAT_DROWNDAMAGE, percent = 0 },
- { type = COMBAT_ICEDAMAGE, percent = 50 },
+ { type = COMBAT_ICEDAMAGE, percent = 15 },
{ type = COMBAT_HOLYDAMAGE, percent = -30 },
{ type = COMBAT_DEATHDAMAGE, percent = 100 },
}
diff --git a/data-otservbr-global/monster/undeads/crypt_warrior.lua b/data-otservbr-global/monster/undeads/crypt_warrior.lua
index 5de0ab637ba..dad0cbe7529 100644
--- a/data-otservbr-global/monster/undeads/crypt_warrior.lua
+++ b/data-otservbr-global/monster/undeads/crypt_warrior.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Crypt Warrior")
local monster = {}
monster.description = "a crypt warrior"
-monster.experience = 4200
+monster.experience = 6050
monster.outfit = {
lookType = 298,
lookHead = 0,
diff --git a/data-otservbr-global/monster/undeads/falcon_knight.lua b/data-otservbr-global/monster/undeads/falcon_knight.lua
index 54a23130758..2e2ac28a1fe 100644
--- a/data-otservbr-global/monster/undeads/falcon_knight.lua
+++ b/data-otservbr-global/monster/undeads/falcon_knight.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Falcon Knight")
local monster = {}
monster.description = "a falcon knight"
-monster.experience = 5985
+monster.experience = 6300
monster.outfit = {
lookType = 1071,
lookHead = 57,
diff --git a/data-otservbr-global/monster/undeads/falcon_paladin.lua b/data-otservbr-global/monster/undeads/falcon_paladin.lua
index 799143fe943..cc6a41571e4 100644
--- a/data-otservbr-global/monster/undeads/falcon_paladin.lua
+++ b/data-otservbr-global/monster/undeads/falcon_paladin.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Falcon Paladin")
local monster = {}
monster.description = "a falcon paladin"
-monster.experience = 6544
+monster.experience = 6900
monster.outfit = {
lookType = 1071,
lookHead = 57,
diff --git a/data-otservbr-global/monster/undeads/hand_of_cursed_fate.lua b/data-otservbr-global/monster/undeads/hand_of_cursed_fate.lua
index b1f76414dd4..527d0708730 100644
--- a/data-otservbr-global/monster/undeads/hand_of_cursed_fate.lua
+++ b/data-otservbr-global/monster/undeads/hand_of_cursed_fate.lua
@@ -108,7 +108,7 @@ monster.loot = {
monster.attacks = {
{ name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -520, condition = { type = CONDITION_POISON, totalDamage = 380, interval = 4000 } },
- { name = "combat", interval = 2000, chance = 15, type = COMBAT_MANADRAIN, minDamage = 0, maxDamage = -920, range = 1, target = false },
+ { name = "combat", interval = 2000, chance = 15, type = COMBAT_MANADRAIN, minDamage = 0, maxDamage = -620, range = 1, target = false },
{ name = "drunk", interval = 2000, chance = 10, radius = 4, effect = CONST_ME_SMALLCLOUDS, target = false, duration = 3000 },
{ name = "combat", interval = 2000, chance = 15, type = COMBAT_LIFEDRAIN, minDamage = -220, maxDamage = -880, range = 1, effect = CONST_ME_SMALLCLOUDS, target = false },
}
@@ -124,7 +124,7 @@ monster.defenses = {
monster.elements = {
{ type = COMBAT_PHYSICALDAMAGE, percent = 0 },
- { type = COMBAT_ENERGYDAMAGE, percent = 100 },
+ { type = COMBAT_ENERGYDAMAGE, percent = 5 },
{ type = COMBAT_EARTHDAMAGE, percent = 100 },
{ type = COMBAT_FIREDAMAGE, percent = 100 },
{ type = COMBAT_LIFEDRAIN, percent = 0 },
diff --git a/data-otservbr-global/monster/undeads/skeleton_elite_warrior.lua b/data-otservbr-global/monster/undeads/skeleton_elite_warrior.lua
index a0cc7545b99..25ebd9c1f6d 100644
--- a/data-otservbr-global/monster/undeads/skeleton_elite_warrior.lua
+++ b/data-otservbr-global/monster/undeads/skeleton_elite_warrior.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Skeleton Elite Warrior")
local monster = {}
monster.description = "a skeleton elite warrior"
-monster.experience = 4500
+monster.experience = 4800
monster.outfit = {
lookType = 298,
lookHead = 0,
diff --git a/data-otservbr-global/monster/undeads/undead_dragon.lua b/data-otservbr-global/monster/undeads/undead_dragon.lua
index 2ab63af8b77..b9039c5d914 100644
--- a/data-otservbr-global/monster/undeads/undead_dragon.lua
+++ b/data-otservbr-global/monster/undeads/undead_dragon.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Undead Dragon")
local monster = {}
monster.description = "an undead dragon"
-monster.experience = 7200
+monster.experience = 7500
monster.outfit = {
lookType = 231,
lookHead = 0,
diff --git a/data-otservbr-global/monster/undeads/undead_elite_gladiator.lua b/data-otservbr-global/monster/undeads/undead_elite_gladiator.lua
index 0f66c87cb21..10d1d5b08bc 100644
--- a/data-otservbr-global/monster/undeads/undead_elite_gladiator.lua
+++ b/data-otservbr-global/monster/undeads/undead_elite_gladiator.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Undead Elite Gladiator")
local monster = {}
monster.description = "an undead elite gladiator"
-monster.experience = 4740
+monster.experience = 5090
monster.outfit = {
lookType = 306,
lookHead = 0,
diff --git a/data-otservbr-global/monster/vermins/cave_devourer.lua b/data-otservbr-global/monster/vermins/cave_devourer.lua
index 1283ac20b81..17d89dfb176 100644
--- a/data-otservbr-global/monster/vermins/cave_devourer.lua
+++ b/data-otservbr-global/monster/vermins/cave_devourer.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Cave Devourer")
local monster = {}
monster.description = "a cave devourer"
-monster.experience = 2380
+monster.experience = 3380
monster.outfit = {
lookType = 1036,
lookHead = 0,
@@ -88,7 +88,7 @@ monster.loot = {
{ name = "slime heart", chance = 13770, maxCount = 4 },
{ name = "cave devourer legs", chance = 17160 },
{ id = 3049, chance = 2540 }, -- stealth ring
- { name = "suspicious device", chance = 420 },
+ { name = "suspicious device", chance = 850 },
}
monster.attacks = {
diff --git a/data-otservbr-global/monster/vermins/chasm_spawn.lua b/data-otservbr-global/monster/vermins/chasm_spawn.lua
index 0764367a1d0..9f27f57b006 100644
--- a/data-otservbr-global/monster/vermins/chasm_spawn.lua
+++ b/data-otservbr-global/monster/vermins/chasm_spawn.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Chasm Spawn")
local monster = {}
monster.description = "a chasm spawn"
-monster.experience = 2700
+monster.experience = 3600
monster.outfit = {
lookType = 1037,
lookHead = 0,
@@ -89,7 +89,7 @@ monster.loot = {
{ name = "green crystal shard", chance = 7850 },
{ name = "violet crystal shard", chance = 4690 },
{ name = "mushroom backpack", chance = 610 },
- { name = "suspicious device", chance = 520 },
+ { name = "suspicious device", chance = 850 },
}
monster.attacks = {
diff --git a/data-otservbr-global/monster/vermins/deepworm.lua b/data-otservbr-global/monster/vermins/deepworm.lua
index 91b2ded8752..21775b2c673 100644
--- a/data-otservbr-global/monster/vermins/deepworm.lua
+++ b/data-otservbr-global/monster/vermins/deepworm.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Deepworm")
local monster = {}
monster.description = "a deepworm"
-monster.experience = 2300
+monster.experience = 2520
monster.outfit = {
lookType = 1033,
lookHead = 0,
diff --git a/data-otservbr-global/monster/vermins/diremaw.lua b/data-otservbr-global/monster/vermins/diremaw.lua
index b89c47c99e7..fd74c4e2be6 100644
--- a/data-otservbr-global/monster/vermins/diremaw.lua
+++ b/data-otservbr-global/monster/vermins/diremaw.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Diremaw")
local monster = {}
monster.description = "a diremaw"
-monster.experience = 2500
+monster.experience = 2770
monster.outfit = {
lookType = 1034,
lookHead = 0,
diff --git a/data-otservbr-global/monster/vermins/drillworm.lua b/data-otservbr-global/monster/vermins/drillworm.lua
index 3cf92fce18a..a435dc16ca3 100644
--- a/data-otservbr-global/monster/vermins/drillworm.lua
+++ b/data-otservbr-global/monster/vermins/drillworm.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Drillworm")
local monster = {}
monster.description = "a drillworm"
-monster.experience = 858
+monster.experience = 1200
monster.outfit = {
lookType = 527,
lookHead = 0,
diff --git a/data-otservbr-global/monster/vermins/tunnel_tyrant.lua b/data-otservbr-global/monster/vermins/tunnel_tyrant.lua
index f8e3b7bfb03..c2b8eb2df90 100644
--- a/data-otservbr-global/monster/vermins/tunnel_tyrant.lua
+++ b/data-otservbr-global/monster/vermins/tunnel_tyrant.lua
@@ -2,7 +2,7 @@ local mType = Game.createMonsterType("Tunnel Tyrant")
local monster = {}
monster.description = "a tunnel tyrant"
-monster.experience = 3400
+monster.experience = 4420
monster.outfit = {
lookType = 1035,
lookHead = 0,
@@ -87,7 +87,7 @@ monster.loot = {
{ name = "crystal mace", chance = 1580 },
{ id = 23508, chance = 3010 }, -- energy vein
{ name = "crystalline armor", chance = 860 },
- { name = "suspicious device", chance = 1290 },
+ { name = "suspicious device", chance = 1850 },
}
monster.attacks = {
diff --git a/data-otservbr-global/npc/emael.lua b/data-otservbr-global/npc/emael.lua
index 79111f8f324..4fff95b1b95 100644
--- a/data-otservbr-global/npc/emael.lua
+++ b/data-otservbr-global/npc/emael.lua
@@ -69,7 +69,7 @@ local function creatureSayCallback(npc, creature, type, message)
npcHandler:say("Ah, I see you killed a lot of dangerous creatures. Here's your podium of vigour!", npc, creature)
local inbox = player:getStoreInbox()
local inboxItems = inbox:getItems()
- if inbox and #inboxItems <= inbox:getMaxCapacity() then
+ if inbox and #inboxItems < inbox:getMaxCapacity() then
local decoKit = inbox:addItem(ITEM_DECORATION_KIT, 1)
if decoKit then
decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "Unwrap it in your own house to create a <" .. ItemType(38707):getName() .. ">.")
diff --git a/data-otservbr-global/npc/emperor_kruzak.lua b/data-otservbr-global/npc/emperor_kruzak.lua
index 4f838396df0..daf0742f615 100644
--- a/data-otservbr-global/npc/emperor_kruzak.lua
+++ b/data-otservbr-global/npc/emperor_kruzak.lua
@@ -82,7 +82,7 @@ local function creatureSayCallback(npc, creature, type, message)
if player:getMoney() + player:getBankBalance() >= 500000000 then
local inbox = player:getStoreInbox()
local inboxItems = inbox:getItems()
- if inbox and #inboxItems <= inbox:getMaxCapacity() then
+ if inbox and #inboxItems < inbox:getMaxCapacity() then
local decoKit = inbox:addItem(ITEM_DECORATION_KIT, 1)
local decoItemName = ItemType(31510):getName()
decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "You bought this item in the Store.\nUnwrap it in your own house to create a " .. decoItemName .. ".")
diff --git a/data-otservbr-global/npc/hireling.lua b/data-otservbr-global/npc/hireling.lua
index aad7785079d..6897bafdcf7 100644
--- a/data-otservbr-global/npc/hireling.lua
+++ b/data-otservbr-global/npc/hireling.lua
@@ -521,7 +521,7 @@ function createHirelingType(HirelingName)
local inboxItems = inbox:getItems()
if player:getFreeCapacity() < itType:getWeight(1) then
npcHandler:say("Sorry, but you don't have enough capacity.", npc, creature)
- elseif not inbox or #inboxItems > inbox:getMaxCapacity() then
+ elseif not inbox or #inboxItems >= inbox:getMaxCapacity() then
player:getPosition():sendMagicEffect(CONST_ME_POFF)
npcHandler:say("Sorry, you don't have enough room on your inbox", npc, creature)
elseif not player:removeMoneyBank(15000) then
diff --git a/data-otservbr-global/npc/king_tibianus.lua b/data-otservbr-global/npc/king_tibianus.lua
index de47e45acca..d9968357fa1 100644
--- a/data-otservbr-global/npc/king_tibianus.lua
+++ b/data-otservbr-global/npc/king_tibianus.lua
@@ -87,7 +87,7 @@ local function creatureSayCallback(npc, creature, type, message)
if player:getMoney() + player:getBankBalance() >= 500000000 then
local inbox = player:getStoreInbox()
local inboxItems = inbox:getItems()
- if inbox and #inboxItems <= inbox:getMaxCapacity() then
+ if inbox and #inboxItems < inbox:getMaxCapacity() then
local decoKit = inbox:addItem(ITEM_DECORATION_KIT, 1)
local decoItemName = ItemType(31510):getName()
decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "Unwrap it in your own house to create a " .. decoItemName .. ".")
diff --git a/data-otservbr-global/npc/queen_eloise.lua b/data-otservbr-global/npc/queen_eloise.lua
index a9397061a56..0467bb6e74a 100644
--- a/data-otservbr-global/npc/queen_eloise.lua
+++ b/data-otservbr-global/npc/queen_eloise.lua
@@ -77,7 +77,7 @@ local function creatureSayCallback(npc, creature, type, message)
if player:getMoney() + player:getBankBalance() >= 500000000 then
local inbox = player:getStoreInbox()
local inboxItems = inbox:getItems()
- if inbox and #inboxItems <= inbox:getMaxCapacity() then
+ if inbox and #inboxItems < inbox:getMaxCapacity() then
local decoKit = inbox:addItem(ITEM_DECORATION_KIT, 1)
local decoItemName = ItemType(31510):getName()
decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "You bought this item in the Store.\nUnwrap it in your own house to create a " .. decoItemName .. ".")
diff --git a/data-otservbr-global/npc/sam.lua b/data-otservbr-global/npc/sam.lua
index cd3a517c147..fdca8c02f01 100644
--- a/data-otservbr-global/npc/sam.lua
+++ b/data-otservbr-global/npc/sam.lua
@@ -210,6 +210,7 @@ npcConfig.shop = {
{ itemName = "legion helmet", clientId = 3374, sell = 22 },
{ itemName = "longsword", clientId = 3285, buy = 160, sell = 51 },
{ itemName = "mace", clientId = 3286, buy = 90, sell = 30 },
+ { itemName = "magic plate armor", clientId = 3366, sell = 6400 },
{ itemName = "morning star", clientId = 3282, buy = 430, sell = 100 },
{ itemName = "orcish axe", clientId = 3316, sell = 350 },
{ itemName = "plate armor", clientId = 3357, buy = 1200, sell = 400 },
@@ -227,16 +228,16 @@ npcConfig.shop = {
{ itemName = "steel shield", clientId = 3409, buy = 240, sell = 80 },
{ itemName = "studded armor", clientId = 3378, buy = 90, sell = 25 },
{ itemName = "studded club", clientId = 3336, sell = 10 },
- { itemName = "studded helmet", clientId = 3376, buy = 63 },
- { itemName = "studded legs", clientId = 3362, buy = 50 },
- { itemName = "studded shield", clientId = 3426, buy = 50 },
- { itemName = "sword", clientId = 3264, buy = 85 },
- { itemName = "throwing knife", clientId = 3298, buy = 25 },
- { itemName = "two handed sword", clientId = 3265, buy = 950 },
- { itemName = "viking helmet", clientId = 3367, buy = 265 },
- { itemName = "viking shield", clientId = 3431, buy = 260 },
- { itemName = "war hammer", clientId = 3279, buy = 10000 },
- { itemName = "wooden shield", clientId = 3412, buy = 15 },
+ { itemName = "studded helmet", clientId = 3376, buy = 63, sell = 20 },
+ { itemName = "studded legs", clientId = 3362, buy = 50, sell = 15 },
+ { itemName = "studded shield", clientId = 3426, buy = 50, sell = 16 },
+ { itemName = "sword", clientId = 3264, buy = 85, sell = 25 },
+ { itemName = "throwing knife", clientId = 3298, buy = 25, sell = 2 },
+ { itemName = "two handed sword", clientId = 3265, buy = 950, sell = 450 },
+ { itemName = "viking helmet", clientId = 3367, buy = 265, sell = 66 },
+ { itemName = "viking shield", clientId = 3431, buy = 260, sell = 85 },
+ { itemName = "war hammer", clientId = 3279, buy = 10000, sell = 470 },
+ { itemName = "wooden shield", clientId = 3412, buy = 15, sell = 5 },
}
-- On buy npc shop message
npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost)
diff --git a/data-otservbr-global/npc/walter_jaeger.lua b/data-otservbr-global/npc/walter_jaeger.lua
index 6911d7ed323..6b0e26075d6 100644
--- a/data-otservbr-global/npc/walter_jaeger.lua
+++ b/data-otservbr-global/npc/walter_jaeger.lua
@@ -283,7 +283,7 @@ local function processItemInboxPurchase(player, name, id)
local inbox = player:getStoreInbox()
local inboxItems = inbox:getItems()
- if inbox and #inboxItems <= inbox:getMaxCapacity() then
+ if inbox and #inboxItems < inbox:getMaxCapacity() then
local decoKit = inbox:addItem(ITEM_DECORATION_KIT, 1)
if decoKit then
decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "You bought this item with the Walter Jaeger.\nUnwrap it in your own house to create a <" .. name .. ">.")
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/grand_,master_oberon.lua b/data-otservbr-global/scripts/actions/bosses_levers/grand_master_oberon.lua
similarity index 100%
rename from data-otservbr-global/scripts/actions/bosses_levers/grand_,master_oberon.lua
rename to data-otservbr-global/scripts/actions/bosses_levers/grand_master_oberon.lua
diff --git a/data-otservbr-global/scripts/actions/bosses_levers/the_fear_feaster.lua b/data-otservbr-global/scripts/actions/bosses_levers/the_fear_feaster.lua
index af870accd41..41dfa1ffe33 100644
--- a/data-otservbr-global/scripts/actions/bosses_levers/the_fear_feaster.lua
+++ b/data-otservbr-global/scripts/actions/bosses_levers/the_fear_feaster.lua
@@ -15,7 +15,7 @@ local config = {
from = Position(33705, 31463, 14),
to = Position(33719, 31477, 14),
},
- exit = Position(33609, 31499, 10),
+ exit = Position(33609, 31495, 10),
}
local lever = BossLever(config)
diff --git a/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua b/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua
index ad0e8d49b73..6f7e00e77f6 100644
--- a/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua
+++ b/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua
@@ -60,15 +60,15 @@ local questTable = {
{ storage = Storage.InServiceofYalahar.DoorToMatrix, storageValue = 1 },
{ storage = Storage.InServiceofYalahar.DoorToQuara, storageValue = 1 },
{ storage = Storage.CultsOfTibia.Questline, storageValue = 7 },
- { storage = Storage.CultsOfTibia.Minotaurs.jamesfrancisTask, storageValue = 1 },
+ { storage = Storage.CultsOfTibia.Minotaurs.JamesfrancisTask, storageValue = 1 },
{ storage = Storage.CultsOfTibia.Minotaurs.Mission, storageValue = 1 },
- { storage = Storage.CultsOfTibia.Minotaurs.bossTimer, storageValue = 1 },
+ { storage = Storage.CultsOfTibia.Minotaurs.BossTimer, storageValue = 1 },
{ storage = Storage.CultsOfTibia.MotA.Mission, storageValue = 1 },
- { storage = Storage.CultsOfTibia.MotA.Pedra1, storageValue = 1 },
- { storage = Storage.CultsOfTibia.MotA.Pedra2, storageValue = 1 },
- { storage = Storage.CultsOfTibia.MotA.Pedra3, storageValue = 1 },
- { storage = Storage.CultsOfTibia.MotA.Respostas, storageValue = 1 },
- { storage = Storage.CultsOfTibia.MotA.Perguntaid, storageValue = 1 },
+ { storage = Storage.CultsOfTibia.MotA.Stone1, storageValue = 1 },
+ { storage = Storage.CultsOfTibia.MotA.Stone2, storageValue = 1 },
+ { storage = Storage.CultsOfTibia.MotA.Stone3, storageValue = 1 },
+ { storage = Storage.CultsOfTibia.MotA.Answer, storageValue = 1 },
+ { storage = Storage.CultsOfTibia.MotA.QuestionId, storageValue = 1 },
{ storage = Storage.CultsOfTibia.Barkless.Mission, storageValue = 1 },
{ storage = Storage.CultsOfTibia.Barkless.sulphur, storageValue = 4 },
{ storage = Storage.CultsOfTibia.Barkless.Tar, storageValue = 3 },
@@ -76,19 +76,19 @@ local questTable = {
{ storage = Storage.CultsOfTibia.Barkless.Objects, storageValue = 1 },
{ storage = Storage.CultsOfTibia.Barkless.Temp, storageValue = 1 },
{ storage = Storage.CultsOfTibia.Orcs.Mission, storageValue = 1 },
- { storage = Storage.CultsOfTibia.Orcs.lookType, storageValue = 1 },
- { storage = Storage.CultsOfTibia.Orcs.bossTimer, storageValue = 1 },
+ { storage = Storage.CultsOfTibia.Orcs.LookType, storageValue = 1 },
+ { storage = Storage.CultsOfTibia.Orcs.BossTimer, storageValue = 1 },
{ storage = Storage.CultsOfTibia.Life.Mission, storageValue = 7 },
- { storage = Storage.CultsOfTibia.Life.bossTimer, storageValue = 1 },
+ { storage = Storage.CultsOfTibia.Life.BossTimer, storageValue = 1 },
{ storage = Storage.CultsOfTibia.Humans.Mission, storageValue = 1 },
{ storage = Storage.CultsOfTibia.Humans.Vaporized, storageValue = 1 },
{ storage = Storage.CultsOfTibia.Humans.Decaying, storageValue = 1 },
- { storage = Storage.CultsOfTibia.Humans.bossTimer, storageValue = 1 },
+ { storage = Storage.CultsOfTibia.Humans.BossTimer, storageValue = 1 },
{ storage = Storage.CultsOfTibia.Misguided.Mission, storageValue = 1 },
{ storage = Storage.CultsOfTibia.Misguided.Monsters, storageValue = 1 },
{ storage = Storage.CultsOfTibia.Misguided.Exorcisms, storageValue = 1 },
{ storage = Storage.CultsOfTibia.Misguided.Time, storageValue = 1 },
- { storage = Storage.CultsOfTibia.Misguided.bossTimer, storageValue = 1 },
+ { storage = Storage.CultsOfTibia.Misguided.BossTimer, storageValue = 1 },
{ storage = Storage.CultsOfTibia.Minotaurs.EntranceAccessDoor, storageValue = 1 },
{ storage = Storage.CultsOfTibia.Minotaurs.AccessDoor, storageValue = 1 },
{ storage = Storage.ExplorerSociety.QuestLine, storageValue = 1 },
@@ -119,6 +119,7 @@ local questTable = {
{ storage = Storage.ForgottenKnowledge.LloydKilled, storageValue = 1 },
{ storage = Storage.ForgottenKnowledge.LadyTenebrisKilled, storageValue = 1 },
{ storage = Storage.ForgottenKnowledge.AccessMachine, storageValue = 1 },
+ { storage = Storage.ForgottenKnowledge.AccessLavaTeleport, storageValue = 1 },
{ storage = Storage.BarbarianTest.Questline, storageValue = 8 },
{ storage = Storage.BarbarianTest.Mission01, storageValue = 3 },
{ storage = Storage.BarbarianTest.Mission02, storageValue = 3 },
@@ -146,8 +147,8 @@ local questTable = {
{ storage = Storage.DjinnWar.MaridFaction.Mission02, storageValue = 2 },
{ storage = Storage.DjinnWar.MaridFaction.RataMari, storageValue = 2 },
{ storage = Storage.DjinnWar.MaridFaction.Mission03, storageValue = 3 },
- { storage = Storage.TheWayToYalahar.Questline, storageValue = 1 },
- { storage = Storage.SearoutesAroundYalahar.TownsCounter, storageValue = 1 },
+ { storage = Storage.TheWayToYalahar.QuestLine, storageValue = 1 },
+ { storage = Storage.SearoutesAroundYalahar.TownsCounter, storageValue = 5 },
{ storage = Storage.SearoutesAroundYalahar.AbDendriel, storageValue = 1 },
{ storage = Storage.SearoutesAroundYalahar.Darashia, storageValue = 1 },
{ storage = Storage.SearoutesAroundYalahar.Venore, storageValue = 1 },
@@ -205,7 +206,7 @@ local questTable = {
{ storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.KingTibianus, storageValue = 1 },
{ storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Leeland, storageValue = 1 },
{ storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Angus, storageValue = 1 },
- { storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Wydrin, storageValue = 1 },
+ { storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Wyrdin, storageValue = 1 },
{ storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Telas, storageValue = 1 },
{ storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Humgolf, storageValue = 1 },
{ storage = Storage.TheShatteredIsles.DefaultStart, storageValue = 3 },
@@ -314,6 +315,7 @@ local questTable = {
{ storage = Storage.FerumbrasAscension.TarbazDoor, storageValue = 1 },
{ storage = Storage.FerumbrasAscension.HabitatsAccess, storageValue = 1 },
{ storage = Storage.FerumbrasAscension.TheLordOfTheLiceAccess, storageValue = 1 },
+ { storage = Storage.FerumbrasAscension.Statue, storageValue = 1 },
{ storage = Storage.Quest.U12_00.TheDreamCourts.AndrewDoor, storageValue = 1 },
@@ -372,11 +374,19 @@ local questTable = {
{ storage = Storage.OutfitQuest.DefaultStart, storageValue = 1 },
{ storage = Storage.HeroRathleton.AccessDoor, storageValue = 1 },
- { storage = Storage.HeroRathleton.FastWay, storageValue = 1 },
+ { storage = Storage.HeroRathleton.AccessTeleport1, storageValue = 1 },
+ { storage = Storage.HeroRathleton.AccessTeleport2, storageValue = 1 },
+ { storage = Storage.HeroRathleton.AccessTeleport3, storageValue = 1 },
-- Sea Serpent Quest
{ storage = Storage.Quest.U8_2.FishForASerpent.QuestLine, storageValue = 5 },
{ storage = Storage.Quest.U8_2.TheHuntForTheSeaSerpent.QuestLine, storageValue = 2 },
+
+ --The White Raven Monastery
+ { storage = Storage.WhiteRavenMonastery.QuestLog, storageValue = 1 },
+ { storage = Storage.WhiteRavenMonastery.Passage, storageValue = 1 },
+ { storage = Storage.WhiteRavenMonastery.Diary, storageValue = 2 },
+ { storage = Storage.WhiteRavenMonastery.Door, storageValue = 1 },
}
-- from Position: (33201, 31762, 1)
diff --git a/data-otservbr-global/scripts/creaturescripts/customs/vip.lua b/data-otservbr-global/scripts/creaturescripts/customs/vip.lua
deleted file mode 100644
index 0ae99c00f2d..00000000000
--- a/data-otservbr-global/scripts/creaturescripts/customs/vip.lua
+++ /dev/null
@@ -1,20 +0,0 @@
-local playerLogin = CreatureEvent("VipLogin")
-
-function playerLogin.onLogin(player)
- if configManager.getBoolean(configKeys.VIP_SYSTEM_ENABLED) then
- local wasVip = player:kv():scoped("account"):get("vip-system") or false
- if wasVip and not player:isVip() then
- player:onRemoveVip()
- end
- if not wasVip and player:isVip() then
- player:onAddVip(player:getVipDays())
- end
-
- if player:isVip() then
- CheckPremiumAndPrint(player, MESSAGE_LOGIN)
- end
- end
- return true
-end
-
-playerLogin:register()
diff --git a/data-otservbr-global/scripts/creaturescripts/monster/faceless_bane_immunity.lua b/data-otservbr-global/scripts/creaturescripts/monster/faceless_bane_immunity.lua
new file mode 100644
index 00000000000..36e1ecd11c3
--- /dev/null
+++ b/data-otservbr-global/scripts/creaturescripts/monster/faceless_bane_immunity.lua
@@ -0,0 +1,47 @@
+local bossName = "Faceless Bane"
+
+local function healBoss(creature)
+ if creature then
+ creature:addHealth(creature:getMaxHealth())
+ creature:getPosition():sendMagicEffect(CONST_ME_BLOCKHIT)
+ end
+end
+
+local function createSummons(creature)
+ if creature then
+ local pos = creature:getPosition()
+ Game.createMonster("Gazer Spectre", pos, true, false, creature)
+ Game.createMonster("Ripper Spectre", pos, true, false, creature)
+ Game.createMonster("Burster Spectre", pos, true, false, creature)
+ end
+end
+
+local function resetBoss(creature, deaths)
+ if creature then
+ healBoss(creature)
+ createSummons(creature)
+ Game.setStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.Deaths, deaths + 1)
+ Game.setStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.StepsOn, 0)
+ Game.setStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.ResetSteps, 1)
+ end
+end
+
+local facelessBaneImmunity = CreatureEvent("facelessBaneImmunity")
+
+function facelessBaneImmunity.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType)
+ if creature and creature:isMonster() and creature:getName() == bossName then
+ local creatureHealthPercent = (creature:getHealth() * 100) / creature:getMaxHealth()
+ local facelessBaneDeathsStorage = Game.getStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.Deaths)
+
+ if creatureHealthPercent <= 20 and facelessBaneDeathsStorage < 1 then
+ resetBoss(creature, facelessBaneDeathsStorage)
+ return true
+ elseif Game.getStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.StepsOn) < 1 then
+ healBoss(creature)
+ return true
+ end
+ end
+ return primaryDamage, primaryType, secondaryDamage, secondaryType
+end
+
+facelessBaneImmunity:register()
diff --git a/data-otservbr-global/scripts/creaturescripts/others/forge_kill.lua b/data-otservbr-global/scripts/creaturescripts/others/forge_kill.lua
deleted file mode 100644
index 3e454d84d14..00000000000
--- a/data-otservbr-global/scripts/creaturescripts/others/forge_kill.lua
+++ /dev/null
@@ -1,12 +0,0 @@
-local forgeKill = CreatureEvent("ForgeSystemMonster")
-
-function forgeKill.onDeath(creature, corpse, killer, mostDamageKiller, unjustified, mostDamageUnjustified)
- local targetMonster = creature:getMonster()
- if not targetMonster then
- return true
- end
-
- return ForgeMonster:onDeath(creature, corpse, killer, mostDamageKiller, unjustified, mostDamageUnjustified)
-end
-
-forgeKill:register()
diff --git a/data-otservbr-global/scripts/globalevents/quests/secret_library_preceptor_lazare.lua b/data-otservbr-global/scripts/globalevents/quests/secret_library_preceptor_lazare.lua
index 4da496fb659..ea05353ad09 100644
--- a/data-otservbr-global/scripts/globalevents/quests/secret_library_preceptor_lazare.lua
+++ b/data-otservbr-global/scripts/globalevents/quests/secret_library_preceptor_lazare.lua
@@ -1,7 +1,7 @@
local config = {
monsterName = "Preceptor Lazare",
bossPosition = Position(33374, 31338, 3),
- range = 5,
+ range = 50,
}
local preceptorLazare = GlobalEvent("PreceptorLazareRespawn")
diff --git a/data-otservbr-global/scripts/movements/quests/the_dream_courts/faceless_bane_step_positions.lua b/data-otservbr-global/scripts/movements/quests/the_dream_courts/faceless_bane_step_positions.lua
new file mode 100644
index 00000000000..8ebdc47ae6f
--- /dev/null
+++ b/data-otservbr-global/scripts/movements/quests/the_dream_courts/faceless_bane_step_positions.lua
@@ -0,0 +1,114 @@
+local walkedPositions = {}
+local lastResetTime = os.time()
+local checkTime = false
+
+local function resetWalkedPositions(checkLastResetTime)
+ if lastResetTime > os.time() and checkLastResetTime then
+ return true
+ end
+
+ walkedPositions = {}
+ Game.setStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.StepsOn, 0)
+ lastResetTime = os.time() + (1 * 60)
+end
+
+local pipePositions = {
+ Position(33612, 32568, 13),
+ Position(33612, 32567, 13),
+ Position(33612, 32566, 13),
+ Position(33612, 32565, 13),
+ Position(33612, 32564, 13),
+ Position(33612, 32563, 13),
+ Position(33612, 32562, 13),
+ Position(33612, 32561, 13),
+ Position(33612, 32560, 13),
+ Position(33612, 32559, 13),
+ Position(33612, 32558, 13),
+ Position(33612, 32557, 13),
+ Position(33612, 32556, 13),
+ Position(33622, 32556, 13),
+ Position(33622, 32557, 13),
+ Position(33622, 32558, 13),
+ Position(33622, 32559, 13),
+ Position(33622, 32560, 13),
+ Position(33622, 32561, 13),
+ Position(33622, 32562, 13),
+ Position(33622, 32563, 13),
+ Position(33622, 32564, 13),
+ Position(33622, 32565, 13),
+ Position(33622, 32566, 13),
+ Position(33622, 32567, 13),
+ Position(33622, 32568, 13),
+}
+
+local function sendEnergyEffect()
+ for _, position in ipairs(pipePositions) do
+ position:sendMagicEffect(CONST_ME_PURPLEENERGY)
+ position:sendSingleSoundEffect(SOUND_EFFECT_TYPE_SPELL_GREAT_ENERGY_BEAM)
+ end
+
+ return true
+end
+
+local facelessBaneStepPositions = MoveEvent()
+
+function facelessBaneStepPositions.onStepIn(creature, item, position, fromPosition)
+ local player = creature:getPlayer()
+ if not player then
+ return true
+ end
+
+ if Game.getStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.ResetSteps) == 1 then
+ Game.setStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.ResetSteps, 0)
+ lastResetTime = os.time()
+ resetWalkedPositions(true)
+ end
+
+ if not checkTime then
+ checkTime = addEvent(resetWalkedPositions, 15 * 1000, false)
+ end
+
+ if Game.getStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.StepsOn) < 1 then
+ if #walkedPositions > 0 then
+ for _, walkedPos in ipairs(walkedPositions) do
+ if walkedPos == position then
+ return true
+ end
+ end
+ end
+
+ position:sendSingleSoundEffect(SOUND_EFFECT_TYPE_SPELL_BUZZ)
+ position:sendMagicEffect(CONST_ME_YELLOWENERGY)
+ table.insert(walkedPositions, position)
+
+ if #walkedPositions == 13 then
+ Game.setStorageValue(GlobalStorage.TheDreamCourts.FacelessBane.StepsOn, 1)
+ addEvent(resetWalkedPositions, 60 * 1000, true)
+ sendEnergyEffect()
+ checkTime = nil
+ end
+ end
+ return true
+end
+
+local facelessBaneSteps = {
+ Position(33615, 32567, 13),
+ Position(33613, 32567, 13),
+ Position(33611, 32563, 13),
+ Position(33610, 32561, 13),
+ Position(33611, 32558, 13),
+ Position(33614, 32557, 13),
+ Position(33617, 32558, 13),
+ Position(33620, 32557, 13),
+ Position(33623, 32558, 13),
+ Position(33624, 32561, 13),
+ Position(33623, 32563, 13),
+ Position(33621, 32567, 13),
+ Position(33619, 32567, 13),
+}
+
+for _, pos in ipairs(facelessBaneSteps) do
+ facelessBaneStepPositions:position(pos)
+end
+
+facelessBaneStepPositions:register()
diff --git a/data/items/items.xml b/data/items/items.xml
index e7e73d9753b..9fdc89bbd8f 100644
--- a/data/items/items.xml
+++ b/data/items/items.xml
@@ -75820,5 +75820,295 @@ Granted by TibiaGoals.com"/>
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/libs/functions/bosslever.lua b/data/libs/functions/boss_lever.lua
similarity index 86%
rename from data/libs/functions/bosslever.lua
rename to data/libs/functions/boss_lever.lua
index 3792cdf629d..b95bf7211b0 100644
--- a/data/libs/functions/bosslever.lua
+++ b/data/libs/functions/boss_lever.lua
@@ -174,24 +174,36 @@ function BossLever:onUse(player)
end
if creature:getLevel() < self.requiredLevel then
- creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "All the players need to be level " .. self.requiredLevel .. " or higher.")
+ local message = "All players need to be level " .. self.requiredLevel .. " or higher."
+ creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, message)
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, message)
return false
end
- if self:lastEncounterTime(creature) > os.time() then
- local info = lever:getInfoPositions()
- for _, v in pairs(info) do
- local newPlayer = v.creature
- if newPlayer then
- local timeLeft = self:lastEncounterTime(newPlayer) - os.time()
- newPlayer:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You or a member in your team have to wait " .. getTimeInWords(timeLeft) .. " to face " .. self.name .. " again!")
- if self:lastEncounterTime(newPlayer) > os.time() then
- newPlayer:getPosition():sendMagicEffect(CONST_ME_POFF)
+ if creature:getGroup():getId() < GROUP_TYPE_GOD and self:lastEncounterTime(creature) > os.time() then
+ local infoPositions = lever:getInfoPositions()
+ for _, posInfo in pairs(infoPositions) do
+ local currentPlayer = posInfo.creature
+ if currentPlayer then
+ local lastEncounter = self:lastEncounterTime(currentPlayer)
+ local currentTime = os.time()
+ if lastEncounter and currentTime < lastEncounter then
+ local timeLeft = lastEncounter - currentTime
+ local timeMessage = getTimeInWords(timeLeft) .. " to face " .. self.name .. " again!"
+ local message = "You have to wait " .. timeMessage
+
+ if currentPlayer ~= player then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "A member in your team has to wait " .. timeMessage)
+ end
+
+ currentPlayer:sendTextMessage(MESSAGE_EVENT_ADVANCE, message)
+ currentPlayer:getPosition():sendMagicEffect(CONST_ME_POFF)
end
end
end
return false
end
+
self.onUseExtra(creature)
return true
end)
diff --git a/data/libs/functions/load.lua b/data/libs/functions/load.lua
index c63276d9444..0fab2c3cb5e 100644
--- a/data/libs/functions/load.lua
+++ b/data/libs/functions/load.lua
@@ -1,14 +1,15 @@
-- Load core functions
dofile(CORE_DIRECTORY .. "/libs/functions/bit.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/bitwise_flags.lua")
+dofile(CORE_DIRECTORY .. "/libs/functions/boss_lever.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/combat.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/constants.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/container.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/creature.lua")
-dofile(CORE_DIRECTORY .. "/libs/functions/functions.lua")
-dofile(CORE_DIRECTORY .. "/libs/functions/gematelier.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/fs.lua")
+dofile(CORE_DIRECTORY .. "/libs/functions/functions.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/game.lua")
+dofile(CORE_DIRECTORY .. "/libs/functions/gematelier.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/item.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/itemtype.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/lever.lua")
@@ -20,14 +21,13 @@ dofile(CORE_DIRECTORY .. "/libs/functions/player.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/position.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/pronouns.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/quests.lua")
+dofile(CORE_DIRECTORY .. "/libs/functions/queue.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/revscriptsys.lua")
+dofile(CORE_DIRECTORY .. "/libs/functions/set.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/spawn.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/spectators.lua")
-dofile(CORE_DIRECTORY .. "/libs/functions/bosslever.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/string.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/tables.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/teleport.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/tile.lua")
dofile(CORE_DIRECTORY .. "/libs/functions/vocation.lua")
-dofile(CORE_DIRECTORY .. "/libs/functions/set.lua")
-dofile(CORE_DIRECTORY .. "/libs/functions/queue.lua")
diff --git a/data/libs/systems/hireling.lua b/data/libs/systems/hireling.lua
index 8b5784759fa..30134cd7cd0 100644
--- a/data/libs/systems/hireling.lua
+++ b/data/libs/systems/hireling.lua
@@ -361,7 +361,7 @@ function Hireling:returnToLamp(player_id)
local inbox = owner:getStoreInbox()
local inboxItems = inbox:getItems()
- if not inbox or #inboxItems > inbox:getMaxCapacity() then
+ if not inbox or #inboxItems >= inbox:getMaxCapacity() then
owner:getPosition():sendMagicEffect(CONST_ME_POFF)
return owner:sendTextMessage(MESSAGE_FAILURE, "You don't have enough room in your inbox.")
end
@@ -556,7 +556,7 @@ function Player:addNewHireling(name, sex)
local inbox = self:getStoreInbox()
local inboxItems = inbox:getItems()
- if not inbox or #inboxItems > inbox:getMaxCapacity() then
+ if not inbox or #inboxItems >= inbox:getMaxCapacity() then
self:getPosition():sendMagicEffect(CONST_ME_POFF)
self:sendTextMessage(MESSAGE_FAILURE, "You don't have enough room in your inbox.")
return false
diff --git a/data/libs/systems/load.lua b/data/libs/systems/load.lua
index 5c7073ad64e..281b8b1d466 100644
--- a/data/libs/systems/load.lua
+++ b/data/libs/systems/load.lua
@@ -9,4 +9,5 @@ dofile(CORE_DIRECTORY .. "/libs/systems/hazard.lua")
dofile(CORE_DIRECTORY .. "/libs/systems/hireling.lua")
dofile(CORE_DIRECTORY .. "/libs/systems/raids.lua")
dofile(CORE_DIRECTORY .. "/libs/systems/reward_boss.lua")
+dofile(CORE_DIRECTORY .. "/libs/systems/vip.lua")
dofile(CORE_DIRECTORY .. "/libs/systems/zones.lua")
diff --git a/data-otservbr-global/lib/others/vip_system.lua b/data/libs/systems/vip.lua
similarity index 61%
rename from data-otservbr-global/lib/others/vip_system.lua
rename to data/libs/systems/vip.lua
index 5a393157c8c..9e76fd8ad80 100644
--- a/data-otservbr-global/lib/others/vip_system.lua
+++ b/data/libs/systems/vip.lua
@@ -1,16 +1,10 @@
local config = {
- activationMessage = "You have received %s VIP days.",
- activationMessageType = MESSAGE_EVENT_ADVANCE,
-
- expirationMessage = "Your VIP days ran out.",
- expirationMessageType = MESSAGE_ADMINISTRATOR,
-
outfits = {},
mounts = {},
}
function Player.onRemoveVip(self)
- self:sendTextMessage(config.expirationMessageType, config.expirationMessage)
+ self:sendTextMessage(MESSAGE_ADMINISTRATOR, "Your VIP status has expired. All VIP benefits have been removed.")
for _, outfit in ipairs(config.outfits) do
self:removeOutfit(outfit)
@@ -21,13 +15,14 @@ function Player.onRemoveVip(self)
end
local playerOutfit = self:getOutfit()
- if table.contains(config.outfits, self:getOutfit().lookType) then
+ if table.contains(config.outfits, playerOutfit.lookType) then
if self:getSex() == PLAYERSEX_FEMALE then
playerOutfit.lookType = 136
else
playerOutfit.lookType = 128
end
playerOutfit.lookAddons = 0
+
self:setOutfit(playerOutfit)
end
@@ -36,7 +31,7 @@ end
function Player.onAddVip(self, days, silent)
if not silent then
- self:sendTextMessage(config.activationMessageType, string.format(config.activationMessage, days))
+ self:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have been granted %s days of VIP status.", days))
end
for _, outfit in ipairs(config.outfits) do
@@ -52,16 +47,15 @@ end
function CheckPremiumAndPrint(player, msgType)
if player:getVipDays() == 0xFFFF then
- player:sendTextMessage(msgType, "You have infinite amount of VIP days left.")
+ player:sendTextMessage(msgType, "You have an unlimited VIP status.")
return true
end
local playerVipTime = player:getVipTime()
if playerVipTime < os.time() then
- local msg = "You do not have VIP on your account."
- player:sendTextMessage(msgType, msg)
+ player:sendTextMessage(msgType, "Your VIP status is currently inactive.")
return true
end
- player:sendTextMessage(msgType, string.format("You have %s of VIP time left.", getFormattedTimeRemaining(playerVipTime)))
+ player:sendTextMessage(msgType, string.format("You have %s of VIP time remaining.", getFormattedTimeRemaining(playerVipTime)))
end
diff --git a/data/modules/scripts/daily_reward/daily_reward.lua b/data/modules/scripts/daily_reward/daily_reward.lua
index 09b927cedda..b6a7c16993a 100644
--- a/data/modules/scripts/daily_reward/daily_reward.lua
+++ b/data/modules/scripts/daily_reward/daily_reward.lua
@@ -454,7 +454,7 @@ function Player.selectDailyReward(self, msg)
-- Adding items to store inbox
local inbox = self:getStoreInbox()
local inboxItems = inbox:getItems()
- if not inbox or #inboxItems > inbox:getMaxCapacity() then
+ if not inbox or #inboxItems >= inbox:getMaxCapacity() then
self:sendError("You do not have enough space in your store inbox.")
return false
end
diff --git a/data/modules/scripts/gamestore/init.lua b/data/modules/scripts/gamestore/init.lua
index ba7398d9d3e..214aec77d43 100644
--- a/data/modules/scripts/gamestore/init.lua
+++ b/data/modules/scripts/gamestore/init.lua
@@ -247,6 +247,11 @@ function onRecvbyte(player, msg, byte)
return player:sendCancelMessage("Store don't have offers for rookgaard citizen.")
end
+ if player:isUIExhausted(250) then
+ player:sendCancelMessage("You are exhausted.")
+ return
+ end
+
if byte == GameStore.RecivedPackets.C_StoreEvent then
elseif byte == GameStore.RecivedPackets.C_TransferCoins then
parseTransferableCoins(player:getId(), msg)
@@ -262,12 +267,6 @@ function onRecvbyte(player, msg, byte)
parseRequestTransactionHistory(player:getId(), msg)
end
- if player:isUIExhausted(250) then
- player:sendCancelMessage("You are exhausted.")
- return false
- end
-
- player:updateUIExhausted()
return true
end
@@ -306,6 +305,7 @@ function parseTransferableCoins(playerId, msg)
GameStore.insertHistory(accountId, GameStore.HistoryTypes.HISTORY_TYPE_NONE, player:getName() .. " transferred you this amount.", amount, GameStore.CoinType.Transferable)
GameStore.insertHistory(player:getAccountId(), GameStore.HistoryTypes.HISTORY_TYPE_NONE, "You transferred this amount to " .. reciver, -1 * amount, GameStore.CoinType.Transferable)
openStore(playerId)
+ player:updateUIExhausted()
end
function parseOpenStore(playerId, msg)
@@ -396,6 +396,7 @@ function parseRequestStoreOffers(playerId, msg)
addPlayerEvent(sendShowStoreOffers, 250, playerId, searchResultsCategory)
end
+ player:updateUIExhausted()
end
function parseBuyStoreOffer(playerId, msg)
@@ -532,6 +533,8 @@ function parseBuyStoreOffer(playerId, msg)
sendUpdatedStoreBalances(playerId)
return addPlayerEvent(sendStorePurchaseSuccessful, 650, playerId, message)
end
+
+ player:updateUIExhausted()
return true
end
@@ -540,11 +543,13 @@ function parseOpenTransactionHistory(playerId, msg)
local page = 1
GameStore.DefaultValues.DEFAULT_VALUE_ENTRIES_PER_PAGE = msg:getByte()
sendStoreTransactionHistory(playerId, page, GameStore.DefaultValues.DEFAULT_VALUE_ENTRIES_PER_PAGE)
+ player:updateUIExhausted()
end
function parseRequestTransactionHistory(playerId, msg)
local page = msg:getU32()
sendStoreTransactionHistory(playerId, page + 1, GameStore.DefaultValues.DEFAULT_VALUE_ENTRIES_PER_PAGE)
+ player:updateUIExhausted()
end
local function getCategoriesRook()
diff --git a/data/npclib/npc_system/npc_handler.lua b/data/npclib/npc_system/npc_handler.lua
index 8f4d5f2ca48..31aaa88faaf 100644
--- a/data/npclib/npc_system/npc_handler.lua
+++ b/data/npclib/npc_system/npc_handler.lua
@@ -480,7 +480,6 @@ if NpcHandler == nil then
-- If is npc shop, send shop window and parse default message (if not have callback on the npc)
if npc:isMerchant() then
- npc:closeShopWindow(player)
npc:openShopWindow(player)
self:say(msg, npc, player)
end
diff --git a/data-otservbr-global/scripts/actions/other/hirelinglamp.lua b/data/scripts/actions/items/hireling_lamp.lua
similarity index 93%
rename from data-otservbr-global/scripts/actions/other/hirelinglamp.lua
rename to data/scripts/actions/items/hireling_lamp.lua
index 8117b5366bc..0ffed1fa744 100644
--- a/data-otservbr-global/scripts/actions/other/hirelinglamp.lua
+++ b/data/scripts/actions/items/hireling_lamp.lua
@@ -2,8 +2,9 @@ local hirelingLamp = Action()
function hirelingLamp.onUse(player, item, fromPosition, target, toPosition, isHotkey)
local spawnPosition = player:getPosition()
- local hireling_id = item:getCustomAttribute("Hireling")
+ local hirelingId = item:getCustomAttribute("Hireling")
local house = spawnPosition and spawnPosition:getTile() and spawnPosition:getTile():getHouse() or nil
+
if not house then
player:getPosition():sendMagicEffect(CONST_ME_POFF)
player:sendTextMessage(MESSAGE_FAILURE, "You may use this only inside a house.")
@@ -22,10 +23,10 @@ function hirelingLamp.onUse(player, item, fromPosition, target, toPosition, isHo
return false
end
- local hireling = getHirelingById(hireling_id)
+ local hireling = getHirelingById(hirelingId)
if not hireling then
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There was an error creating the hireling and it has been deleted, please, contact server admin.")
- logger.warn("[hirelingLamp.onUse] Player {} is using hireling with id {} not exist in the database", player:getName(), hireling_id)
+ logger.warn("[hirelingLamp.onUse] Player {} is using hireling with id {} not exist in the database", player:getName(), hirelingId)
logger.error("Deleted the lamp")
item:remove(1)
return true
diff --git a/data/scripts/actions/objects/large_seashell.lua b/data/scripts/actions/objects/large_seashell.lua
new file mode 100644
index 00000000000..c6d767146ec
--- /dev/null
+++ b/data/scripts/actions/objects/large_seashell.lua
@@ -0,0 +1,30 @@
+local largeSeashell = Action()
+
+function largeSeashell.onUse(player, item, fromPosition, target, toPosition, isHotkey)
+ if player:hasExhaustion("delay-large-seashell") then
+ player:say("You have already opened a shell today.", TALKTYPE_MONSTER_SAY, false, player, item:getPosition())
+ return true
+ end
+
+ local chance = math.random(100)
+ local message = "Nothing is inside."
+
+ if chance <= 16 then
+ doTargetCombatHealth(0, player, COMBAT_PHYSICALDAMAGE, -200, -200, CONST_ME_NONE)
+ message = "Ouch! You squeezed your fingers."
+ elseif chance > 16 and chance <= 64 then
+ Game.createItem(math.random(281, 282), 1, player:getPosition())
+ message = "You found a beautiful pearl."
+ player:addAchievementProgress("Shell Seeker", 100)
+ end
+
+ player:setExhaustion("delay-large-seashell", 20 * 60 * 60)
+ player:say(message, TALKTYPE_MONSTER_SAY, false, player, item:getPosition())
+ item:transform(198)
+ item:decay()
+ item:getPosition():sendMagicEffect(CONST_ME_BUBBLES)
+ return true
+end
+
+largeSeashell:id(197)
+largeSeashell:register()
diff --git a/data-otservbr-global/scripts/globalevents/others/bosslever_death.lua b/data/scripts/creaturescripts/monster/boss_lever_death.lua
similarity index 99%
rename from data-otservbr-global/scripts/globalevents/others/bosslever_death.lua
rename to data/scripts/creaturescripts/monster/boss_lever_death.lua
index 9538fe15eb7..a2c58d1e43b 100644
--- a/data-otservbr-global/scripts/globalevents/others/bosslever_death.lua
+++ b/data/scripts/creaturescripts/monster/boss_lever_death.lua
@@ -1,22 +1,28 @@
local onBossDeath = CreatureEvent("BossLeverOnDeath")
+
function onBossDeath.onDeath(creature)
if not creature then
return true
end
+
local name = creature:getName()
local key = "boss." .. toKey(name)
local zone = Zone(key)
+
if not zone then
return true
end
+
local bossLever = BossLever[name]
if not bossLever then
return true
end
+
if bossLever.timeoutEvent then
stopEvent(bossLever.timeoutEvent)
bossLever.timeoutEvent = nil
end
+
if bossLever.timeAfterKill > 0 then
zone:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The " .. name .. " has been defeated. You have " .. bossLever.timeAfterKill .. " seconds to leave the room.")
bossLever.timeoutEvent = addEvent(function(zn)
@@ -26,4 +32,5 @@ function onBossDeath.onDeath(creature)
end
return true
end
+
onBossDeath:register()
diff --git a/data-canary/scripts/creaturescripts/forge_kill.lua b/data/scripts/creaturescripts/monster/forge_kill.lua
similarity index 100%
rename from data-canary/scripts/creaturescripts/forge_kill.lua
rename to data/scripts/creaturescripts/monster/forge_kill.lua
diff --git a/data-otservbr-global/scripts/creaturescripts/others/adventure_blessing_login.lua b/data/scripts/creaturescripts/player/adventure_blessing_login.lua
similarity index 99%
rename from data-otservbr-global/scripts/creaturescripts/others/adventure_blessing_login.lua
rename to data/scripts/creaturescripts/player/adventure_blessing_login.lua
index df04052716f..8ebf88a72e9 100644
--- a/data-otservbr-global/scripts/creaturescripts/others/adventure_blessing_login.lua
+++ b/data/scripts/creaturescripts/player/adventure_blessing_login.lua
@@ -1,6 +1,7 @@
dofile(CORE_DIRECTORY .. "/modules/scripts/blessings/blessings.lua")
local adventurerBlessingLogin = CreatureEvent("AdventurerBlessingLogin")
+
function adventurerBlessingLogin.onLogin(player)
return Blessings.doAdventurerBlessing(player)
end
diff --git a/data/scripts/creaturescripts/player/login.lua b/data/scripts/creaturescripts/player/login.lua
index 34d139a047d..fbe4f9e63b6 100644
--- a/data/scripts/creaturescripts/player/login.lua
+++ b/data/scripts/creaturescripts/player/login.lua
@@ -49,10 +49,8 @@ function playerLoginGlobal.onLogin(player)
end
-- Boosted
- player:sendTextMessage(MESSAGE_BOOSTED_CREATURE, "Today's boosted creature: " .. Game.getBoostedCreature() .. " \
- Boosted creatures yield more experience points, carry more loot than usual and respawn at a faster rate.")
- player:sendTextMessage(MESSAGE_BOOSTED_CREATURE, "Today's boosted boss: " .. Game.getBoostedBoss() .. " \
- Boosted bosses contain more loot and count more kills for your Bosstiary.")
+ player:sendTextMessage(MESSAGE_BOOSTED_CREATURE, string.format("Today's boosted creature: %s.\nBoosted creatures yield more experience points, carry more loot than usual, and respawn at a faster rate.", Game.getBoostedCreature()))
+ player:sendTextMessage(MESSAGE_BOOSTED_CREATURE, string.format("Today's boosted boss: %s.\nBoosted bosses contain more loot and count more kills for your Bosstiary.", Game.getBoostedBoss()))
-- Rewards
local rewards = #player:getRewardList()
@@ -118,6 +116,24 @@ function playerLoginGlobal.onLogin(player)
player:setStaminaXpBoost(player:getFinalBonusStamina() * 100)
player:getFinalLowLevelBonus()
+ -- Updates the player's VIP status and executes corresponding actions if applicable.
+ if configManager.getBoolean(configKeys.VIP_SYSTEM_ENABLED) then
+ local isVipNow = player:isVip()
+ local wasVip = player:kv():scoped("account"):get("vip-system") or false
+
+ if wasVip ~= isVipNow then
+ if wasVip then
+ player:onRemoveVip()
+ else
+ player:onAddVip(player:getVipDays())
+ end
+ end
+
+ if isVipNow then
+ CheckPremiumAndPrint(player, MESSAGE_LOGIN)
+ end
+ end
+
-- Set Ghost Mode
if player:getGroup():getId() >= GROUP_TYPE_GAMEMASTER then
player:setGhostMode(true)
diff --git a/data/scripts/creaturescripts/player/offline_training.lua b/data/scripts/creaturescripts/player/offline_training.lua
index 4466c6ee0e3..7d08f07efe8 100644
--- a/data/scripts/creaturescripts/player/offline_training.lua
+++ b/data/scripts/creaturescripts/player/offline_training.lua
@@ -55,18 +55,23 @@ function offlineTraining.onLogin(player)
local vocation = player:getVocation()
local promotion = vocation:getPromotion()
local topVocation = not promotion and vocation or promotion
- local updateSkills = false
+ local tries = nil
if table.contains({ SKILL_CLUB, SKILL_SWORD, SKILL_AXE, SKILL_DISTANCE }, offlineTrainingSkill) then
- local modifier = topVocation:getBaseAttackSpeed() / 1000 / configManager.getFloat(configKeys.RATE_OFFLINE_TRAINING_SPEED)
- updateSkills = player:addOfflineTrainingTries(offlineTrainingSkill, (trainingTime / modifier) / (offlineTrainingSkill == SKILL_DISTANCE and 4 or 2))
+ local modifier = topVocation:getBaseAttackSpeed() / 1000
+ tries = (trainingTime / modifier) / (offlineTrainingSkill == SKILL_DISTANCE and 4 or 2)
elseif offlineTrainingSkill == SKILL_MAGLEVEL then
- local gainTicks = topVocation:getManaGainTicks() * 2
+ local gainTicks = topVocation:getManaGainTicks() / 1000
if gainTicks == 0 then
gainTicks = 1
end
- updateSkills = player:addOfflineTrainingTries(SKILL_MAGLEVEL, trainingTime * (vocation:getManaGainAmount() / gainTicks))
+ tries = trainingTime * (vocation:getManaGainAmount() / gainTicks)
+ end
+
+ local updateSkills = false
+ if tries then
+ updateSkills = player:addOfflineTrainingTries(offlineTrainingSkill, tries * configManager.getFloat(configKeys.RATE_OFFLINE_TRAINING_SPEED))
end
if updateSkills then
diff --git a/data/scripts/eventcallbacks/README.md b/data/scripts/eventcallbacks/README.md
index 601653574bd..ae5de046bd2 100644
--- a/data/scripts/eventcallbacks/README.md
+++ b/data/scripts/eventcallbacks/README.md
@@ -14,8 +14,8 @@ Event callbacks are available for several categories of game entities, such as `
### These are the functions available to use
- `(bool)` `creatureOnChangeOutfit`
-- `(bool)` `creatureOnAreaCombat`
-- `(bool)` `creatureOnTargetCombat`
+- `(ReturnValue)` `creatureOnAreaCombat`
+- `(ReturnValue)` `creatureOnTargetCombat`
- `(void)` `creatureOnHear`
- `(void)` `creatureOnDrainHealth`
- `(bool)` `partyOnJoin`
@@ -66,7 +66,7 @@ local callback = EventCallback()
function callback.creatureOnAreaCombat(creature, tile, isAggressive)
-- custom behavior when a creature enters combat area
- return true
+ return RETURNVALUE_NOERROR
end
callback:register()
@@ -131,14 +131,36 @@ Here is an example of a boolean event callback:
```lua
local callback = EventCallback()
+function callback.playerOnMoveItem(player, item, count, fromPos, toPos, fromCylinder, toCylinder)
+ if item:getId() == ITEM_PARCEL then
+ --Custom behavior when the player moves a parcel.
+ return false
+ end
+ return true
+end
+
+callback:register()
+```
+
+### In this example, when a player moves an item, the function checks if the item is a parcel and apply a custom behaviour, returning false making it impossible to move, stopping the associated function on the C++ side.
+
+## ReturnValue Event Callbacks
+
+Some event callbacks are expected to return a enum value, in this case, the enum ReturnValue. If the return is different of RETURNVALUE_NOERROR, it will stop the execution of the next callbacks.
+
+Here is an example of a ReturnValue event callback:
+
+```lua
+local callback = EventCallback()
+
function callback.creatureOnAreaCombat(creature, tile, isAggressive)
-- if the creature is not aggressive, stop the execution of the C++ function
if not isAggressive then
- return false
+ return RETURNVALUE_NOTPOSSIBLE
end
-- custom behavior when an aggressive creature enters a combat area
- return true
+ return RETURNVALUE_NOERROR
end
callback:register()
@@ -146,6 +168,7 @@ callback:register()
### In this example, when a non-aggressive creature enters a combat area, the creatureOnAreaCombat function returns false, stopping the associated function on the C++ side.
+
## Multiple Callbacks for the Same Event
You can define multiple callbacks for the same event type. This allows you to encapsulate different behaviors in separate callbacks, making your code more modular and easier to manage.
diff --git a/data/scripts/eventcallbacks/creature/on_area_combat.lua b/data/scripts/eventcallbacks/creature/on_area_combat.lua
index a6295074df3..f68cc95ccad 100644
--- a/data/scripts/eventcallbacks/creature/on_area_combat.lua
+++ b/data/scripts/eventcallbacks/creature/on_area_combat.lua
@@ -1,7 +1,7 @@
local callback = EventCallback()
function callback.creatureOnAreaCombat(creature, tile, isAggressive)
- return true
+ return RETURNVALUE_NOERROR
end
callback:register()
diff --git a/data/scripts/talkactions/god/add_skill.lua b/data/scripts/talkactions/god/add_skill.lua
index 20644582325..09d240ece4d 100644
--- a/data/scripts/talkactions/god/add_skill.lua
+++ b/data/scripts/talkactions/god/add_skill.lua
@@ -16,11 +16,6 @@ local function getSkillId(skillName)
end
end
-local function getExpForLevel(level)
- level = level - 1
- return ((50 * level * level * level) - (150 * level * level) + (400 * level)) / 3
-end
-
local addSkill = TalkAction("/addskill")
function addSkill.onSay(player, words, param)
@@ -54,7 +49,7 @@ function addSkill.onSay(player, words, param)
local ch = split[2]:sub(1, 1)
if ch == "l" or ch == "e" then
targetLevel = target:getLevel() + count
- targetExp = getExpForLevel(targetLevel)
+ targetExp = Game.getExperienceForLevel(targetLevel)
addExp = targetExp - target:getExperience()
target:addExperience(addExp, false)
elseif ch == "m" then
diff --git a/data/scripts/talkactions/god/create_monster.lua b/data/scripts/talkactions/god/manage_monster.lua
similarity index 83%
rename from data/scripts/talkactions/god/create_monster.lua
rename to data/scripts/talkactions/god/manage_monster.lua
index ce5869a2eee..da110d59265 100644
--- a/data/scripts/talkactions/god/create_monster.lua
+++ b/data/scripts/talkactions/god/manage_monster.lua
@@ -56,7 +56,7 @@ local createMonster = TalkAction("/m")
-- @param param: String containing the command parameters.
-- Format: "/m monstername, monstercount, [fiendish/influenced level], spawnRadius, [forceCreate]"
-- Example: "/m rat, 10, fiendish, 5, true"
--- @param: the last param is by default "false", if add "," or any value i'ts set to true
+-- @param: the last param is by default "false", if add "," or any value it's set to true
-- @return true if the command is executed successfully, false otherwise.
function createMonster.onSay(player, words, param)
-- create log
@@ -127,3 +127,33 @@ end
createMonster:separator(" ")
createMonster:groupType("god")
createMonster:register()
+
+----------------- Rename monster name -----------------
+local setMonsterName = TalkAction("/setmonstername")
+
+-- @function setMonsterName.onSay
+-- @desc TalkAction to rename nearby monsters within a radius of 4 sqm.
+-- Format: "/setmonstername newName"
+function setMonsterName.onSay(player, words, param)
+ if param == "" then
+ player:sendCancelMessage("Command param required.")
+ return true
+ end
+
+ local split = param:split(",")
+ local monsterNewName = split[1]
+
+ local spectators, spectator = Game.getSpectators(player:getPosition(), false, false, 4, 4, 4, 4)
+ for i = 1, #spectators do
+ spectator = spectators[i]
+ if spectator:isMonster() then
+ spectator:setName(monsterNewName)
+ end
+ end
+
+ return true
+end
+
+setMonsterName:separator(" ")
+setMonsterName:groupType("god")
+setMonsterName:register()
diff --git a/data/scripts/talkactions/god/reload.lua b/data/scripts/talkactions/god/reload.lua
index 20b9431dbba..d0c0cdd45a8 100644
--- a/data/scripts/talkactions/god/reload.lua
+++ b/data/scripts/talkactions/god/reload.lua
@@ -7,6 +7,7 @@ local reloadTypes = {
["configuration"] = RELOAD_TYPE_CONFIG,
["core"] = RELOAD_TYPE_CORE,
["events"] = RELOAD_TYPE_EVENTS,
+ ["familiar"] = RELOAD_TYPE_FAMILIARS,
["global"] = RELOAD_TYPE_CORE,
["group"] = RELOAD_TYPE_GROUPS,
["groups"] = RELOAD_TYPE_GROUPS,
@@ -20,6 +21,8 @@ local reloadTypes = {
["monsters"] = RELOAD_TYPE_MONSTERS,
["mount"] = RELOAD_TYPE_MOUNTS,
["mounts"] = RELOAD_TYPE_MOUNTS,
+ ["outfit"] = RELOAD_TYPE_OUTFITS,
+ ["outfits"] = RELOAD_TYPE_OUTFITS,
["npc"] = RELOAD_TYPE_NPCS,
["npcs"] = RELOAD_TYPE_NPCS,
["raid"] = RELOAD_TYPE_RAIDS,
diff --git a/data/scripts/talkactions/player/reward.lua b/data/scripts/talkactions/player/reward.lua
index 3f4cc3787de..a05dab3a933 100644
--- a/data/scripts/talkactions/player/reward.lua
+++ b/data/scripts/talkactions/player/reward.lua
@@ -26,7 +26,7 @@ local function sendExerciseRewardModal(player)
local inbox = player:getStoreInbox()
local inboxItems = inbox:getItems()
- if inbox and #inboxItems <= inbox:getMaxCapacity() and player:getFreeCapacity() >= iType:getWeight() then
+ if inbox and #inboxItems < inbox:getMaxCapacity() and player:getFreeCapacity() >= iType:getWeight() then
local item = inbox:addItem(it.id, it.charges)
if item then
item:setActionId(IMMOVABLE_ACTION_ID)
diff --git a/data-otservbr-global/scripts/weapons/dawnport_weapon.lua b/data/scripts/weapons/dawnport_weapons.lua
similarity index 100%
rename from data-otservbr-global/scripts/weapons/dawnport_weapon.lua
rename to data/scripts/weapons/dawnport_weapons.lua
diff --git a/data-otservbr-global/scripts/weapons/scripts/burst_arrow.lua b/data/scripts/weapons/scripts/burst_arrow.lua
similarity index 100%
rename from data-otservbr-global/scripts/weapons/scripts/burst_arrow.lua
rename to data/scripts/weapons/scripts/burst_arrow.lua
diff --git a/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua b/data/scripts/weapons/scripts/diamond_arrow.lua
similarity index 100%
rename from data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua
rename to data/scripts/weapons/scripts/diamond_arrow.lua
diff --git a/data-otservbr-global/scripts/weapons/scripts/poison_arrow.lua b/data/scripts/weapons/scripts/poison_arrow.lua
similarity index 100%
rename from data-otservbr-global/scripts/weapons/scripts/poison_arrow.lua
rename to data/scripts/weapons/scripts/poison_arrow.lua
diff --git a/data-otservbr-global/scripts/weapons/scripts/viper_star.lua b/data/scripts/weapons/scripts/viper_star.lua
similarity index 100%
rename from data-otservbr-global/scripts/weapons/scripts/viper_star.lua
rename to data/scripts/weapons/scripts/viper_star.lua
diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev
index 161a80a2e21..4cd63334d5e 100644
--- a/docker/Dockerfile.dev
+++ b/docker/Dockerfile.dev
@@ -1,10 +1,10 @@
# Stage 1: Download all dependencies
-FROM ubuntu:23.04 AS dependencies
+FROM ubuntu:24.04 AS dependencies
RUN --mount=type=cache,target=/var/cache/apt \
apt-get update && apt-get install -y --no-install-recommends cmake git \
unzip build-essential ca-certificates curl zip unzip tar \
- pkg-config ninja-build autoconf automake libtool \
+ pkg-config ninja-build autoconf automake libtool glibc-tools \
python3 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
@@ -32,7 +32,7 @@ COPY recompile.sh CMakeLists.txt CMakePresets.json vcpkg.json ./
RUN ./recompile.sh "/opt"
# Stage 3: execute
-FROM ubuntu:23.04 AS prod
+FROM ubuntu:24.04 AS prod
COPY --from=build /srv/build/build/linux-release/bin/canary /bin/canary
WORKDIR /srv/canary
ENTRYPOINT ["/srv/canary/start.sh", "canary"]
diff --git a/docker/data/start.sh b/docker/data/start.sh
index db688b488d0..c7d10fa0fdb 100755
--- a/docker/data/start.sh
+++ b/docker/data/start.sh
@@ -10,8 +10,8 @@ OT_SERVER_LOGIN_PORT="${OT_SERVER_LOGIN_PORT:-7171}"
OT_SERVER_GAME_PORT="${OT_SERVER_GAME_PORT:-7172}"
OT_SERVER_STATUS_PORT="${OT_SERVER_STATUS_PORT:-7171}"
OT_SERVER_TEST_ACCOUNTS="${OT_SERVER_TEST_ACCOUNTS:-false}"
-OT_SERVER_DATA="${OT_SERVER_DATA:-data-canary}"
-OT_SERVER_MAP="${OT_SERVER_MAP:-https://github.com/opentibiabr/otservbr-global/releases/download/v1.5.0/otservbr.otbm}"
+OT_SERVER_DATA="${OT_SERVER_DATA:-data-otservbr-global}"
+OT_SERVER_MAP="${OT_SERVER_MAP:-https://github.com/opentibiabr/canary/releases/download/v3.1.0/otservbr.otbm}"
echo ""
echo "===== Print Variables ====="
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 46a2fb619b2..88fd938e500 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -1,5 +1,3 @@
----
-version: '3.8'
name: otbr
services:
database:
diff --git a/schema.sql b/schema.sql
index 7bbf6c86ac5..af245067057 100644
--- a/schema.sql
+++ b/schema.sql
@@ -217,7 +217,7 @@ CREATE TABLE IF NOT EXISTS `account_viplist` (
-- Table structure `account_vipgroup`
CREATE TABLE IF NOT EXISTS `account_vipgroups` (
- `id` tinyint(3) UNSIGNED NOT NULL,
+ `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`account_id` int(11) UNSIGNED NOT NULL COMMENT 'id of account whose vip group entry it is',
`name` varchar(128) NOT NULL,
`customizable` BOOLEAN NOT NULL DEFAULT '1',
@@ -229,9 +229,9 @@ CREATE TABLE IF NOT EXISTS `account_vipgroups` (
--
DELIMITER //
CREATE TRIGGER `oncreate_accounts` AFTER INSERT ON `accounts` FOR EACH ROW BEGIN
- INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES (1, NEW.`id`, 'Enemies', 0);
- INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES (2, NEW.`id`, 'Friends', 0);
- INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`) VALUES (3, NEW.`id`, 'Trading Partner', 0);
+ INSERT INTO `account_vipgroups` (`account_id`, `name`, `customizable`) VALUES (NEW.`id`, 'Enemies', 0);
+ INSERT INTO `account_vipgroups` (`account_id`, `name`, `customizable`) VALUES (NEW.`id`, 'Friends', 0);
+ INSERT INTO `account_vipgroups` (`account_id`, `name`, `customizable`) VALUES (NEW.`id`, 'Trading Partner', 0);
END
//
DELIMITER ;
@@ -240,7 +240,7 @@ DELIMITER ;
CREATE TABLE IF NOT EXISTS `account_vipgrouplist` (
`account_id` int(11) UNSIGNED NOT NULL COMMENT 'id of account whose viplist entry it is',
`player_id` int(11) NOT NULL COMMENT 'id of target player of viplist entry',
- `vipgroup_id` tinyint(3) UNSIGNED NOT NULL COMMENT 'id of vip group that player belongs',
+ `vipgroup_id` int(11) UNSIGNED NOT NULL COMMENT 'id of vip group that player belongs',
INDEX `account_id` (`account_id`),
INDEX `player_id` (`player_id`),
INDEX `vipgroup_id` (`vipgroup_id`),
@@ -258,35 +258,36 @@ CREATE TABLE IF NOT EXISTS `boosted_boss` (
`boostname` TEXT,
`date` varchar(250) NOT NULL DEFAULT '',
`raceid` varchar(250) NOT NULL DEFAULT '',
- `looktypeEx` int(11) NOT NULL DEFAULT "0",
- `looktype` int(11) NOT NULL DEFAULT "136",
- `lookfeet` int(11) NOT NULL DEFAULT "0",
- `looklegs` int(11) NOT NULL DEFAULT "0",
- `lookhead` int(11) NOT NULL DEFAULT "0",
- `lookbody` int(11) NOT NULL DEFAULT "0",
- `lookaddons` int(11) NOT NULL DEFAULT "0",
- `lookmount` int(11) DEFAULT "0",
+ `looktypeEx` int(11) NOT NULL DEFAULT 0,
+ `looktype` int(11) NOT NULL DEFAULT 136,
+ `lookfeet` int(11) NOT NULL DEFAULT 0,
+ `looklegs` int(11) NOT NULL DEFAULT 0,
+ `lookhead` int(11) NOT NULL DEFAULT 0,
+ `lookbody` int(11) NOT NULL DEFAULT 0,
+ `lookaddons` int(11) NOT NULL DEFAULT 0,
+ `lookmount` int(11) DEFAULT 0,
PRIMARY KEY (`date`)
-) AS SELECT 0 AS date, "default" AS boostname, 0 AS raceid;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+INSERT INTO `boosted_boss` (`boostname`, `date`, `raceid`) VALUES ('default', 0, 0);
-- Table structure `boosted_creature`
CREATE TABLE IF NOT EXISTS `boosted_creature` (
`boostname` TEXT,
`date` varchar(250) NOT NULL DEFAULT '',
`raceid` varchar(250) NOT NULL DEFAULT '',
- `looktype` int(11) NOT NULL DEFAULT "136",
- `lookfeet` int(11) NOT NULL DEFAULT "0",
- `looklegs` int(11) NOT NULL DEFAULT "0",
- `lookhead` int(11) NOT NULL DEFAULT "0",
- `lookbody` int(11) NOT NULL DEFAULT "0",
- `lookaddons` int(11) NOT NULL DEFAULT "0",
- `lookmount` int(11) DEFAULT "0",
+ `looktype` int(11) NOT NULL DEFAULT 136,
+ `lookfeet` int(11) NOT NULL DEFAULT 0,
+ `looklegs` int(11) NOT NULL DEFAULT 0,
+ `lookhead` int(11) NOT NULL DEFAULT 0,
+ `lookbody` int(11) NOT NULL DEFAULT 0,
+ `lookaddons` int(11) NOT NULL DEFAULT 0,
+ `lookmount` int(11) DEFAULT 0,
PRIMARY KEY (`date`)
-) AS SELECT 0 AS date, "default" AS boostname, 0 AS raceid;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--- --------------------------------------------------------
+INSERT INTO `boosted_creature` (`boostname`, `date`, `raceid`) VALUES ('default', 0, 0);
---
-- Tabble Structure `daily_reward_history`
CREATE TABLE IF NOT EXISTS `daily_reward_history` (
`id` int(11) NOT NULL AUTO_INCREMENT,
@@ -473,7 +474,6 @@ END
DELIMITER ;
-- Table structure `house_lists`
-
CREATE TABLE IF NOT EXISTS `house_lists` (
`house_id` int NOT NULL,
`listid` int NOT NULL,
@@ -485,7 +485,6 @@ CREATE TABLE IF NOT EXISTS `house_lists` (
CONSTRAINT `houses_list_house_fk` FOREIGN KEY (`house_id`) REFERENCES `houses` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
-
-- Table structure `ip_bans`
CREATE TABLE IF NOT EXISTS `ip_bans` (
`ip` int(11) NOT NULL,
@@ -540,7 +539,6 @@ CREATE TABLE IF NOT EXISTS `market_offers` (
ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-- Table structure `players_online`
CREATE TABLE IF NOT EXISTS `players_online` (
`player_id` int(11) NOT NULL,
@@ -671,7 +669,6 @@ CREATE TABLE IF NOT EXISTS `player_wheeldata` (
PRIMARY KEY (`player_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-- Table structure `player_kills`
CREATE TABLE IF NOT EXISTS `player_kills` (
`player_id` int(11) NOT NULL,
@@ -830,6 +827,7 @@ CREATE TABLE IF NOT EXISTS `account_sessions` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+-- Table structure `kv_store`
CREATE TABLE IF NOT EXISTS `kv_store` (
`key_name` varchar(191) NOT NULL,
`timestamp` bigint NOT NULL,
@@ -852,9 +850,3 @@ INSERT INTO `players`
(4, 'Paladin Sample', 1, 1, 8, 3, 185, 185, 4200, 113, 115, 95, 39, 129, 0, 90, 90, 0, 8, '', 470, 1, 10, 0, 10, 0, 10, 0, 10, 0),
(5, 'Knight Sample', 1, 1, 8, 4, 185, 185, 4200, 113, 115, 95, 39, 129, 0, 90, 90, 0, 8, '', 470, 1, 10, 0, 10, 0, 10, 0, 10, 0),
(6, 'GOD', 6, 1, 2, 0, 155, 155, 100, 113, 115, 95, 39, 75, 0, 60, 60, 0, 8, '', 410, 1, 10, 0, 10, 0, 10, 0, 10, 0);
-
--- Create vip groups for GOD account
-INSERT INTO `account_vipgroups` (`id`, `name`, `account_id`. `customizable`) VALUES
-(1, 'Friends', 1, 0),
-(2, 'Enemies', 1, 0),
-(3, 'Trading Partners', 1, 0);
diff --git a/src/canary_server.cpp b/src/canary_server.cpp
index 17a36ff3d23..e49a86d7d9f 100644
--- a/src/canary_server.cpp
+++ b/src/canary_server.cpp
@@ -93,8 +93,8 @@ int CanaryServer::run() {
#ifndef _WIN32
if (getuid() == 0 || geteuid() == 0) {
logger.warn("{} has been executed as root user, "
- "please consider running it as a normal user",
- ProtocolStatus::SERVER_NAME);
+ "please consider running it as a normal user",
+ ProtocolStatus::SERVER_NAME);
}
#endif
@@ -213,7 +213,7 @@ void CanaryServer::logInfos() {
logger.info("A server developed by: {}", ProtocolStatus::SERVER_DEVELOPERS);
logger.info("Visit our website for updates, support, and resources: "
- "https://docs.opentibiabr.com/");
+ "https://docs.opentibiabr.com/");
}
/**
@@ -234,7 +234,7 @@ void CanaryServer::toggleForceCloseButton() {
void CanaryServer::badAllocationHandler() {
// Use functions that only use stack allocation
g_logger().error("Allocation failed, server out of memory, "
- "decrease the size of your map or compile in 64 bits mode");
+ "decrease the size of your map or compile in 64 bits mode");
if (isatty(STDIN_FILENO)) {
getchar();
@@ -318,7 +318,7 @@ void CanaryServer::initializeDatabase() {
DatabaseManager::updateDatabase();
if (g_configManager().getBoolean(OPTIMIZE_DATABASE, __FUNCTION__)
- && !DatabaseManager::optimizeTables()) {
+ && !DatabaseManager::optimizeTables()) {
logger.debug("No tables were optimized");
}
}
diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp
index 22043f42525..0e53c97546e 100644
--- a/src/config/config_enums.hpp
+++ b/src/config/config_enums.hpp
@@ -50,6 +50,7 @@ enum ConfigKey_t : uint16_t {
DATA_DIRECTORY,
DAY_KILLS_TO_RED,
DEATH_LOSE_PERCENT,
+ DEFAULT_RESPAWN_TIME,
DEFAULT_DESPAWNRADIUS,
DEFAULT_DESPAWNRANGE,
DEFAULT_PRIORITY,
diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp
index e1345bbac4b..35d2cf3b1be 100644
--- a/src/config/configmanager.cpp
+++ b/src/config/configmanager.cpp
@@ -225,6 +225,7 @@ bool ConfigManager::load() {
loadIntConfig(L, CRITICALCHANCE, "criticalChance", 10);
loadIntConfig(L, DAY_KILLS_TO_RED, "dayKillsToRedSkull", 3);
loadIntConfig(L, DEATH_LOSE_PERCENT, "deathLosePercent", -1);
+ loadIntConfig(L, DEFAULT_RESPAWN_TIME, "defaultRespawnTime", 60);
loadIntConfig(L, DEFAULT_DESPAWNRADIUS, "deSpawnRadius", 50);
loadIntConfig(L, DEFAULT_DESPAWNRANGE, "deSpawnRange", 2);
loadIntConfig(L, DEPOTCHEST, "depotChest", 4);
diff --git a/src/creatures/appearance/mounts/mounts.cpp b/src/creatures/appearance/mounts/mounts.cpp
index 7051d04ddb8..7014d0b026b 100644
--- a/src/creatures/appearance/mounts/mounts.cpp
+++ b/src/creatures/appearance/mounts/mounts.cpp
@@ -29,7 +29,7 @@ bool Mounts::loadFromXml() {
}
for (auto mountNode : doc.child("mounts").children()) {
- uint16_t lookType = pugi::cast(mountNode.attribute("clientid").value());
+ auto lookType = pugi::cast(mountNode.attribute("clientid").value());
if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && lookType != 0 && !g_game().isLookTypeRegistered(lookType)) {
g_logger().warn("{} - An unregistered creature mount with id '{}' was blocked to prevent client crash.", __FUNCTION__, lookType);
continue;
diff --git a/src/creatures/appearance/outfit/outfit.cpp b/src/creatures/appearance/outfit/outfit.cpp
index 9e96c60e756..251bf7bde35 100644
--- a/src/creatures/appearance/outfit/outfit.cpp
+++ b/src/creatures/appearance/outfit/outfit.cpp
@@ -14,6 +14,10 @@
#include "utils/tools.hpp"
#include "game/game.hpp"
+Outfits &Outfits::getInstance() {
+ return inject();
+}
+
bool Outfits::reload() {
for (auto &outfitsVector : outfits) {
outfitsVector.clear();
@@ -41,7 +45,7 @@ bool Outfits::loadFromXml() {
continue;
}
- uint16_t type = pugi::cast(attr.value());
+ auto type = pugi::cast(attr.value());
if (type > PLAYERSEX_LAST) {
g_logger().warn("[Outfits::loadFromXml] - Invalid outfit type {}", type);
continue;
@@ -53,9 +57,9 @@ bool Outfits::loadFromXml() {
continue;
}
- if (uint16_t lookType = pugi::cast(lookTypeAttribute.value());
- g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && lookType != 0
- && !g_game().isLookTypeRegistered(lookType)) {
+ if (auto lookType = pugi::cast(lookTypeAttribute.value());
+ g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && lookType != 0
+ && !g_game().isLookTypeRegistered(lookType)) {
g_logger().warn("[Outfits::loadFromXml] An unregistered creature looktype type with id '{}' was ignored to prevent client crash.", lookType);
continue;
}
@@ -74,7 +78,22 @@ bool Outfits::loadFromXml() {
return true;
}
-std::shared_ptr Outfits::getOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const {
+std::shared_ptr Outfits::getOutfitByLookType(const std::shared_ptr &player, uint16_t lookType, bool isOppositeOutfit) const {
+ if (!player) {
+ g_logger().error("[{}] - Player not found", __FUNCTION__);
+ return nullptr;
+ }
+
+ auto sex = player->getSex();
+ if (sex != PLAYERSEX_FEMALE && sex != PLAYERSEX_MALE) {
+ g_logger().error("[{}] - Sex invalid or player: {}", __FUNCTION__, player->getName());
+ return nullptr;
+ }
+
+ if (isOppositeOutfit) {
+ sex = (sex == PLAYERSEX_MALE) ? PLAYERSEX_FEMALE : PLAYERSEX_MALE;
+ }
+
auto it = std::ranges::find_if(outfits[sex], [&lookType](const auto &outfit) {
return outfit->lookType == lookType;
});
@@ -84,15 +103,3 @@ std::shared_ptr Outfits::getOutfitByLookType(PlayerSex_t sex, uint16_t l
}
return nullptr;
}
-
-/**
- * Get the oposite sex equivalent outfit
- * @param sex current sex
- * @param lookType current looktype
- * @return const pointer to the outfit or nullptr if it could not be found.
- */
-
-std::shared_ptr Outfits::getOpositeSexOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const {
- PlayerSex_t searchSex = (sex == PLAYERSEX_MALE) ? PLAYERSEX_FEMALE : PLAYERSEX_MALE;
- return getOutfitByLookType(searchSex, lookType);
-}
diff --git a/src/creatures/appearance/outfit/outfit.hpp b/src/creatures/appearance/outfit/outfit.hpp
index 30b84d1aca2..0d89a2c932d 100644
--- a/src/creatures/appearance/outfit/outfit.hpp
+++ b/src/creatures/appearance/outfit/outfit.hpp
@@ -12,8 +12,10 @@
#include "declarations.hpp"
#include "lib/di/container.hpp"
+class Player;
+
struct OutfitEntry {
- constexpr OutfitEntry(uint16_t initLookType, uint8_t initAddons) :
+ constexpr explicit OutfitEntry(uint16_t initLookType, uint8_t initAddons) :
lookType(initLookType), addons(initAddons) { }
uint16_t lookType;
@@ -42,16 +44,12 @@ struct ProtocolOutfit {
class Outfits {
public:
- static Outfits &getInstance() {
- return inject();
- }
-
- std::shared_ptr getOpositeSexOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const;
+ static Outfits &getInstance();
- bool loadFromXml();
bool reload();
+ bool loadFromXml();
- [[nodiscard]] std::shared_ptr getOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const;
+ [[nodiscard]] std::shared_ptr getOutfitByLookType(const std::shared_ptr &player, uint16_t lookType, bool isOppositeOutfit = false) const;
[[nodiscard]] const std::vector> &getOutfits(PlayerSex_t sex) const {
return outfits[sex];
}
diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp
index 2aaadac4d61..87f7500d0a9 100644
--- a/src/creatures/combat/combat.cpp
+++ b/src/creatures/combat/combat.cpp
@@ -21,6 +21,8 @@
#include "items/weapons/weapons.hpp"
#include "map/spectators.hpp"
#include "lib/metrics/metrics.hpp"
+#include "lua/callbacks/event_callback.hpp"
+#include "lua/callbacks/events_callbacks.hpp"
int32_t Combat::getLevelFormula(std::shared_ptr player, const std::shared_ptr wheelSpell, const CombatDamage &damage) const {
if (!player) {
@@ -273,8 +275,11 @@ ReturnValue Combat::canDoCombat(std::shared_ptr caster, std::shared_pt
}
}
}
-
- return g_events().eventCreatureOnAreaCombat(caster, tile, aggressive);
+ ReturnValue ret = g_events().eventCreatureOnAreaCombat(caster, tile, aggressive);
+ if (ret == RETURNVALUE_NOERROR) {
+ ret = g_callbacks().checkCallbackWithReturnValue(EventCallback_t::creatureOnTargetCombat, &EventCallback::creatureOnAreaCombat, caster, tile, aggressive);
+ }
+ return ret;
}
bool Combat::isInPvpZone(std::shared_ptr attacker, std::shared_ptr target) {
@@ -409,7 +414,11 @@ ReturnValue Combat::canDoCombat(std::shared_ptr attacker, std::shared_
}
}
}
- return g_events().eventCreatureOnTargetCombat(attacker, target);
+ ReturnValue ret = g_events().eventCreatureOnTargetCombat(attacker, target);
+ if (ret == RETURNVALUE_NOERROR) {
+ ret = g_callbacks().checkCallbackWithReturnValue(EventCallback_t::creatureOnTargetCombat, &EventCallback::creatureOnTargetCombat, attacker, target);
+ }
+ return ret;
}
void Combat::setPlayerCombatValues(formulaType_t newFormulaType, double newMina, double newMinb, double newMaxa, double newMaxb) {
@@ -648,8 +657,8 @@ CombatDamage Combat::applyImbuementElementalDamage(std::shared_ptr attac
}
if (imbuementInfo.imbuement->combatType == COMBAT_NONE
- || damage.primary.type == COMBAT_HEALING
- || damage.secondary.type == COMBAT_HEALING) {
+ || damage.primary.type == COMBAT_HEALING
+ || damage.secondary.type == COMBAT_HEALING) {
continue;
}
@@ -1247,8 +1256,8 @@ void Combat::doCombatHealth(std::shared_ptr caster, std::shared_ptr caster, std::shared_ptr target, const Position &origin, CombatDamage &damage, const CombatParams ¶ms) {
bool canCombat = !params.aggressive || (caster != target && Combat::canDoCombat(caster, target, params.aggressive) == RETURNVALUE_NOERROR);
if ((caster && target)
- && (caster == target || canCombat)
- && (params.impactEffect != CONST_ME_NONE)) {
+ && (caster == target || canCombat)
+ && (params.impactEffect != CONST_ME_NONE)) {
g_game().addMagicEffect(target->getPosition(), params.impactEffect);
}
@@ -1291,8 +1300,8 @@ void Combat::doCombatMana(std::shared_ptr caster, std::shared_ptr caster, std::shared_ptr target, const Position &origin, CombatDamage &damage, const CombatParams ¶ms) {
bool canCombat = !params.aggressive || (caster != target && Combat::canDoCombat(caster, target, params.aggressive) == RETURNVALUE_NOERROR);
if ((caster && target)
- && (caster == target || canCombat)
- && (params.impactEffect != CONST_ME_NONE)) {
+ && (caster == target || canCombat)
+ && (params.impactEffect != CONST_ME_NONE)) {
g_game().addMagicEffect(target->getPosition(), params.impactEffect);
}
@@ -1359,8 +1368,8 @@ void Combat::doCombatDispel(std::shared_ptr caster, const Position &po
void Combat::doCombatDispel(std::shared_ptr caster, std::shared_ptr target, const CombatParams ¶ms) {
bool canCombat = !params.aggressive || (caster != target && Combat::canDoCombat(caster, target, params.aggressive) == RETURNVALUE_NOERROR);
if ((caster && target)
- && (caster == target || canCombat)
- && (params.impactEffect != CONST_ME_NONE)) {
+ && (caster == target || canCombat)
+ && (params.impactEffect != CONST_ME_NONE)) {
g_game().addMagicEffect(target->getPosition(), params.impactEffect);
}
@@ -1399,7 +1408,7 @@ void Combat::doCombatDefault(std::shared_ptr caster, std::shared_ptrgetPosition(), params.impactEffect);
+ g_game().addMagicEffect(target->getPosition(), params.impactEffect);
}
*/
@@ -1531,8 +1540,8 @@ void ValueCallback::getMinMaxValues(std::shared_ptr player, CombatDamage
// onGetPlayerMinMaxValues(...)
if (!scriptInterface->reserveScriptEnv()) {
g_logger().error("[ValueCallback::getMinMaxValues - Player {} formula {}] "
- "Call stack overflow. Too many lua script calls being nested.",
- player->getName(), fmt::underlying(type));
+ "Call stack overflow. Too many lua script calls being nested.",
+ player->getName(), fmt::underlying(type));
return;
}
@@ -1624,8 +1633,8 @@ void TileCallback::onTileCombat(std::shared_ptr creature, std::shared_
// onTileCombat(creature, pos)
if (!scriptInterface->reserveScriptEnv()) {
g_logger().error("[TileCallback::onTileCombat - Creature {} type {} on tile x: {} y: {} z: {}] "
- "Call stack overflow. Too many lua script calls being nested.",
- creature->getName(), fmt::underlying(type), (tile->getPosition()).getX(), (tile->getPosition()).getY(), (tile->getPosition()).getZ());
+ "Call stack overflow. Too many lua script calls being nested.",
+ creature->getName(), fmt::underlying(type), (tile->getPosition()).getX(), (tile->getPosition()).getY(), (tile->getPosition()).getZ());
return;
}
@@ -1655,8 +1664,8 @@ void TargetCallback::onTargetCombat(std::shared_ptr creature, std::sha
// onTargetCombat(creature, target)
if (!scriptInterface->reserveScriptEnv()) {
g_logger().error("[TargetCallback::onTargetCombat - Creature {}] "
- "Call stack overflow. Too many lua script calls being nested.",
- creature->getName());
+ "Call stack overflow. Too many lua script calls being nested.",
+ creature->getName());
return;
}
@@ -1715,8 +1724,8 @@ void ChainCallback::onChainCombat(std::shared_ptr creature, uint8_t &m
// onChainCombat(creature)
if (!scriptInterface->reserveScriptEnv()) {
g_logger().error("[ChainCallback::onTargetCombat - Creature {}] "
- "Call stack overflow. Too many lua script calls being nested.",
- creature->getName());
+ "Call stack overflow. Too many lua script calls being nested.",
+ creature->getName());
return;
}
@@ -1757,8 +1766,8 @@ bool ChainPickerCallback::onChainCombat(std::shared_ptr creature, std:
// onChainCombat(creature, target)
if (!scriptInterface->reserveScriptEnv()) {
g_logger().error("[ChainPickerCallback::onTargetCombat - Creature {}] "
- "Call stack overflow. Too many lua script calls being nested.",
- creature->getName());
+ "Call stack overflow. Too many lua script calls being nested.",
+ creature->getName());
return true;
}
@@ -2169,7 +2178,7 @@ void Combat::applyExtensions(std::shared_ptr caster, std::shared_ptrgetInventoryItem(CONST_SLOT_LEFT);
- playerWeapon != nullptr && playerWeapon->getTier() > 0) {
+ playerWeapon != nullptr && playerWeapon->getTier() > 0) {
double_t fatalChance = playerWeapon->getFatalChance();
double_t randomChance = uniform_random(0, 10000) / 100;
if (fatalChance > 0 && randomChance < fatalChance) {
diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp
index 7f477f1d859..b9603d010a2 100644
--- a/src/creatures/combat/condition.cpp
+++ b/src/creatures/combat/condition.cpp
@@ -2042,7 +2042,7 @@ bool ConditionFeared::executeCondition(std::shared_ptr creature, int32
g_dispatcher().addEvent([id = creature->getID(), listDir = listDir.data()] {
g_game().forcePlayerAutoWalk(id, listDir);
},
- "ConditionFeared::executeCondition");
+ "ConditionFeared::executeCondition");
g_logger().debug("[ConditionFeared::executeCondition] Walking Scheduled");
}
diff --git a/src/creatures/combat/spells.cpp b/src/creatures/combat/spells.cpp
index dd4305de14e..f8852c5e534 100644
--- a/src/creatures/combat/spells.cpp
+++ b/src/creatures/combat/spells.cpp
@@ -108,7 +108,7 @@ void Spells::clear() {
bool Spells::hasInstantSpell(const std::string &word) const {
if (auto iterate = instants.find(word);
- iterate != instants.end()) {
+ iterate != instants.end()) {
return true;
}
return false;
@@ -127,8 +127,8 @@ bool Spells::registerInstantLuaEvent(const std::shared_ptr instant
// Checks if there is any spell registered with the same name
if (hasInstantSpell(words)) {
g_logger().warn("[Spells::registerInstantLuaEvent] - "
- "Duplicate registered instant spell with words: {}, on spell with name: {}",
- words, instantName);
+ "Duplicate registered instant spell with words: {}, on spell with name: {}",
+ words, instantName);
return false;
}
// Register spell word in the map
@@ -166,7 +166,7 @@ std::list Spells::getSpellsByVocation(uint16_t vocationId) {
vocSpellsIt = vocSpells.find(vocationId);
if (vocSpellsIt != vocSpells.end()
- && vocSpellsIt->second) {
+ && vocSpellsIt->second) {
spellsList.push_back(it.second->getSpellId());
}
}
@@ -361,8 +361,8 @@ bool CombatSpell::executeCastSpell(std::shared_ptr creature, const Lua
// onCastSpell(creature, var)
if (!getScriptInterface()->reserveScriptEnv()) {
g_logger().error("[CombatSpell::executeCastSpell - Creature {}] "
- "Call stack overflow. Too many lua script calls being nested.",
- creature->getName());
+ "Call stack overflow. Too many lua script calls being nested.",
+ creature->getName());
return false;
}
@@ -950,8 +950,8 @@ bool InstantSpell::executeCastSpell(std::shared_ptr creature, const Lu
// onCastSpell(creature, var)
if (!getScriptInterface()->reserveScriptEnv()) {
g_logger().error("[InstantSpell::executeCastSpell - Creature {} words {}] "
- "Call stack overflow. Too many lua script calls being nested.",
- creature->getName(), getWords());
+ "Call stack overflow. Too many lua script calls being nested.",
+ creature->getName(), getWords());
return false;
}
@@ -1095,8 +1095,8 @@ bool RuneSpell::executeCastSpell(std::shared_ptr creature, const LuaVa
// onCastSpell(creature, var, isHotkey)
if (!getScriptInterface()->reserveScriptEnv()) {
g_logger().error("[RuneSpell::executeCastSpell - Creature {} runeId {}] "
- "Call stack overflow. Too many lua script calls being nested.",
- creature->getName(), getRuneItemId());
+ "Call stack overflow. Too many lua script calls being nested.",
+ creature->getName(), getRuneItemId());
return false;
}
diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp
index 7796863f066..c68ab4f351b 100644
--- a/src/creatures/creature.cpp
+++ b/src/creatures/creature.cpp
@@ -259,7 +259,7 @@ void Creature::addEventWalk(bool firstStep) {
self->eventWalk = g_dispatcher().scheduleEvent(
static_cast(ticks),
- [creatureId = self->getID()] { g_game().checkCreatureWalk(creatureId); }, "Creature::checkCreatureWalk"
+ [creatureId = self->getID()] { g_game().checkCreatureWalk(creatureId); }, "Game::checkCreatureWalk"
);
});
}
@@ -828,7 +828,7 @@ bool Creature::dropCorpse(std::shared_ptr lastHitCreature, std::shared
g_dispatcher().addEvent([player, corpseContainer, corpsePosition = corpse->getPosition()] {
g_game().playerQuickLootCorpse(player, corpseContainer, corpsePosition);
},
- "Game::playerQuickLootCorpse");
+ "Game::playerQuickLootCorpse");
}
}
}
diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp
index 33f62dbd2aa..74ec6c5f2c8 100644
--- a/src/creatures/creatures_definitions.hpp
+++ b/src/creatures/creatures_definitions.hpp
@@ -1524,12 +1524,12 @@ using StashItemList = std::map;
using ItemsTierCountList = std::map>;
/*
- > ItemsTierCountList structure:
- |- [itemID]
- |- [itemTier]
- |- Count
- | ...
- | ...
+ > ItemsTierCountList structure:
+ |- [itemID]
+ |- [itemTier]
+ |- Count
+ | ...
+ | ...
*/
struct ProtocolFamiliars {
diff --git a/src/creatures/interactions/chat.cpp b/src/creatures/interactions/chat.cpp
index e16af670fcc..900247e457a 100644
--- a/src/creatures/interactions/chat.cpp
+++ b/src/creatures/interactions/chat.cpp
@@ -145,8 +145,8 @@ bool ChatChannel::executeCanJoinEvent(const std::shared_ptr &player) {
LuaScriptInterface* scriptInterface = g_chat().getScriptInterface();
if (!scriptInterface->reserveScriptEnv()) {
g_logger().error("[CanJoinChannelEvent::execute - Player {}, on channel {}] "
- "Call stack overflow. Too many lua script calls being nested.",
- player->getName(), getName());
+ "Call stack overflow. Too many lua script calls being nested.",
+ player->getName(), getName());
return false;
}
@@ -171,8 +171,8 @@ bool ChatChannel::executeOnJoinEvent(const std::shared_ptr &player) {
LuaScriptInterface* scriptInterface = g_chat().getScriptInterface();
if (!scriptInterface->reserveScriptEnv()) {
g_logger().error("[OnJoinChannelEvent::execute - Player {}, on channel {}] "
- "Call stack overflow. Too many lua script calls being nested",
- player->getName(), getName());
+ "Call stack overflow. Too many lua script calls being nested",
+ player->getName(), getName());
return false;
}
@@ -197,8 +197,8 @@ bool ChatChannel::executeOnLeaveEvent(const std::shared_ptr &player) {
LuaScriptInterface* scriptInterface = g_chat().getScriptInterface();
if (!scriptInterface->reserveScriptEnv()) {
g_logger().error("[OnLeaveChannelEvent::execute - Player {}, on channel {}] "
- "Call stack overflow. Too many lua script calls being nested.",
- player->getName(), getName());
+ "Call stack overflow. Too many lua script calls being nested.",
+ player->getName(), getName());
return false;
}
@@ -223,8 +223,8 @@ bool ChatChannel::executeOnSpeakEvent(const std::shared_ptr &player, Spe
LuaScriptInterface* scriptInterface = g_chat().getScriptInterface();
if (!scriptInterface->reserveScriptEnv()) {
g_logger().error("[OnSpeakChannelEvent::execute - Player {}, type {}] "
- "Call stack overflow. Too many lua script calls being nested.",
- player->getName(), fmt::underlying(type));
+ "Call stack overflow. Too many lua script calls being nested.",
+ player->getName(), fmt::underlying(type));
return false;
}
diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp
index fccc19b05e5..4b993cbab0e 100644
--- a/src/creatures/monsters/monster.cpp
+++ b/src/creatures/monsters/monster.cpp
@@ -33,7 +33,7 @@ std::shared_ptr Monster::createMonster(const std::string &name) {
Monster::Monster(const std::shared_ptr mType) :
Creature(),
- strDescription(asLowerCaseString(mType->nameDescription)),
+ nameDescription(asLowerCaseString(mType->nameDescription)),
mType(mType) {
defaultOutfit = mType->info.outfit;
currentOutfit = mType->info.outfit;
@@ -50,8 +50,8 @@ Monster::Monster(const std::shared_ptr mType) :
for (const std::string &scriptName : mType->info.scripts) {
if (!registerCreatureEvent(scriptName)) {
g_logger().warn("[Monster::Monster] - "
- "Unknown event name: {}",
- scriptName);
+ "Unknown event name: {}",
+ scriptName);
}
}
}
@@ -64,6 +64,37 @@ void Monster::removeList() {
g_game().removeMonster(static_self_cast());
}
+const std::string &Monster::getName() const {
+ if (name.empty()) {
+ return mType->name;
+ }
+ return name;
+}
+
+void Monster::setName(const std::string &name) {
+ if (getName() == name) {
+ return;
+ }
+
+ this->name = name;
+
+ // NOTE: Due to how client caches known creatures,
+ // it is not feasible to send creature update to everyone that has ever met it
+ auto spectators = Spectators().find(position, true);
+ for (const auto &spectator : spectators) {
+ if (const auto &tmpPlayer = spectator->getPlayer()) {
+ tmpPlayer->sendUpdateTileCreature(static_self_cast());
+ }
+ }
+}
+
+const std::string &Monster::getNameDescription() const {
+ if (nameDescription.empty()) {
+ return mType->nameDescription;
+ }
+ return nameDescription;
+}
+
bool Monster::canWalkOnFieldType(CombatType_t combatType) const {
switch (combatType) {
case COMBAT_ENERGYDAMAGE:
@@ -107,8 +138,8 @@ void Monster::onCreatureAppear(std::shared_ptr creature, bool isLogin)
LuaScriptInterface* scriptInterface = mType->info.scriptInterface;
if (!scriptInterface->reserveScriptEnv()) {
g_logger().error("[Monster::onCreatureAppear - Monster {} creature {}] "
- "Call stack overflow. Too many lua script calls being nested.",
- getName(), creature->getName());
+ "Call stack overflow. Too many lua script calls being nested.",
+ getName(), creature->getName());
return;
}
@@ -145,8 +176,8 @@ void Monster::onRemoveCreature(std::shared_ptr creature, bool isLogout
LuaScriptInterface* scriptInterface = mType->info.scriptInterface;
if (!scriptInterface->reserveScriptEnv()) {
g_logger().error("[Monster::onCreatureDisappear - Monster {} creature {}] "
- "Call stack overflow. Too many lua script calls being nested.",
- getName(), creature->getName());
+ "Call stack overflow. Too many lua script calls being nested.",
+ getName(), creature->getName());
return;
}
@@ -186,8 +217,8 @@ void Monster::onCreatureMove(const std::shared_ptr &creature, const st
LuaScriptInterface* scriptInterface = mType->info.scriptInterface;
if (!scriptInterface->reserveScriptEnv()) {
g_logger().error("[Monster::onCreatureMove - Monster {} creature {}] "
- "Call stack overflow. Too many lua script calls being nested.",
- getName(), creature->getName());
+ "Call stack overflow. Too many lua script calls being nested.",
+ getName(), creature->getName());
return;
}
@@ -260,8 +291,8 @@ void Monster::onCreatureSay(std::shared_ptr creature, SpeakClasses typ
LuaScriptInterface* scriptInterface = mType->info.scriptInterface;
if (!scriptInterface->reserveScriptEnv()) {
g_logger().error("Monster {} creature {}] Call stack overflow. Too many lua "
- "script calls being nested.",
- getName(), creature->getName());
+ "script calls being nested.",
+ getName(), creature->getName());
return;
}
@@ -740,8 +771,8 @@ void Monster::onThink(uint32_t interval) {
LuaScriptInterface* scriptInterface = mType->info.scriptInterface;
if (!scriptInterface->reserveScriptEnv()) {
g_logger().error("Monster {} Call stack overflow. Too many lua script calls "
- "being nested.",
- getName());
+ "being nested.",
+ getName());
return;
}
@@ -2010,8 +2041,8 @@ void Monster::dropLoot(std::shared_ptr corpse, std::shared_ptrname;
- }
// Real monster name, set on monster creation "createMonsterType(typeName)"
const std::string &getTypeName() const override {
return mType->typeName;
}
- const std::string &getNameDescription() const override {
- return mType->nameDescription;
- }
+ const std::string &getNameDescription() const override;
+ void setNameDescription(const std::string &nameDescription) {
+ this->nameDescription = nameDescription;
+ };
std::string getDescription(int32_t) override {
- return strDescription + '.';
+ return nameDescription + '.';
}
CreatureType_t getType() const override {
@@ -363,7 +364,8 @@ class Monster final : public Creature {
uint16_t forgeStack = 0;
ForgeClassifications_t monsterForgeClassification = ForgeClassifications_t::FORGE_NORMAL_MONSTER;
- std::string strDescription;
+ std::string name;
+ std::string nameDescription;
std::shared_ptr mType;
SpawnMonster* spawnMonster = nullptr;
@@ -453,7 +455,11 @@ class Monster final : public Creature {
void dropLoot(std::shared_ptr corpse, std::shared_ptr lastHitCreature) override;
void getPathSearchParams(const std::shared_ptr &creature, FindPathParams &fpp) override;
bool useCacheMap() const override {
- return !randomStepping;
+ // return !randomStepping;
+ // As the map cache is done synchronously for each movement that a monster makes, it is better to disable it,
+ // as the pathfinder, which is one of the resources that uses this cache the most,
+ // is multithreding and thus the processing cost is divided between the threads.
+ return false;
}
friend class MonsterFunctions;
diff --git a/src/creatures/monsters/monsters.cpp b/src/creatures/monsters/monsters.cpp
index 3d457aa4d47..78358f69d87 100644
--- a/src/creatures/monsters/monsters.cpp
+++ b/src/creatures/monsters/monsters.cpp
@@ -97,7 +97,7 @@ bool Monsters::deserializeSpell(const std::shared_ptr spell, spell
}
if (std::string spellName = asLowerCaseString(spell->name);
- spellName == "melee") {
+ spellName == "melee") {
sb.isMelee = true;
if (spell->attack > 0 && spell->skill > 0) {
@@ -164,8 +164,8 @@ bool Monsters::deserializeSpell(const std::shared_ptr spell, spell
condition->setOutfit(outfit);
} else {
g_logger().error("[Monsters::deserializeSpell] - "
- "Missing outfit monster or item in outfit spell for: {}",
- description);
+ "Missing outfit monster or item in outfit spell for: {}",
+ description);
return false;
}
@@ -208,8 +208,8 @@ bool Monsters::deserializeSpell(const std::shared_ptr spell, spell
} else if (spellName == "condition") {
if (spell->conditionType == CONDITION_NONE) {
g_logger().error("[Monsters::deserializeSpell] - "
- "{} condition is not set for: {}",
- description, spell->name);
+ "{} condition is not set for: {}",
+ description, spell->name);
}
} else if (spellName == "strength") {
//
@@ -217,8 +217,8 @@ bool Monsters::deserializeSpell(const std::shared_ptr spell, spell
//
} else {
g_logger().error("[Monsters::deserializeSpell] - "
- "{} unknown or missing parameter on spell with name: {}",
- description, spell->name);
+ "{} unknown or missing parameter on spell with name: {}",
+ description, spell->name);
}
if (spell->shoot != CONST_ANI_NONE) {
@@ -295,9 +295,9 @@ bool MonsterType::loadCallback(LuaScriptInterface* scriptInterface) {
std::shared_ptr Monsters::getMonsterType(const std::string &name, bool silent /* = false*/) const {
std::string lowerCaseName = asLowerCaseString(name);
if (auto it = monsters.find(lowerCaseName);
- it != monsters.end()
- // We will only return the MonsterType if it match the exact name of the monster
- && it->first.find(lowerCaseName) != it->first.npos) {
+ it != monsters.end()
+ // We will only return the MonsterType if it match the exact name of the monster
+ && it->first.find(lowerCaseName) != it->first.npos) {
return it->second;
}
if (!silent) {
diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp
index ef5912c8052..968b90d8cae 100644
--- a/src/creatures/monsters/spawns/spawn_monster.cpp
+++ b/src/creatures/monsters/spawns/spawn_monster.cpp
@@ -94,7 +94,15 @@ bool SpawnsMonster::loadFromXML(const std::string &filemonstername) {
weight = pugi::cast(weightAttribute.value());
}
- spawnMonster.addMonster(nameAttribute.as_string(), pos, dir, pugi::cast(childMonsterNode.attribute("spawntime").value()) * 1000, weight);
+ uint32_t scheduleInterval = g_configManager().getNumber(DEFAULT_RESPAWN_TIME, __FUNCTION__);
+
+ try {
+ scheduleInterval = pugi::cast(childMonsterNode.attribute("spawntime").value());
+ } catch (...) {
+ g_logger().warn("Failed to add schedule interval to monster: {}, interval: {}. Setting to default respawn time: {}", nameAttribute.value(), childMonsterNode.attribute("spawntime").value(), scheduleInterval);
+ }
+
+ spawnMonster.addMonster(nameAttribute.as_string(), pos, dir, scheduleInterval * 1000, weight);
}
}
}
diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp
index e445a58fcca..fdbf58853b4 100644
--- a/src/creatures/npcs/npc.cpp
+++ b/src/creatures/npcs/npc.cpp
@@ -101,14 +101,13 @@ void Npc::onRemoveCreature(std::shared_ptr creature, bool isLogout) {
}
if (auto player = creature->getPlayer()) {
+ removeShopPlayer(player->getGUID());
onPlayerDisappear(player);
}
if (spawnNpc) {
spawnNpc->startSpawnNpcCheck();
}
-
- shopPlayerMap.clear();
}
void Npc::onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) {
@@ -259,7 +258,7 @@ void Npc::onPlayerBuyItem(std::shared_ptr player, uint16_t itemId, uint8
}
uint32_t buyPrice = 0;
- const std::vector &shopVector = getShopItemVector(player->getGUID());
+ const auto &shopVector = getShopItemVector(player->getGUID());
for (const ShopBlock &shopBlock : shopVector) {
if (itemType.id == shopBlock.itemId && shopBlock.itemBuyPrice != 0) {
buyPrice = shopBlock.itemBuyPrice;
@@ -372,7 +371,7 @@ void Npc::onPlayerSellItem(std::shared_ptr player, uint16_t itemId, uint
uint32_t sellPrice = 0;
const ItemType &itemType = Item::items[itemId];
- const std::vector &shopVector = getShopItemVector(player->getGUID());
+ const auto &shopVector = getShopItemVector(player->getGUID());
for (const ShopBlock &shopBlock : shopVector) {
if (itemType.id == shopBlock.itemId && shopBlock.itemSellPrice != 0) {
sellPrice = shopBlock.itemSellPrice;
@@ -522,7 +521,7 @@ void Npc::onThinkWalk(uint32_t interval) {
}
if (Direction newDirection;
- getRandomStep(newDirection)) {
+ getRandomStep(newDirection)) {
listWalkDir.push_front(newDirection);
addEventWalk();
}
@@ -586,7 +585,7 @@ void Npc::setPlayerInteraction(uint32_t playerId, uint16_t topicId /*= 0*/) {
void Npc::removePlayerInteraction(std::shared_ptr player) {
if (playerInteractions.contains(player->getID())) {
playerInteractions.erase(player->getID());
- player->closeShopWindow(true);
+ player->closeShopWindow();
}
}
@@ -634,7 +633,7 @@ bool Npc::getRandomStep(Direction &moveDirection) {
std::ranges::shuffle(directionvector, getRandomGenerator());
for (const Position &creaturePos = getPosition();
- Direction direction : directionvector) {
+ Direction direction : directionvector) {
if (canWalkTo(creaturePos, direction)) {
moveDirection = direction;
return true;
@@ -643,30 +642,26 @@ bool Npc::getRandomStep(Direction &moveDirection) {
return false;
}
-void Npc::addShopPlayer(const std::shared_ptr &player, const std::vector &shopItems /* = {}*/) {
- if (!player) {
- return;
- }
-
- shopPlayerMap.try_emplace(player->getGUID(), shopItems);
+bool Npc::isShopPlayer(uint32_t playerGUID) const {
+ return shopPlayers.find(playerGUID) != shopPlayers.end();
}
-void Npc::removeShopPlayer(const std::shared_ptr &player) {
- if (!player) {
- return;
- }
+void Npc::addShopPlayer(uint32_t playerGUID, const std::vector &shopItems) {
+ shopPlayers.try_emplace(playerGUID, shopItems);
+}
- shopPlayerMap.erase(player->getGUID());
+void Npc::removeShopPlayer(uint32_t playerGUID) {
+ shopPlayers.erase(playerGUID);
}
void Npc::closeAllShopWindows() {
- for (const auto &[playerGUID, playerPtr] : shopPlayerMap) {
- auto shopPlayer = g_game().getPlayerByGUID(playerGUID);
- if (shopPlayer) {
- shopPlayer->closeShopWindow();
+ for (const auto &[playerGUID, shopBlock] : shopPlayers) {
+ const auto &player = g_game().getPlayerByGUID(playerGUID);
+ if (player) {
+ player->closeShopWindow();
}
}
- shopPlayerMap.clear();
+ shopPlayers.clear();
}
void Npc::handlePlayerMove(std::shared_ptr player, const Position &newPos) {
diff --git a/src/creatures/npcs/npc.hpp b/src/creatures/npcs/npc.hpp
index 7e8be84c7f1..c246aa4b68d 100644
--- a/src/creatures/npcs/npc.hpp
+++ b/src/creatures/npcs/npc.hpp
@@ -95,10 +95,10 @@ class Npc final : public Creature {
npcType->info.currencyId = currency;
}
- std::vector getShopItemVector(uint32_t playerGUID) {
+ const std::vector &getShopItemVector(uint32_t playerGUID) const {
if (playerGUID != 0) {
- auto it = shopPlayerMap.find(playerGUID);
- if (it != shopPlayerMap.end() && !it->second.empty()) {
+ auto it = shopPlayers.find(playerGUID);
+ if (it != shopPlayers.end() && !it->second.empty()) {
return it->second;
}
}
@@ -165,8 +165,10 @@ class Npc final : public Creature {
internalLight = npcType->info.light;
}
- void addShopPlayer(const std::shared_ptr &player, const std::vector &shopItems = {});
- void removeShopPlayer(const std::shared_ptr &player);
+ bool isShopPlayer(uint32_t playerGUID) const;
+
+ void addShopPlayer(uint32_t playerGUID, const std::vector &shopItems);
+ void removeShopPlayer(uint32_t playerGUID);
void closeAllShopWindows();
static uint32_t npcAutoID;
@@ -184,7 +186,7 @@ class Npc final : public Creature {
std::map playerInteractions;
- phmap::flat_hash_map> shopPlayerMap;
+ std::unordered_map> shopPlayers;
std::shared_ptr npcType;
std::shared_ptr spawnNpc;
diff --git a/src/creatures/players/achievement/player_achievement.cpp b/src/creatures/players/achievement/player_achievement.cpp
index 2db53dbe776..69d1d7ab1fa 100644
--- a/src/creatures/players/achievement/player_achievement.cpp
+++ b/src/creatures/players/achievement/player_achievement.cpp
@@ -53,7 +53,7 @@ bool PlayerAchievement::remove(uint16_t id) {
if (auto it = std::find_if(m_achievementsUnlocked.begin(), m_achievementsUnlocked.end(), [id](auto achievement_it) {
return achievement_it.first == id;
});
- it != m_achievementsUnlocked.end()) {
+ it != m_achievementsUnlocked.end()) {
getUnlockedKV()->remove(achievement.name);
m_achievementsUnlocked.erase(it);
removePoints(achievement.points);
@@ -72,7 +72,7 @@ bool PlayerAchievement::isUnlocked(uint16_t id) const {
if (auto it = std::find_if(m_achievementsUnlocked.begin(), m_achievementsUnlocked.end(), [id](auto achievement_it) {
return achievement_it.first == id;
});
- it != m_achievementsUnlocked.end()) {
+ it != m_achievementsUnlocked.end()) {
return true;
}
diff --git a/src/creatures/players/cyclopedia/player_badge.cpp b/src/creatures/players/cyclopedia/player_badge.cpp
index 9b892a6164c..639640b2ffe 100644
--- a/src/creatures/players/cyclopedia/player_badge.cpp
+++ b/src/creatures/players/cyclopedia/player_badge.cpp
@@ -26,7 +26,7 @@ bool PlayerBadge::hasBadge(uint8_t id) const {
if (auto it = std::find_if(m_badgesUnlocked.begin(), m_badgesUnlocked.end(), [id](auto badge_it) {
return badge_it.first.m_id == id;
});
- it != m_badgesUnlocked.end()) {
+ it != m_badgesUnlocked.end()) {
return true;
}
diff --git a/src/creatures/players/cyclopedia/player_title.cpp b/src/creatures/players/cyclopedia/player_title.cpp
index 624b0313457..a6b44f3d3c4 100644
--- a/src/creatures/players/cyclopedia/player_title.cpp
+++ b/src/creatures/players/cyclopedia/player_title.cpp
@@ -26,7 +26,7 @@ bool PlayerTitle::isTitleUnlocked(uint8_t id) const {
if (auto it = std::find_if(m_titlesUnlocked.begin(), m_titlesUnlocked.end(), [id](auto title_it) {
return title_it.first.m_id == id;
});
- it != m_titlesUnlocked.end()) {
+ it != m_titlesUnlocked.end()) {
return true;
}
diff --git a/src/creatures/players/grouping/familiars.cpp b/src/creatures/players/grouping/familiars.cpp
index a922013f19b..6312aaa285d 100644
--- a/src/creatures/players/grouping/familiars.cpp
+++ b/src/creatures/players/grouping/familiars.cpp
@@ -10,10 +10,15 @@
#include "pch.hpp"
#include "creatures/players/grouping/familiars.hpp"
+#include "lib/di/container.hpp"
#include "config/configmanager.hpp"
#include "utils/pugicast.hpp"
#include "utils/tools.hpp"
+Familiars &Familiars::getInstance() {
+ return inject();
+}
+
bool Familiars::reload() {
for (auto &familiarsVector : familiars) {
familiarsVector.clear();
@@ -42,7 +47,7 @@ bool Familiars::loadFromXml() {
continue;
}
- uint16_t vocation = pugi::cast(attr.value());
+ auto vocation = pugi::cast(attr.value());
if (vocation > VOCATION_LAST) {
g_logger().warn("[Familiars::loadFromXml] - Invalid familiar vocation {}", vocation);
continue;
@@ -72,7 +77,7 @@ std::shared_ptr Familiars::getFamiliarByLookType(uint16_t vocation, ui
if (auto it = std::find_if(familiars[vocation].begin(), familiars[vocation].end(), [lookType](auto familiar_it) {
return familiar_it->lookType == lookType;
});
- it != familiars[vocation].end()) {
+ it != familiars[vocation].end()) {
return *it;
}
return nullptr;
diff --git a/src/creatures/players/grouping/familiars.hpp b/src/creatures/players/grouping/familiars.hpp
index 8f553af9fbe..9eda7d95ba2 100644
--- a/src/creatures/players/grouping/familiars.hpp
+++ b/src/creatures/players/grouping/familiars.hpp
@@ -9,6 +9,7 @@
#pragma once
+#include "declarations.hpp"
#include "lib/di/container.hpp"
struct FamiliarEntry {
@@ -32,9 +33,7 @@ struct Familiar {
class Familiars {
public:
- static Familiars &getInstance() {
- return inject();
- }
+ static Familiars &getInstance();
bool loadFromXml();
bool reload();
diff --git a/src/creatures/players/grouping/groups.cpp b/src/creatures/players/grouping/groups.cpp
index 27ae68d9fd4..c4dab4a9039 100644
--- a/src/creatures/players/grouping/groups.cpp
+++ b/src/creatures/players/grouping/groups.cpp
@@ -43,7 +43,7 @@ PlayerFlags_t Groups::getFlagFromNumber(uint8_t value) {
return magic_enum::enum_value(value);
}
-bool Groups::reload() const {
+bool Groups::reload() {
// Clear groups
g_game().groups.getGroups().clear();
return g_game().groups.load();
@@ -99,11 +99,11 @@ bool Groups::load() {
return true;
}
-std::shared_ptr Groups::getGroup(uint16_t id) {
+std::shared_ptr Groups::getGroup(uint16_t id) const {
if (auto it = std::find_if(groups_vector.begin(), groups_vector.end(), [id](auto group_it) {
return group_it->id == id;
});
- it != groups_vector.end()) {
+ it != groups_vector.end()) {
return *it;
}
return nullptr;
diff --git a/src/creatures/players/grouping/groups.hpp b/src/creatures/players/grouping/groups.hpp
index d76914e2632..af319e95772 100644
--- a/src/creatures/players/grouping/groups.hpp
+++ b/src/creatures/players/grouping/groups.hpp
@@ -24,9 +24,9 @@ class Groups {
public:
static uint8_t getFlagNumber(PlayerFlags_t playerFlags);
static PlayerFlags_t getFlagFromNumber(uint8_t value);
- bool reload() const;
+ static bool reload();
bool load();
- std::shared_ptr getGroup(uint16_t id);
+ [[nodiscard]] std::shared_ptr getGroup(uint16_t id) const;
std::vector> &getGroups() {
return groups_vector;
}
diff --git a/src/creatures/players/grouping/party.hpp b/src/creatures/players/grouping/party.hpp
index 10663a278bf..6aaecc56190 100644
--- a/src/creatures/players/grouping/party.hpp
+++ b/src/creatures/players/grouping/party.hpp
@@ -108,7 +108,7 @@ class Party : public SharedObject {
if (auto it = std::find_if(membersData.begin(), membersData.end(), [playerId](const std::shared_ptr &preyIt) {
return preyIt->id == playerId;
});
- it != membersData.end()) {
+ it != membersData.end()) {
return *it;
}
diff --git a/src/creatures/players/imbuements/imbuements.cpp b/src/creatures/players/imbuements/imbuements.cpp
index ed312dbf8e1..4bfb0de836b 100644
--- a/src/creatures/players/imbuements/imbuements.cpp
+++ b/src/creatures/players/imbuements/imbuements.cpp
@@ -347,9 +347,9 @@ std::vector Imbuements::getImbuements(std::shared_ptr player
// Parse the storages for each imbuement in imbuements.xml and config.lua (enable/disable storage)
if (g_configManager().getBoolean(TOGGLE_IMBUEMENT_SHRINE_STORAGE, __FUNCTION__)
- && imbuement->getStorage() != 0
- && player->getStorageValue(imbuement->getStorage() == -1)
- && imbuement->getBaseID() >= 1 && imbuement->getBaseID() <= 3) {
+ && imbuement->getStorage() != 0
+ && player->getStorageValue(imbuement->getStorage() == -1)
+ && imbuement->getBaseID() >= 1 && imbuement->getBaseID() <= 3) {
continue;
}
diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp
index 4109eb77472..a9320ffba5f 100644
--- a/src/creatures/players/player.cpp
+++ b/src/creatures/players/player.cpp
@@ -284,7 +284,7 @@ std::shared_ptr- Player::getQuiverAmmoOfType(const ItemType &it) const {
std::shared_ptr
- quiver = inventory[CONST_SLOT_RIGHT];
for (std::shared_ptr container = quiver->getContainer();
- auto ammoItem : container->getItemList()) {
+ auto ammoItem : container->getItemList()) {
if (ammoItem->getAmmoType() == it.ammoType) {
if (level >= Item::items[ammoItem->getID()].minReqLevel) {
return ammoItem;
@@ -1715,13 +1715,7 @@ void Player::onCreatureAppear(std::shared_ptr creature, bool isLogin)
Creature::onCreatureAppear(creature, isLogin);
if (isLogin && creature == getPlayer()) {
- for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) {
- std::shared_ptr
- item = inventory[slot];
- if (item) {
- item->startDecaying();
- g_moveEvents().onPlayerEquip(getPlayer(), item, static_cast(slot), false);
- }
- }
+ onEquipInventory();
// Refresh bosstiary tracker onLogin
refreshCyclopediaMonsterTracker(true);
@@ -1862,14 +1856,9 @@ void Player::onRemoveCreature(std::shared_ptr creature, bool isLogout)
Creature::onRemoveCreature(creature, isLogout);
if (auto player = getPlayer(); player == creature) {
- for (uint8_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) {
- const auto item = inventory[slot];
- if (item) {
- g_moveEvents().onPlayerDeEquip(getPlayer(), item, static_cast(slot));
- }
- }
-
if (isLogout) {
+ onDeEquipInventory();
+
if (m_party) {
m_party->leaveParty(player);
}
@@ -1905,32 +1894,39 @@ void Player::onRemoveCreature(std::shared_ptr creature, bool isLogout)
}
}
-bool Player::openShopWindow(std::shared_ptr npc) {
+bool Player::openShopWindow(std::shared_ptr npc, const std::vector &shopItems) {
+ Benchmark brenchmark;
if (!npc) {
g_logger().error("[Player::openShopWindow] - Npc is wrong or nullptr");
return false;
}
+ if (npc->isShopPlayer(getGUID())) {
+ g_logger().debug("[Player::openShopWindow] - Player {} is already in shop window", getName());
+ return false;
+ }
+
+ npc->addShopPlayer(getGUID(), shopItems);
+
setShopOwner(npc);
sendShop(npc);
std::map inventoryMap;
sendSaleItemList(getAllSaleItemIdAndCount(inventoryMap));
+
+ g_logger().debug("[Player::openShopWindow] - Player {} has opened shop window in {} ms", getName(), brenchmark.duration());
return true;
}
-bool Player::closeShopWindow(bool sendCloseShopWindow /*= true*/) {
+bool Player::closeShopWindow() {
if (!shopOwner) {
return false;
}
- shopOwner->removeShopPlayer(static_self_cast());
+ shopOwner->removeShopPlayer(getGUID());
setShopOwner(nullptr);
- if (sendCloseShopWindow) {
- sendCloseShop();
- }
-
+ sendCloseShop();
return true;
}
@@ -2011,6 +2007,25 @@ void Player::onCreatureMove(const std::shared_ptr &creature, const std
}
}
+void Player::onEquipInventory() {
+ for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) {
+ std::shared_ptr
- item = inventory[slot];
+ if (item) {
+ item->startDecaying();
+ g_moveEvents().onPlayerEquip(getPlayer(), item, static_cast(slot), false);
+ }
+ }
+}
+
+void Player::onDeEquipInventory() {
+ for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) {
+ std::shared_ptr
- item = inventory[slot];
+ if (item) {
+ g_moveEvents().onPlayerDeEquip(getPlayer(), item, static_cast(slot));
+ }
+ }
+}
+
// container
void Player::onAddContainerItem(std::shared_ptr
- item) {
checkTradeState(item);
@@ -3820,7 +3835,7 @@ bool Player::hasItemCountById(uint16_t itemId, uint32_t itemAmount, bool checkSt
// Check items from stash
for (StashItemList stashToSend = getStashItems();
- auto [stashItemId, itemCount] : stashToSend) {
+ auto [stashItemId, itemCount] : stashToSend) {
if (!checkStash) {
break;
}
@@ -4285,7 +4300,7 @@ bool Player::hasShopItemForSale(uint16_t itemId, uint8_t subType) const {
}
const ItemType &itemType = Item::items[itemId];
- std::vector shoplist = shopOwner->getShopItemVector(getGUID());
+ const auto &shoplist = shopOwner->getShopItemVector(getGUID());
return std::any_of(shoplist.begin(), shoplist.end(), [&](const ShopBlock &shopBlock) {
return shopBlock.itemId == itemId && shopBlock.itemBuyPrice != 0 && (!itemType.isFluidContainer() || shopBlock.itemSubType == subType);
});
@@ -4914,7 +4929,7 @@ bool Player::canWear(uint16_t lookType, uint8_t addons) const {
return true;
}
- const auto &outfit = Outfits::getInstance().getOutfitByLookType(sex, lookType);
+ const auto &outfit = Outfits::getInstance().getOutfitByLookType(getPlayer(), lookType);
if (!outfit) {
return false;
}
@@ -5001,7 +5016,7 @@ bool Player::removeOutfitAddon(uint16_t lookType, uint8_t addons) {
return false;
}
-bool Player::getOutfitAddons(const std::shared_ptr outfit, uint8_t &addons) const {
+bool Player::getOutfitAddons(const std::shared_ptr &outfit, uint8_t &addons) const {
if (group->access) {
addons = 3;
return true;
@@ -5377,7 +5392,7 @@ uint16_t Player::getSkillLevel(skills_t skill) const {
skillLevel = std::max(0, skillLevel + varSkills[skill]);
if (auto it = maxValuePerSkill.find(skill);
- it != maxValuePerSkill.end()) {
+ it != maxValuePerSkill.end()) {
skillLevel = std::min(it->second, skillLevel);
}
@@ -5826,7 +5841,7 @@ bool Player::toggleMount(bool mount) {
return false;
}
- const auto &playerOutfit = Outfits::getInstance().getOutfitByLookType(getSex(), defaultOutfit.lookType);
+ const auto &playerOutfit = Outfits::getInstance().getOutfitByLookType(getPlayer(), defaultOutfit.lookType);
if (!playerOutfit) {
return false;
}
@@ -6213,7 +6228,7 @@ std::pair Player::getForgeSliversAndCores() const {
// Check items from stash
for (StashItemList stashToSend = getStashItems();
- auto [itemId, itemCount] : stashToSend) {
+ auto [itemId, itemCount] : stashToSend) {
if (itemId == ITEM_FORGE_SLIVER) {
sliverCount += itemCount;
}
@@ -6609,7 +6624,7 @@ std::string Player::getBlessingsName() const {
for (uint8_t i = 1; i <= 8; i++) {
if (hasBlessing(i)) {
if (auto blessName = BlessingNames.find(static_cast(i));
- blessName != BlessingNames.end()) {
+ blessName != BlessingNames.end()) {
os << (*blessName).second;
} else {
continue;
@@ -6802,7 +6817,7 @@ void Player::requestDepotSearchItem(uint16_t itemId, uint8_t tier) {
uint32_t stashCount = 0;
if (const ItemType &iType = Item::items[itemId];
- iType.stackable && iType.wareId > 0) {
+ iType.stackable && iType.wareId > 0) {
stashCount = getStashItemCount(itemId);
}
@@ -6851,10 +6866,10 @@ void Player::retrieveAllItemsFromDepotSearch(uint16_t itemId, uint8_t tier, bool
for (const std::shared_ptr
- &locker : depotLocker->getItemList()) {
std::shared_ptr c = locker->getContainer();
if (!c || c->empty() ||
- // Retrieve from inbox.
- (c->isInbox() && isDepot) ||
- // Retrieve from depot.
- (!c->isInbox() && !isDepot)) {
+ // Retrieve from inbox.
+ (c->isInbox() && isDepot) ||
+ // Retrieve from depot.
+ (!c->isInbox() && !isDepot)) {
continue;
}
@@ -6920,7 +6935,7 @@ std::shared_ptr
- Player::getItemFromDepotSearch(uint16_t itemId, const Posi
for (const std::shared_ptr
- &locker : depotLocker->getItemList()) {
std::shared_ptr c = locker->getContainer();
if (!c || c->empty() || (c->isInbox() && pos.y != 0x21) || // From inbox.
- (!c->isInbox() && pos.y != 0x20)) { // From depot.
+ (!c->isInbox() && pos.y != 0x20)) { // From depot.
continue;
}
@@ -7104,7 +7119,7 @@ void Player::forgeFuseItems(ForgeAction_t actionType, uint16_t firstItemId, uint
return;
}
if (returnValue = g_game().internalRemoveItem(secondForgingItem, 1);
- returnValue != RETURNVALUE_NOERROR) {
+ returnValue != RETURNVALUE_NOERROR) {
g_logger().error("[Log 2] Failed to remove forge item {} from player with name {}", secondItemId, getName());
sendCancelMessage(getReturnMessage(returnValue));
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
@@ -7347,7 +7362,7 @@ void Player::forgeTransferItemTier(ForgeAction_t actionType, uint16_t donorItemI
return;
}
if (returnValue = g_game().internalRemoveItem(receiveItem, 1);
- returnValue != RETURNVALUE_NOERROR) {
+ returnValue != RETURNVALUE_NOERROR) {
g_logger().error("[Log 2] Failed to remove transfer item {} from player with name {}", receiveItemId, getName());
sendCancelMessage(getReturnMessage(returnValue));
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
@@ -7487,7 +7502,7 @@ void Player::forgeResourceConversion(ForgeAction_t actionType) {
}
if (std::shared_ptr
- item = Item::CreateItem(ITEM_FORGE_CORE, 1);
- item) {
+ item) {
returnValue = g_game().internalPlayerAddItem(static_self_cast(), item);
}
if (returnValue != RETURNVALUE_NOERROR) {
@@ -7509,7 +7524,7 @@ void Player::forgeResourceConversion(ForgeAction_t actionType) {
auto upgradeCost = dustLevel - 75;
if (auto dusts = getForgeDusts();
- upgradeCost > dusts) {
+ upgradeCost > dusts) {
g_logger().error("[{}] Not enough dust", __FUNCTION__);
sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR);
return;
@@ -8085,7 +8100,7 @@ bool Player::hasPermittedConditionInPZ() const {
uint16_t Player::getDodgeChance() const {
uint16_t chance = 0;
if (auto playerArmor = getInventoryItem(CONST_SLOT_ARMOR);
- playerArmor != nullptr && playerArmor->getTier()) {
+ playerArmor != nullptr && playerArmor->getTier()) {
chance += static_cast(playerArmor->getDodgeChance() * 100);
}
diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp
index 58bebfb9f2f..04b6a139a02 100644
--- a/src/creatures/players/player.hpp
+++ b/src/creatures/players/player.hpp
@@ -153,8 +153,8 @@ class Player final : public Creature, public Cylinder, public Bankable {
const std::string &getName() const override {
return name;
}
- void setName(std::string newName) {
- this->name = std::move(newName);
+ void setName(const std::string &name) {
+ this->name = name;
}
const std::string &getTypeName() const override {
return name;
@@ -230,9 +230,8 @@ class Player final : public Creature, public Cylinder, public Bankable {
void addList() override;
void removePlayer(bool displayEffect, bool forced = true);
- static uint64_t getExpForLevel(int32_t lv) {
- lv--;
- return ((50ULL * lv * lv * lv) - (150ULL * lv * lv) + (400ULL * lv)) / 3ULL;
+ static uint64_t getExpForLevel(const uint32_t level) {
+ return (((level - 6ULL) * level + 17ULL) * level - 12ULL) / 6ULL * 100ULL;
}
uint16_t getStaminaMinutes() const {
@@ -477,7 +476,12 @@ class Player final : public Creature, public Cylinder, public Bankable {
return blessings[index - 1] != 0;
}
uint8_t getBlessingCount(uint8_t index) const {
- return blessings[index - 1];
+ if (index > 0 && index <= blessings.size()) {
+ return blessings[index - 1];
+ } else {
+ g_logger().error("[{}] - index outside range 0-10.", __FUNCTION__);
+ return 0;
+ }
}
std::string getBlessingsName() const;
@@ -846,8 +850,8 @@ class Player final : public Creature, public Cylinder, public Bankable {
void onWalkComplete() override;
void stopWalk();
- bool openShopWindow(std::shared_ptr npc);
- bool closeShopWindow(bool sendCloseShopWindow = true);
+ bool openShopWindow(std::shared_ptr npc, const std::vector &shopItems = {});
+ bool closeShopWindow();
bool updateSaleShopList(std::shared_ptr
- item);
bool hasShopItemForSale(uint16_t itemId, uint8_t subType) const;
@@ -1032,7 +1036,7 @@ class Player final : public Creature, public Cylinder, public Bankable {
void addOutfit(uint16_t lookType, uint8_t addons);
bool removeOutfit(uint16_t lookType);
bool removeOutfitAddon(uint16_t lookType, uint8_t addons);
- bool getOutfitAddons(const std::shared_ptr outfit, uint8_t &addons) const;
+ bool getOutfitAddons(const std::shared_ptr &outfit, uint8_t &addons) const;
bool canFamiliar(uint16_t lookType) const;
void addFamiliar(uint16_t lookType);
@@ -1071,6 +1075,11 @@ class Player final : public Creature, public Cylinder, public Bankable {
client->sendRemoveTileThing(pos, stackpos);
}
}
+ void sendUpdateTileCreature(const std::shared_ptr creature) {
+ if (client) {
+ client->sendUpdateTileCreature(creature->getPosition(), creature->getTile()->getClientIndexOfCreature(static_self_cast(), creature), creature);
+ }
+ }
void sendUpdateTile(std::shared_ptr updateTile, const Position &pos) {
if (client) {
client->sendUpdateTile(updateTile, pos);
@@ -1306,6 +1315,9 @@ class Player final : public Creature, public Cylinder, public Bankable {
void onRemoveCreature(std::shared_ptr creature, bool isLogout) override;
void onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) override;
+ void onEquipInventory();
+ void onDeEquipInventory();
+
void onAttackedCreatureDisappear(bool isLogout) override;
void onFollowCreatureDisappear(bool isLogout) override;
@@ -1944,7 +1956,7 @@ class Player final : public Creature, public Cylinder, public Bankable {
bool isImmuneCleanse(ConditionType_t conditiontype) {
uint64_t timenow = OTSYS_TIME();
if ((cleanseCondition.first == conditiontype)
- && (timenow <= cleanseCondition.second)) {
+ && (timenow <= cleanseCondition.second)) {
return true;
}
return false;
@@ -2142,7 +2154,7 @@ class Player final : public Creature, public Cylinder, public Bankable {
if (auto it = std::find_if(preys.begin(), preys.end(), [slotid](const std::unique_ptr &preyIt) {
return preyIt->id == slotid;
});
- it != preys.end()) {
+ it != preys.end()) {
return *it;
}
@@ -2209,7 +2221,7 @@ class Player final : public Creature, public Cylinder, public Bankable {
if (auto it = std::find_if(preys.begin(), preys.end(), [raceId](const std::unique_ptr &it) {
return it->selectedRaceId == raceId;
});
- it != preys.end()) {
+ it != preys.end()) {
return *it;
}
@@ -2240,7 +2252,7 @@ class Player final : public Creature, public Cylinder, public Bankable {
if (auto it = std::find_if(taskHunting.begin(), taskHunting.end(), [slotid](const std::unique_ptr &itTask) {
return itTask->id == slotid;
});
- it != taskHunting.end()) {
+ it != taskHunting.end()) {
return *it;
}
@@ -2309,7 +2321,7 @@ class Player final : public Creature, public Cylinder, public Bankable {
if (auto it = std::find_if(taskHunting.begin(), taskHunting.end(), [raceId](const std::unique_ptr &itTask) {
return itTask->selectedRaceId == raceId;
});
- it != taskHunting.end()) {
+ it != taskHunting.end()) {
return *it;
}
@@ -2550,8 +2562,7 @@ class Player final : public Creature, public Cylinder, public Bankable {
}
bool checkAutoLoot(bool isBoss) const {
- const bool autoLoot = g_configManager().getBoolean(AUTOLOOT, __FUNCTION__);
- if (!autoLoot) {
+ if (!g_configManager().getBoolean(AUTOLOOT, __FUNCTION__)) {
return false;
}
if (g_configManager().getBoolean(VIP_SYSTEM_ENABLED, __FUNCTION__) && g_configManager().getBoolean(VIP_AUTOLOOT_VIP_ONLY, __FUNCTION__) && !isVip()) {
@@ -2559,18 +2570,13 @@ class Player final : public Creature, public Cylinder, public Bankable {
}
auto featureKV = kv()->scoped("features")->get("autoloot");
- if (featureKV.has_value()) {
- auto value = featureKV->getNumber();
- if (value == 2) {
- return true;
- } else if (value == 1) {
- return !isBoss;
- } else if (value == 0) {
- return false;
- }
+ auto value = featureKV.has_value() ? featureKV->getNumber() : 0;
+ if (value == 2) {
+ return true;
+ } else if (value == 1) {
+ return !isBoss;
}
-
- return true;
+ return false;
}
QuickLootFilter_t getQuickLootFilter() const {
@@ -2854,7 +2860,7 @@ class Player final : public Creature, public Cylinder, public Bankable {
uint16_t lastStatsTrainingTime = 0;
uint16_t staminaMinutes = 2520;
- std::vector blessings = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ std::vector blessings = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
uint16_t maxWriteLen = 0;
uint16_t baseXpGain = 100;
uint16_t voucherXpBoost = 0;
diff --git a/src/creatures/players/vip/player_vip.cpp b/src/creatures/players/vip/player_vip.cpp
index b4b1642ec69..95ebe91ad5f 100644
--- a/src/creatures/players/vip/player_vip.cpp
+++ b/src/creatures/players/vip/player_vip.cpp
@@ -143,13 +143,13 @@ std::shared_ptr PlayerVIP::getGroupByName(const std::string &name) con
void PlayerVIP::addGroupInternal(uint8_t groupId, const std::string &name, bool customizable) {
if (getGroupByName(name) != nullptr) {
- g_logger().warn("{} - Group name already exists.", __FUNCTION__);
+ g_logger().debug("{} - Group name already exists.", __FUNCTION__);
return;
}
const auto freeId = getFreeId();
if (freeId == 0) {
- g_logger().warn("{} - No id available.", __FUNCTION__);
+ g_logger().debug("{} - No id available.", __FUNCTION__);
return;
}
diff --git a/src/creatures/players/vocations/vocation.cpp b/src/creatures/players/vocations/vocation.cpp
index 98dbaeb1bea..6fec2725164 100644
--- a/src/creatures/players/vocations/vocation.cpp
+++ b/src/creatures/players/vocations/vocation.cpp
@@ -129,13 +129,13 @@ bool Vocations::loadFromXml() {
voc->skillMultipliers[skill_id] = pugi::cast(childNode.attribute("multiplier").value());
} else {
g_logger().warn("[Vocations::loadFromXml] - "
- "No valid skill id: {} for vocation: {}",
- skill_id, voc->id);
+ "No valid skill id: {} for vocation: {}",
+ skill_id, voc->id);
}
} else {
g_logger().warn("[Vocations::loadFromXml] - "
- "Missing skill id for vocation: {}",
- voc->id);
+ "Missing skill id for vocation: {}",
+ voc->id);
}
} else if (strcasecmp(childNode.name(), "mitigation") == 0) {
pugi::xml_attribute factorAttribute = childNode.attribute("multiplier");
@@ -198,8 +198,8 @@ std::shared_ptr Vocations::getVocation(uint16_t id) {
auto it = vocationsMap.find(id);
if (it == vocationsMap.end()) {
g_logger().warn("[Vocations::getVocation] - "
- "Vocation {} not found",
- id);
+ "Vocation {} not found",
+ id);
return nullptr;
}
return it->second;
diff --git a/src/database/databasemanager.cpp b/src/database/databasemanager.cpp
index cbcc116dd9a..dfbb7d9a64a 100644
--- a/src/database/databasemanager.cpp
+++ b/src/database/databasemanager.cpp
@@ -89,8 +89,8 @@ void DatabaseManager::updateDatabase() {
ss << g_configManager().getString(DATA_DIRECTORY, __FUNCTION__) + "/migrations/" << version << ".lua";
if (luaL_dofile(L, ss.str().c_str()) != 0) {
g_logger().error("DatabaseManager::updateDatabase - Version: {}"
- "] {}",
- version, lua_tostring(L, -1));
+ "] {}",
+ version, lua_tostring(L, -1));
break;
}
diff --git a/src/database/databasetasks.cpp b/src/database/databasetasks.cpp
index 6d43992ac81..06cfda93fc0 100644
--- a/src/database/databasetasks.cpp
+++ b/src/database/databasetasks.cpp
@@ -23,7 +23,7 @@ DatabaseTasks &DatabaseTasks::getInstance() {
}
void DatabaseTasks::execute(const std::string &query, std::function callback /* nullptr */) {
- threadPool.addLoad([this, query, callback]() {
+ threadPool.detach_task([this, query, callback]() {
bool success = db.executeQuery(query);
if (callback != nullptr) {
g_dispatcher().addEvent([callback, success]() { callback(nullptr, success); }, "DatabaseTasks::execute");
@@ -32,7 +32,7 @@ void DatabaseTasks::execute(const std::string &query, std::function callback /* nullptr */) {
- threadPool.addLoad([this, query, callback]() {
+ threadPool.detach_task([this, query, callback]() {
DBResult_ptr result = db.storeQuery(query);
if (callback != nullptr) {
g_dispatcher().addEvent([callback, result]() { callback(result, true); }, "DatabaseTasks::store");
diff --git a/src/enums/player_cyclopedia.hpp b/src/enums/player_cyclopedia.hpp
index f0637011a19..c6e1b7032c0 100644
--- a/src/enums/player_cyclopedia.hpp
+++ b/src/enums/player_cyclopedia.hpp
@@ -36,3 +36,17 @@ enum CyclopediaTitle_t : uint8_t {
MAP,
OTHERS,
};
+
+enum class CyclopediaMapData_t : uint8_t {
+ MinimapMarker = 0,
+ DiscoveryData = 1,
+ ActiveRaid = 2,
+ ImminentRaidMainArea = 3,
+ ImminentRaidSubArea = 4,
+ SetDiscoveryArea = 5,
+ Passage = 6,
+ SubAreaMonsters = 7,
+ MonsterBestiary = 8,
+ Donations = 9,
+ SetCurrentArea = 10,
+};
diff --git a/src/game/functions/game_reload.cpp b/src/game/functions/game_reload.cpp
index 0fc74bcee71..d9d9eb1f449 100644
--- a/src/game/functions/game_reload.cpp
+++ b/src/game/functions/game_reload.cpp
@@ -22,7 +22,7 @@
GameReload::GameReload() = default;
GameReload::~GameReload() = default;
-bool GameReload::init(Reload_t reloadTypes) const {
+bool GameReload::init(Reload_t reloadTypes) {
switch (reloadTypes) {
case Reload_t::RELOAD_TYPE_ALL:
return reloadAll();
@@ -32,34 +32,38 @@ bool GameReload::init(Reload_t reloadTypes) const {
return reloadConfig();
case Reload_t::RELOAD_TYPE_EVENTS:
return reloadEvents();
- case Reload_t::RELOAD_TYPE_CORE:
- return reloadCore();
+ case Reload_t::RELOAD_TYPE_MODULES:
+ return reloadModules();
+ case Reload_t::RELOAD_TYPE_OUTFITS:
+ return reloadOutfits();
+ case Reload_t::RELOAD_TYPE_MOUNTS:
+ return reloadMounts();
+ case Reload_t::RELOAD_TYPE_FAMILIARS:
+ return reloadFamiliars();
case Reload_t::RELOAD_TYPE_IMBUEMENTS:
return reloadImbuements();
+ case Reload_t::RELOAD_TYPE_VOCATIONS:
+ return reloadVocations();
+ case Reload_t::RELOAD_TYPE_CORE:
+ return reloadCore();
+ case Reload_t::RELOAD_TYPE_GROUPS:
+ return reloadGroups();
+ case Reload_t::RELOAD_TYPE_SCRIPTS:
+ return reloadScripts();
case Reload_t::RELOAD_TYPE_ITEMS:
return reloadItems();
- case Reload_t::RELOAD_TYPE_MODULES:
- return reloadModules();
case Reload_t::RELOAD_TYPE_MONSTERS:
return reloadMonsters();
- case Reload_t::RELOAD_TYPE_MOUNTS:
- return reloadMounts();
case Reload_t::RELOAD_TYPE_NPCS:
return reloadNpcs();
case Reload_t::RELOAD_TYPE_RAIDS:
return reloadRaids();
- case Reload_t::RELOAD_TYPE_SCRIPTS:
- return reloadScripts();
- case Reload_t::RELOAD_TYPE_GROUPS:
- return reloadGroups();
- case Reload_t::RELOAD_TYPE_VOCATIONS:
- return reloadVocations();
default:
return false;
}
}
-uint8_t GameReload::getReloadNumber(Reload_t reloadTypes) const {
+uint8_t GameReload::getReloadNumber(Reload_t reloadTypes) {
return magic_enum::enum_integer(reloadTypes);
}
@@ -78,7 +82,7 @@ void logReloadStatus(const std::string &name, bool result) {
* If it is necessary to call elsewhere, seriously think about creating a function that calls this
* Changing this to public may cause some unexpected behavior or bug
*/
-bool GameReload::reloadAll() const {
+bool GameReload::reloadAll() {
std::vector reloadResults;
reloadResults.reserve(magic_enum::enum_count());
@@ -93,25 +97,62 @@ bool GameReload::reloadAll() const {
return std::ranges::any_of(reloadResults, [](bool result) { return result; });
}
-bool GameReload::reloadChat() const {
+bool GameReload::reloadChat() {
const bool result = g_chat().load();
logReloadStatus("Chat", result);
return result;
}
-bool GameReload::reloadConfig() const {
+bool GameReload::reloadConfig() {
const bool result = g_configManager().reload();
logReloadStatus("Config", result);
return result;
}
-bool GameReload::reloadEvents() const {
+bool GameReload::reloadEvents() {
const bool result = g_events().loadFromXml();
logReloadStatus("Events", result);
return result;
}
-bool GameReload::reloadCore() const {
+bool GameReload::reloadModules() {
+ const bool result = g_modules().reload();
+ logReloadStatus("Modules", result);
+ return result;
+}
+
+bool GameReload::reloadOutfits() {
+ const bool result = g_game().outfits.reload();
+ logReloadStatus("Outfits", result);
+ return result;
+}
+
+bool GameReload::reloadMounts() {
+ const bool result = g_game().mounts.reload();
+ logReloadStatus("Mounts", result);
+ return result;
+}
+
+bool GameReload::reloadFamiliars() {
+ const bool result = g_game().familiars.reload();
+ logReloadStatus("Familiars", result);
+ return result;
+}
+
+bool GameReload::reloadImbuements() {
+ const bool result = g_imbuements().reload();
+ logReloadStatus("Imbuements", result);
+ return result;
+}
+
+bool GameReload::reloadVocations() {
+ const bool result = g_vocations().reload();
+ reloadScripts();
+ logReloadStatus("Vocations", result);
+ return result;
+}
+
+bool GameReload::reloadCore() {
const auto &coreFolder = g_configManager().getString(CORE_DIRECTORY, __FUNCTION__);
const bool coreLoaded = g_luaEnvironment().loadFile(coreFolder + "/core.lua", "core.lua") == 0;
@@ -126,25 +167,38 @@ bool GameReload::reloadCore() const {
return false;
}
-bool GameReload::reloadImbuements() const {
- const bool result = g_imbuements().reload();
- logReloadStatus("Imbuements", result);
+bool GameReload::reloadGroups() {
+ const bool result = g_game().groups.reload();
+ logReloadStatus("Groups", result);
return result;
}
-bool GameReload::reloadItems() const {
- const bool result = Item::items.reload();
- logReloadStatus("Items", result);
- return result;
+bool GameReload::reloadScripts() {
+ g_scripts().clearAllScripts();
+ Zone::clearZones();
+
+ const auto &datapackFolder = g_configManager().getString(DATA_DIRECTORY, __FUNCTION__);
+ const auto &coreFolder = g_configManager().getString(CORE_DIRECTORY, __FUNCTION__);
+
+ g_scripts().loadScripts(coreFolder + "/scripts/lib", true, false);
+ g_scripts().loadScripts(datapackFolder + "/scripts", false, true);
+ g_scripts().loadScripts(coreFolder + "/scripts", false, true);
+
+ // It should come last, after everything else has been cleaned up.
+ reloadMonsters();
+ reloadNpcs();
+ reloadItems();
+ logReloadStatus("Scripts", true);
+ return true;
}
-bool GameReload::reloadModules() const {
- const bool result = g_modules().reload();
- logReloadStatus("Modules", result);
+bool GameReload::reloadItems() {
+ const bool result = Item::items.reload();
+ logReloadStatus("Items", result);
return result;
}
-bool GameReload::reloadMonsters() const {
+bool GameReload::reloadMonsters() {
g_monsters().clear();
const auto &datapackFolder = g_configManager().getString(DATA_DIRECTORY, __FUNCTION__);
const auto &coreFolder = g_configManager().getString(CORE_DIRECTORY, __FUNCTION__);
@@ -161,50 +215,14 @@ bool GameReload::reloadMonsters() const {
}
}
-bool GameReload::reloadMounts() const {
- const bool result = g_game().mounts.reload();
- logReloadStatus("Mounts", result);
- return result;
-}
-
-bool GameReload::reloadNpcs() const {
+bool GameReload::reloadNpcs() {
const bool result = g_npcs().reload();
logReloadStatus("NPCs", result);
return result;
}
-bool GameReload::reloadRaids() const {
+bool GameReload::reloadRaids() {
const bool result = g_game().raids.reload() && g_game().raids.startup();
logReloadStatus("Raids", result);
return result;
}
-
-bool GameReload::reloadScripts() const {
- g_scripts().clearAllScripts();
- Zone::clearZones();
-
- const auto &datapackFolder = g_configManager().getString(DATA_DIRECTORY, __FUNCTION__);
- const auto &coreFolder = g_configManager().getString(CORE_DIRECTORY, __FUNCTION__);
-
- g_scripts().loadScripts(coreFolder + "/scripts/lib", true, false);
- g_scripts().loadScripts(datapackFolder + "/scripts", false, true);
- g_scripts().loadScripts(coreFolder + "/scripts", false, true);
-
- // It should come last, after everything else has been cleaned up.
- reloadMonsters();
- reloadNpcs();
- logReloadStatus("Scripts", true);
- return true;
-}
-
-bool GameReload::reloadGroups() const {
- const bool result = g_game().groups.reload();
- logReloadStatus("Groups", result);
- return result;
-}
-
-bool GameReload::reloadVocations() const {
- const bool result = g_vocations().reload();
- logReloadStatus("Vocations", result);
- return result;
-}
diff --git a/src/game/functions/game_reload.hpp b/src/game/functions/game_reload.hpp
index 45ff35c01a2..0f59046a9d4 100644
--- a/src/game/functions/game_reload.hpp
+++ b/src/game/functions/game_reload.hpp
@@ -19,17 +19,19 @@ enum class Reload_t : uint8_t {
RELOAD_TYPE_CHAT,
RELOAD_TYPE_CONFIG,
RELOAD_TYPE_EVENTS,
- RELOAD_TYPE_CORE,
+ RELOAD_TYPE_MODULES,
+ RELOAD_TYPE_OUTFITS,
+ RELOAD_TYPE_MOUNTS,
+ RELOAD_TYPE_FAMILIARS,
RELOAD_TYPE_IMBUEMENTS,
+ RELOAD_TYPE_VOCATIONS,
+ RELOAD_TYPE_CORE,
+ RELOAD_TYPE_GROUPS,
+ RELOAD_TYPE_SCRIPTS,
RELOAD_TYPE_ITEMS,
- RELOAD_TYPE_MODULES,
RELOAD_TYPE_MONSTERS,
- RELOAD_TYPE_MOUNTS,
RELOAD_TYPE_NPCS,
RELOAD_TYPE_RAIDS,
- RELOAD_TYPE_SCRIPTS,
- RELOAD_TYPE_GROUPS,
- RELOAD_TYPE_VOCATIONS,
// Every is last
RELOAD_TYPE_LAST
@@ -48,25 +50,27 @@ class GameReload : public Game {
return inject();
}
- bool init(Reload_t reloadType) const;
- uint8_t getReloadNumber(Reload_t reloadTypes) const;
+ static bool init(Reload_t reloadType);
+ static uint8_t getReloadNumber(Reload_t reloadTypes);
private:
- bool reloadAll() const;
- bool reloadChat() const;
- bool reloadConfig() const;
- bool reloadEvents() const;
- bool reloadCore() const;
- bool reloadImbuements() const;
- bool reloadItems() const;
- bool reloadModules() const;
- bool reloadMonsters() const;
- bool reloadMounts() const;
- bool reloadNpcs() const;
- bool reloadRaids() const;
- bool reloadScripts() const;
- bool reloadGroups() const;
- bool reloadVocations() const;
+ static bool reloadAll();
+ static bool reloadChat();
+ static bool reloadConfig();
+ static bool reloadEvents();
+ static bool reloadModules();
+ static bool reloadOutfits();
+ static bool reloadMounts();
+ static bool reloadFamiliars();
+ static bool reloadImbuements();
+ static bool reloadVocations();
+ static bool reloadCore();
+ static bool reloadGroups();
+ static bool reloadScripts();
+ static bool reloadItems();
+ static bool reloadMonsters();
+ static bool reloadNpcs();
+ static bool reloadRaids();
};
constexpr auto g_gameReload = GameReload::getInstance;
diff --git a/src/game/game.cpp b/src/game/game.cpp
index 621e3593816..bae7975a295 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -126,6 +126,10 @@ namespace InternalGame {
if (isGuest && !isItemInGuestInventory && !item->isLadder() && !item->canBeUsedByGuests()) {
return false;
}
+
+ if (isGuest && item->isDummy()) {
+ return false;
+ }
}
return true;
@@ -382,7 +386,7 @@ void Game::loadBoostedCreature() {
const auto result = db.storeQuery("SELECT * FROM `boosted_creature`");
if (!result) {
g_logger().warn("[Game::loadBoostedCreature] - "
- "Failed to detect boosted creature database. (CODE 01)");
+ "Failed to detect boosted creature database. (CODE 01)");
return;
}
@@ -419,15 +423,15 @@ void Game::loadBoostedCreature() {
if (selectedMonster.raceId == 0) {
g_logger().warn("[Game::loadBoostedCreature] - "
- "It was not possible to generate a new boosted creature->");
+ "It was not possible to generate a new boosted creature->");
return;
}
const auto monsterType = g_monsters().getMonsterType(selectedMonster.name);
if (!monsterType) {
g_logger().warn("[Game::loadBoostedCreature] - "
- "It was not possible to generate a new boosted creature-> Monster '{}' not found.",
- selectedMonster.name);
+ "It was not possible to generate a new boosted creature-> Monster '{}' not found.",
+ selectedMonster.name);
return;
}
@@ -447,7 +451,7 @@ void Game::loadBoostedCreature() {
if (!db.executeQuery(query)) {
g_logger().warn("[Game::loadBoostedCreature] - "
- "Failed to detect boosted creature database. (CODE 02)");
+ "Failed to detect boosted creature database. (CODE 02)");
}
}
@@ -1699,7 +1703,7 @@ void Game::playerMoveItem(std::shared_ptr player, const Position &fromPo
uint8_t itemStackPos = fromStackPos;
if (fromPos.x != 0xFFFF && Position::areInRange<1, 1>(mapFromPos, playerPos)
- && !Position::areInRange<1, 1, 0>(mapFromPos, walkPos)) {
+ && !Position::areInRange<1, 1, 0>(mapFromPos, walkPos)) {
// need to pickup the item first
std::shared_ptr
- moveItem = nullptr;
@@ -2089,20 +2093,20 @@ ReturnValue Game::internalMoveItem(std::shared_ptr fromCylinder, std::
std::shared_ptr
- quiver = toCylinder->getItem();
if (quiver && quiver->isQuiver()
- && quiver->getHoldingPlayer()
- && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) {
+ && quiver->getHoldingPlayer()
+ && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) {
quiver->getHoldingPlayer()->sendInventoryItem(CONST_SLOT_RIGHT, quiver);
} else {
quiver = fromCylinder->getItem();
if (quiver && quiver->isQuiver()
- && quiver->getHoldingPlayer()
- && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) {
+ && quiver->getHoldingPlayer()
+ && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) {
quiver->getHoldingPlayer()->sendInventoryItem(CONST_SLOT_RIGHT, quiver);
}
}
if (SoundEffect_t soundEffect = item->getMovementSound(toCylinder);
- toCylinder && soundEffect != SoundEffect_t::SILENCE) {
+ toCylinder && soundEffect != SoundEffect_t::SILENCE) {
if (toCylinder->getContainer() && actor && actor->getPlayer() && (toCylinder->getContainer()->isInsideDepot(true) || toCylinder->getContainer()->getHoldingPlayer())) {
actor->getPlayer()->sendSingleSoundEffect(toCylinder->getPosition(), soundEffect, SourceEffect_t::OWN);
} else {
@@ -2235,8 +2239,8 @@ ReturnValue Game::internalAddItem(std::shared_ptr toCylinder, std::sha
}
if (addedItem && addedItem->isQuiver()
- && addedItem->getHoldingPlayer()
- && addedItem->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == addedItem) {
+ && addedItem->getHoldingPlayer()
+ && addedItem->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == addedItem) {
addedItem->getHoldingPlayer()->sendInventoryItem(CONST_SLOT_RIGHT, addedItem);
}
@@ -2296,8 +2300,8 @@ ReturnValue Game::internalRemoveItem(std::shared_ptr
- item, int32_t count /
std::shared_ptr
- quiver = cylinder->getItem();
if (quiver && quiver->isQuiver()
- && quiver->getHoldingPlayer()
- && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) {
+ && quiver->getHoldingPlayer()
+ && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) {
quiver->getHoldingPlayer()->sendInventoryItem(CONST_SLOT_RIGHT, quiver);
}
@@ -2756,8 +2760,8 @@ std::shared_ptr
- Game::transformItem(std::shared_ptr
- item, uint16_t n
std::shared_ptr
- quiver = cylinder->getItem();
if (quiver && quiver->isQuiver()
- && quiver->getHoldingPlayer()
- && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) {
+ && quiver->getHoldingPlayer()
+ && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) {
quiver->getHoldingPlayer()->sendInventoryItem(CONST_SLOT_RIGHT, quiver);
}
item->startDecaying();
@@ -2768,8 +2772,8 @@ std::shared_ptr
- Game::transformItem(std::shared_ptr
- item, uint16_t n
std::shared_ptr
- quiver = cylinder->getItem();
if (quiver && quiver->isQuiver()
- && quiver->getHoldingPlayer()
- && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) {
+ && quiver->getHoldingPlayer()
+ && quiver->getHoldingPlayer()->getThing(CONST_SLOT_RIGHT) == quiver) {
quiver->getHoldingPlayer()->sendInventoryItem(CONST_SLOT_RIGHT, quiver);
}
@@ -3695,7 +3699,7 @@ void Game::playerUseItemEx(uint32_t playerId, const Position &fromPos, uint8_t f
mustReloadDepotSearch = true;
} else {
if (auto targetThing = internalGetThing(player, toPos, toStackPos, toItemId, STACKPOS_FIND_THING);
- targetThing && targetThing->getItem() && targetThing->getItem()->isInsideDepot(true)) {
+ targetThing && targetThing->getItem() && targetThing->getItem()->isInsideDepot(true)) {
mustReloadDepotSearch = true;
}
}
@@ -4231,7 +4235,7 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos
item->setCustomAttribute("LookFeet", static_cast(outfit.lookFeet));
item->setCustomAttribute("LookAddons", static_cast(outfit.lookAddons));
} else if (auto pastLookType = item->getCustomAttribute("PastLookType");
- pastLookType && pastLookType->getInteger() > 0) {
+ pastLookType && pastLookType->getInteger() > 0) {
item->removeCustomAttribute("LookType");
item->removeCustomAttribute("PastLookType");
}
@@ -4243,7 +4247,7 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos
item->setCustomAttribute("LookMountLegs", static_cast(outfit.lookMountLegs));
item->setCustomAttribute("LookMountFeet", static_cast(outfit.lookMountFeet));
} else if (auto pastLookMount = item->getCustomAttribute("PastLookMount");
- pastLookMount && pastLookMount->getInteger() > 0) {
+ pastLookMount && pastLookMount->getInteger() > 0) {
item->removeCustomAttribute("LookMount");
item->removeCustomAttribute("PastLookMount");
}
@@ -4257,7 +4261,7 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos
name << item->getName() << " displaying the ";
bool outfited = false;
if (outfit.lookType != 0) {
- const auto &outfitInfo = Outfits::getInstance().getOutfitByLookType(player->getSex(), outfit.lookType);
+ const auto &outfitInfo = Outfits::getInstance().getOutfitByLookType(player, outfit.lookType);
if (!outfitInfo) {
return;
}
@@ -5165,7 +5169,7 @@ void Game::playerBuyItem(uint32_t playerId, uint16_t itemId, uint8_t count, uint
return;
}
- if (inBackpacks) {
+ if (inBackpacks || it.isContainer()) {
uint32_t maxContainer = static_cast(g_configManager().getNumber(MAX_CONTAINER, __FUNCTION__));
auto backpack = player->getInventoryItem(CONST_SLOT_BACKPACK);
auto mainBackpack = backpack ? backpack->getContainer() : nullptr;
@@ -5492,8 +5496,8 @@ void Game::playerLootAllCorpses(std::shared_ptr player, const Position &
}
if (!tileCorpse->isRewardCorpse()
- && tileCorpse->getCorpseOwner() != 0
- && !player->canOpenCorpse(tileCorpse->getCorpseOwner())) {
+ && tileCorpse->getCorpseOwner() != 0
+ && !player->canOpenCorpse(tileCorpse->getCorpseOwner())) {
player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
g_logger().debug("Player {} cannot loot corpse from id {} in position {}", player->getName(), tileItem->getID(), tileItem->getPosition().toString());
continue;
@@ -5923,7 +5927,7 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit, uint8_t isMoun
outfit.lookMount = randomMount->clientId;
}
- const auto playerOutfit = Outfits::getInstance().getOutfitByLookType(player->getSex(), outfit.lookType);
+ const auto playerOutfit = Outfits::getInstance().getOutfitByLookType(player, outfit.lookType);
if (!playerOutfit) {
outfit.lookMount = 0;
}
@@ -6251,7 +6255,6 @@ void Game::checkCreatureWalk(uint32_t creatureId) {
const auto &creature = getCreatureByID(creatureId);
if (creature && creature->getHealth() > 0) {
creature->onCreatureWalk();
- cleanup();
}
}
@@ -6314,7 +6317,6 @@ void Game::checkCreatures() {
--end;
}
}
- cleanup();
index = (index + 1) % EVENT_CREATURECOUNT;
}
@@ -7118,9 +7120,9 @@ bool Game::combatChangeHealth(std::shared_ptr attacker, std::shared_pt
if (!damage.extension && attackerMonster && targetPlayer) {
// Charm rune (target as player)
if (charmRune_t activeCharm = g_iobestiary().getCharmFromTarget(targetPlayer, g_monsters().getMonsterTypeByRaceId(attackerMonster->getRaceId()));
- activeCharm != CHARM_NONE && activeCharm != CHARM_CLEANSE) {
+ activeCharm != CHARM_NONE && activeCharm != CHARM_CLEANSE) {
if (const auto charm = g_iobestiary().getBestiaryCharm(activeCharm);
- charm->type == CHARM_DEFENSIVE && charm->chance > normal_random(0, 100) && g_iobestiary().parseCharmCombat(charm, targetPlayer, attacker, (damage.primary.value + damage.secondary.value))) {
+ charm->type == CHARM_DEFENSIVE && charm->chance > normal_random(0, 100) && g_iobestiary().parseCharmCombat(charm, targetPlayer, attacker, (damage.primary.value + damage.secondary.value))) {
return false; // Dodge charm
}
}
@@ -7506,7 +7508,7 @@ void Game::applyCharmRune(
return;
}
if (charmRune_t activeCharm = g_iobestiary().getCharmFromTarget(attackerPlayer, g_monsters().getMonsterTypeByRaceId(targetMonster->getRaceId()));
- activeCharm != CHARM_NONE) {
+ activeCharm != CHARM_NONE) {
const auto charm = g_iobestiary().getBestiaryCharm(activeCharm);
int8_t chance = charm->id == CHARM_CRIPPLE ? charm->chance : charm->chance + attackerPlayer->getCharmChanceModifier();
g_logger().debug("charm chance: {}, base: {}, bonus: {}", chance, charm->chance, attackerPlayer->getCharmChanceModifier());
@@ -7532,7 +7534,7 @@ void Game::applyManaLeech(
// Void charm rune
if (targetMonster) {
if (uint16_t playerCharmRaceidVoid = attackerPlayer->parseRacebyCharm(CHARM_VOID, false, 0);
- playerCharmRaceidVoid != 0 && playerCharmRaceidVoid == targetMonster->getRace()) {
+ playerCharmRaceidVoid != 0 && playerCharmRaceidVoid == targetMonster->getRace()) {
if (const auto charm = g_iobestiary().getBestiaryCharm(CHARM_VOID)) {
manaSkill += charm->percent;
}
@@ -7563,7 +7565,7 @@ void Game::applyLifeLeech(
}
if (targetMonster) {
if (uint16_t playerCharmRaceidVamp = attackerPlayer->parseRacebyCharm(CHARM_VAMP, false, 0);
- playerCharmRaceidVamp != 0 && playerCharmRaceidVamp == targetMonster->getRaceId()) {
+ playerCharmRaceidVamp != 0 && playerCharmRaceidVamp == targetMonster->getRaceId()) {
if (const auto lifec = g_iobestiary().getBestiaryCharm(CHARM_VAMP)) {
lifeSkill += lifec->percent;
}
@@ -7963,8 +7965,6 @@ void Game::shutdown() {
map.spawnsNpc.clear();
raids.clear();
- cleanup();
-
if (serviceManager) {
serviceManager->stop();
}
@@ -7976,16 +7976,6 @@ void Game::shutdown() {
g_logger().info("Done!");
}
-void Game::cleanup() {
- for (auto it = browseFields.begin(); it != browseFields.end();) {
- if (it->second.expired()) {
- it = browseFields.erase(it);
- } else {
- ++it;
- }
- }
-}
-
void Game::addBestiaryList(uint16_t raceid, std::string name) {
auto it = BestiaryList.find(raceid);
if (it != BestiaryList.end()) {
@@ -9533,7 +9523,7 @@ void Game::playerBosstiarySlot(uint32_t playerId, uint8_t slotId, uint32_t selec
uint32_t bossIdSlot = player->getSlotBossId(slotId);
if (uint32_t boostedBossId = g_ioBosstiary().getBoostedBossId();
- selectedBossId == 0 && bossIdSlot != boostedBossId) {
+ selectedBossId == 0 && bossIdSlot != boostedBossId) {
uint8_t removeTimes = player->getRemoveTimes();
uint32_t removePrice = g_ioBosstiary().calculteRemoveBoss(removeTimes);
g_game().removeMoney(player, removePrice, 0, true);
@@ -9569,7 +9559,7 @@ void Game::playerSetMonsterPodium(uint32_t playerId, uint32_t monsterRaceId, con
if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) {
if (stdext::arraylist listDir(128);
- player->getPathTo(pos, listDir, 0, 1, true, false)) {
+ player->getPathTo(pos, listDir, 0, 1, true, false)) {
g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk");
std::shared_ptr task = createPlayerTask(
400, [this, playerId, pos] { playerBrowseField(playerId, pos); }, "Game::playerBrowseField"
@@ -9607,7 +9597,7 @@ void Game::playerSetMonsterPodium(uint32_t playerId, uint32_t monsterRaceId, con
const auto [podiumVisible, monsterVisible] = podiumAndMonsterVisible;
bool changeTentuglyName = false;
if (auto monsterOutfit = mType->info.outfit;
- (monsterOutfit.lookType != 0 || monsterOutfit.lookTypeEx != 0) && monsterVisible) {
+ (monsterOutfit.lookType != 0 || monsterOutfit.lookTypeEx != 0) && monsterVisible) {
// "Tantugly's Head" boss have to send other looktype to the podium
if (monsterOutfit.lookTypeEx == 35105) {
monsterOutfit.lookTypeEx = 39003;
@@ -9669,7 +9659,7 @@ void Game::playerRotatePodium(uint32_t playerId, const Position &pos, uint8_t st
if (pos.x != 0xFFFF && !Position::areInRange<1, 1, 0>(pos, player->getPosition())) {
if (stdext::arraylist listDir(128);
- player->getPathTo(pos, listDir, 0, 1, true, true)) {
+ player->getPathTo(pos, listDir, 0, 1, true, true)) {
g_dispatcher().addEvent([this, playerId = player->getID(), listDir = listDir.data()] { playerAutoWalk(playerId, listDir); }, "Game::playerAutoWalk");
std::shared_ptr task = createPlayerTask(
400, [this, playerId, pos, stackPos, itemId] {
@@ -10041,8 +10031,8 @@ void Game::sendUpdateCreature(std::shared_ptr creature) {
uint32_t Game::makeInfluencedMonster() {
if (auto influencedLimit = g_configManager().getNumber(FORGE_INFLUENCED_CREATURES_LIMIT, __FUNCTION__);
- // Condition
- forgeableMonsters.empty() || influencedMonsters.size() >= influencedLimit) {
+ // Condition
+ forgeableMonsters.empty() || influencedMonsters.size() >= influencedLimit) {
return 0;
}
@@ -10122,8 +10112,8 @@ uint32_t Game::makeFiendishMonster(uint32_t forgeableMonsterId /* = 0*/, bool cr
}
if (auto fiendishLimit = g_configManager().getNumber(FORGE_FIENDISH_CREATURES_LIMIT, __FUNCTION__);
- // Condition
- forgeableMonsters.empty() || fiendishMonsters.size() >= fiendishLimit) {
+ // Condition
+ forgeableMonsters.empty() || fiendishMonsters.size() >= fiendishLimit) {
return 0;
}
@@ -10227,8 +10217,8 @@ bool Game::removeForgeMonster(uint32_t id, ForgeClassifications_t monsterForgeCl
bool Game::removeInfluencedMonster(uint32_t id, bool create /* = false*/) {
if (auto find = influencedMonsters.find(id);
- // Condition
- find != influencedMonsters.end()) {
+ // Condition
+ find != influencedMonsters.end()) {
influencedMonsters.erase(find);
if (create) {
@@ -10244,8 +10234,8 @@ bool Game::removeInfluencedMonster(uint32_t id, bool create /* = false*/) {
bool Game::removeFiendishMonster(uint32_t id, bool create /* = true*/) {
if (auto find = fiendishMonsters.find(id);
- // Condition
- find != fiendishMonsters.end()) {
+ // Condition
+ find != fiendishMonsters.end()) {
fiendishMonsters.erase(find);
checkForgeEventId(id);
@@ -10296,8 +10286,8 @@ void Game::createFiendishMonsters() {
}
if (auto ret = makeFiendishMonster();
- // Condition
- ret == 0) {
+ // Condition
+ ret == 0) {
return;
}
@@ -10315,8 +10305,8 @@ void Game::createInfluencedMonsters() {
}
if (auto ret = makeInfluencedMonster();
- // If condition
- ret == 0) {
+ // If condition
+ ret == 0) {
return;
}
@@ -10335,8 +10325,8 @@ void Game::checkForgeEventId(uint32_t monsterId) {
bool Game::addInfluencedMonster(std::shared_ptr monster) {
if (monster && monster->canBeForgeMonster()) {
if (auto maxInfluencedMonsters = static_cast(g_configManager().getNumber(FORGE_INFLUENCED_CREATURES_LIMIT, __FUNCTION__));
- // If condition
- (influencedMonsters.size() + 1) > maxInfluencedMonsters) {
+ // If condition
+ (influencedMonsters.size() + 1) > maxInfluencedMonsters) {
return false;
}
diff --git a/src/game/game.hpp b/src/game/game.hpp
index 3a199254259..2de00410e5e 100644
--- a/src/game/game.hpp
+++ b/src/game/game.hpp
@@ -426,7 +426,6 @@ class Game {
void updatePlayerHelpers(std::shared_ptr player);
- void cleanup();
void shutdown();
void dieSafely(const std::string &errorMsg);
void addBestiaryList(uint16_t raceid, std::string name);
@@ -582,8 +581,10 @@ class Game {
bool hasDistanceEffect(uint16_t effectId);
Groups groups;
+ Familiars familiars;
Map map;
Mounts mounts;
+ Outfits outfits;
Raids raids;
std::unique_ptr m_appearancesPtr;
diff --git a/src/game/movement/teleport.cpp b/src/game/movement/teleport.cpp
index d5a3a73daf1..44050100eb6 100644
--- a/src/game/movement/teleport.cpp
+++ b/src/game/movement/teleport.cpp
@@ -80,8 +80,8 @@ void Teleport::addThing(int32_t, std::shared_ptr thing) {
if (checkInfinityLoop(destTile)) {
const Position &pos = getPosition();
g_logger().warn("[Teleport:addThing] - "
- "Infinity loop teleport at position: {}",
- pos.toString());
+ "Infinity loop teleport at position: {}",
+ pos.toString());
return;
}
diff --git a/src/game/scheduling/dispatcher.cpp b/src/game/scheduling/dispatcher.cpp
index ec999848a92..dbbfc020be5 100644
--- a/src/game/scheduling/dispatcher.cpp
+++ b/src/game/scheduling/dispatcher.cpp
@@ -23,10 +23,10 @@ Dispatcher &Dispatcher::getInstance() {
void Dispatcher::init() {
UPDATE_OTSYS_TIME();
- threadPool.addLoad([this] {
+ threadPool.detach_task([this] {
std::unique_lock asyncLock(dummyMutex);
- while (!threadPool.getIoContext().stopped()) {
+ while (!threadPool.isStopped()) {
UPDATE_OTSYS_TIME();
executeEvents();
@@ -56,30 +56,51 @@ void Dispatcher::executeSerialEvents(std::vector &tasks) {
}
void Dispatcher::executeParallelEvents(std::vector &tasks, const uint8_t groupId) {
- std::atomic_uint_fast64_t totalTaskSize = tasks.size();
- std::atomic_bool isTasksCompleted = false;
+ asyncWait(tasks.size(), [groupId, &tasks](size_t i) {
+ dispacherContext.type = DispatcherType::AsyncEvent;
+ dispacherContext.group = static_cast(groupId);
+ tasks[i].execute();
- for (const auto &task : tasks) {
- threadPool.addLoad([groupId, &task, &isTasksCompleted, &totalTaskSize] {
- dispacherContext.type = DispatcherType::AsyncEvent;
- dispacherContext.group = static_cast(groupId);
- dispacherContext.taskName = task.getContext();
+ dispacherContext.reset();
+ });
- task.execute();
+ tasks.clear();
+}
- dispacherContext.reset();
+void Dispatcher::asyncWait(size_t requestSize, std::function &&f) {
+ if (requestSize == 0) {
+ return;
+ }
- totalTaskSize.fetch_sub(1);
- if (totalTaskSize.load() == 0) {
- isTasksCompleted.store(true);
- isTasksCompleted.notify_one();
- }
- });
+ // This prevents an async call from running inside another async call.
+ if (asyncWaitDisabled) {
+ for (uint_fast64_t i = 0; i < requestSize; ++i) {
+ f(i);
+ }
+ return;
}
- isTasksCompleted.wait(false);
+ const auto &partitions = generatePartition(requestSize);
+ const auto pSize = partitions.size();
- tasks.clear();
+ BS::multi_future retFuture;
+
+ if (pSize > 1) {
+ asyncWaitDisabled = true;
+ const auto min = partitions[1].first;
+ const auto max = partitions[partitions.size() - 1].second;
+ retFuture = threadPool.submit_loop(min, max, [&f](const unsigned int i) { f(i); });
+ }
+
+ const auto &[min, max] = partitions[0];
+ for (uint_fast64_t i = min; i < max; ++i) {
+ f(i);
+ }
+
+ if (pSize > 1) {
+ retFuture.wait();
+ asyncWaitDisabled = false;
+ }
}
void Dispatcher::executeEvents(const TaskGroup startGroup) {
diff --git a/src/game/scheduling/dispatcher.hpp b/src/game/scheduling/dispatcher.hpp
index d9e26a0b5d5..94b284c9316 100644
--- a/src/game/scheduling/dispatcher.hpp
+++ b/src/game/scheduling/dispatcher.hpp
@@ -84,7 +84,7 @@ class Dispatcher {
public:
explicit Dispatcher(ThreadPool &threadPool) :
threadPool(threadPool) {
- threads.reserve(threadPool.getNumberOfThreads() + 1);
+ threads.reserve(threadPool.get_thread_count() + 1);
for (uint_fast16_t i = 0; i < threads.capacity(); ++i) {
threads.emplace_back(std::make_unique());
}
@@ -108,6 +108,7 @@ class Dispatcher {
}
void asyncEvent(std::function &&f, TaskGroup group = TaskGroup::GenericParallel);
+ void asyncWait(size_t size, std::function &&f);
uint64_t asyncCycleEvent(uint32_t delay, std::function &&f, TaskGroup group = TaskGroup::GenericParallel) {
return scheduleEvent(
@@ -173,6 +174,22 @@ class Dispatcher {
}
}
+ std::vector> generatePartition(size_t size) const {
+ if (size == 0) {
+ return {};
+ }
+
+ std::vector> list;
+ list.reserve(threadPool.get_thread_count());
+
+ const auto size_per_block = std::ceil(size / static_cast(threadPool.get_thread_count()));
+ for (uint_fast64_t i = 0; i < size; i += size_per_block) {
+ list.emplace_back(i, std::min(size, i + size_per_block));
+ }
+
+ return list;
+ }
+
uint_fast64_t dispatcherCycle = 0;
ThreadPool &threadPool;
@@ -200,6 +217,8 @@ class Dispatcher {
phmap::btree_multiset, Task::Compare> scheduledTasks;
phmap::parallel_flat_hash_map_m> scheduledTasksRef;
+ bool asyncWaitDisabled = false;
+
friend class CanaryServer;
};
diff --git a/src/game/scheduling/save_manager.cpp b/src/game/scheduling/save_manager.cpp
index 2c1eff6657c..fbad528598b 100644
--- a/src/game/scheduling/save_manager.cpp
+++ b/src/game/scheduling/save_manager.cpp
@@ -41,7 +41,7 @@ void SaveManager::scheduleAll() {
return;
}
- threadPool.addLoad([this, scheduledAt]() {
+ threadPool.detach_task([this, scheduledAt]() {
if (m_scheduledAt.load() != scheduledAt) {
logger.warn("Skipping save for server because another save has been scheduled.");
return;
@@ -69,7 +69,7 @@ void SaveManager::schedulePlayer(std::weak_ptr playerPtr) {
logger.debug("Scheduling player {} for saving.", playerToSave->getName());
auto scheduledAt = std::chrono::steady_clock::now();
m_playerMap[playerToSave->getGUID()] = scheduledAt;
- threadPool.addLoad([this, playerPtr, scheduledAt]() {
+ threadPool.detach_task([this, playerPtr, scheduledAt]() {
auto player = playerPtr.lock();
if (!player) {
logger.debug("Skipping save for player because player is no longer online.");
diff --git a/src/game/scheduling/task.hpp b/src/game/scheduling/task.hpp
index c6591887c8a..7bdc7db8084 100644
--- a/src/game/scheduling/task.hpp
+++ b/src/game/scheduling/task.hpp
@@ -70,10 +70,10 @@ class Task {
bool hasTraceableContext() const {
const static auto tasksContext = std::unordered_set({
- "Creature::checkCreatureWalk",
"Decay::checkDecay",
"Dispatcher::asyncEvent",
"Game::checkCreatureAttack",
+ "Game::checkCreatureWalk",
"Game::checkCreatures",
"Game::checkImbuements",
"Game::checkLight",
@@ -94,7 +94,6 @@ class Task {
"SpawnNpc::checkSpawnNpc",
"Webhook::run",
"Protocol::sendRecvMessageCallback",
- "sendRecvMessageCallback",
});
return tasksContext.contains(context);
diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp
index 6273f8fefbb..8f7cf461fdf 100644
--- a/src/io/functions/iologindata_load_player.cpp
+++ b/src/io/functions/iologindata_load_player.cpp
@@ -66,7 +66,7 @@ bool IOLoginDataLoad::preLoadPlayer(std::shared_ptr player, const std::s
}
player->setGUID(result->getNumber("id"));
- std::shared_ptr group = g_game().groups.getGroup(result->getNumber("group_id"));
+ const auto &group = g_game().groups.getGroup(result->getNumber("group_id"));
if (!group) {
g_logger().error("Player {} has group id {} which doesn't exist", player->name, result->getNumber("group_id"));
return false;
@@ -118,7 +118,7 @@ bool IOLoginDataLoad::loadPlayerFirst(std::shared_ptr player, DBResult_p
player->setAccount(result->getNumber("account_id"));
}
- std::shared_ptr group = g_game().groups.getGroup(result->getNumber("group_id"));
+ const auto &group = g_game().groups.getGroup(result->getNumber("group_id"));
if (!group) {
g_logger().error("Player {} has group id {} which doesn't exist", player->name, result->getNumber("group_id"));
return false;
diff --git a/src/io/io_bosstiary.cpp b/src/io/io_bosstiary.cpp
index 0090c0b3ebb..2f625cdb7cc 100644
--- a/src/io/io_bosstiary.cpp
+++ b/src/io/io_bosstiary.cpp
@@ -89,7 +89,7 @@ void IOBosstiary::loadBoostedBoss() {
query << "`date` = '" << today << "',";
query << "`boostname` = " << database.escapeString(bossName) << ",";
if (const auto bossType = getMonsterTypeByBossRaceId(bossId);
- bossType) {
+ 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) << ",";
@@ -124,7 +124,7 @@ void IOBosstiary::loadBoostedBoss() {
void IOBosstiary::addBosstiaryMonster(uint16_t raceId, const std::string &name) {
if (auto it = bosstiaryMap.find(raceId);
- it != bosstiaryMap.end()) {
+ it != bosstiaryMap.end()) {
return;
}
@@ -282,7 +282,7 @@ uint8_t IOBosstiary::getBossCurrentLevel(std::shared_ptr player, uint16_
auto bossRace = mType->info.bosstiaryRace;
uint8_t level = 0;
if (auto it = levelInfos.find(bossRace);
- it != levelInfos.end()) {
+ it != levelInfos.end()) {
const std::vector &infoForCurrentRace = it->second;
for (const auto &raceInfo : infoForCurrentRace) {
if (currentKills >= raceInfo.kills) {
diff --git a/src/io/iobestiary.cpp b/src/io/iobestiary.cpp
index c544021367e..9cd964e7c7c 100644
--- a/src/io/iobestiary.cpp
+++ b/src/io/iobestiary.cpp
@@ -225,9 +225,9 @@ void IOBestiary::addBestiaryKill(std::shared_ptr player, const std::shar
player->addBestiaryKillCount(raceid, amount);
if ((curCount == 0) || // Initial kill stage
- (curCount < mtype->info.bestiaryFirstUnlock && (curCount + amount) >= mtype->info.bestiaryFirstUnlock) || // First kill stage reached
- (curCount < mtype->info.bestiarySecondUnlock && (curCount + amount) >= mtype->info.bestiarySecondUnlock) || // Second kill stage reached
- (curCount < mtype->info.bestiaryToUnlock && (curCount + amount) >= mtype->info.bestiaryToUnlock)) { // Final kill stage reached
+ (curCount < mtype->info.bestiaryFirstUnlock && (curCount + amount) >= mtype->info.bestiaryFirstUnlock) || // First kill stage reached
+ (curCount < mtype->info.bestiarySecondUnlock && (curCount + amount) >= mtype->info.bestiarySecondUnlock) || // Second kill stage reached
+ (curCount < mtype->info.bestiaryToUnlock && (curCount + amount) >= mtype->info.bestiaryToUnlock)) { // Final kill stage reached
ss << "You unlocked details for the creature '" << mtype->name << "'";
player->sendTextMessage(MESSAGE_STATUS, ss.str());
player->sendBestiaryEntryChanged(raceid);
diff --git a/src/io/iomap.cpp b/src/io/iomap.cpp
index d50d9b2804f..c17978e8d1d 100644
--- a/src/io/iomap.cpp
+++ b/src/io/iomap.cpp
@@ -14,27 +14,27 @@
#include "io/filestream.hpp"
/*
- OTBM_ROOTV1
- |
- |--- OTBM_MAP_DATA
- | |
- | |--- OTBM_TILE_AREA
- | | |--- OTBM_TILE
- | | |--- OTBM_TILE_SQUARE (not implemented)
- | | |--- OTBM_TILE_REF (not implemented)
- | | |--- OTBM_HOUSETILE
- | |
- | |--- OTBM_SPAWNS (not implemented)
- | | |--- OTBM_SPAWN_AREA (not implemented)
- | | |--- OTBM_MONSTER (not implemented)
- | |
- | |--- OTBM_TOWNS
- | | |--- OTBM_TOWN
- | |
- | |--- OTBM_WAYPOINTS
- | |--- OTBM_WAYPOINT
- |
- |--- OTBM_ITEM_DEF (not implemented)
+ OTBM_ROOTV1
+ |
+ |--- OTBM_MAP_DATA
+ | |
+ | |--- OTBM_TILE_AREA
+ | | |--- OTBM_TILE
+ | | |--- OTBM_TILE_SQUARE (not implemented)
+ | | |--- OTBM_TILE_REF (not implemented)
+ | | |--- OTBM_HOUSETILE
+ | |
+ | |--- OTBM_SPAWNS (not implemented)
+ | | |--- OTBM_SPAWN_AREA (not implemented)
+ | | |--- OTBM_MONSTER (not implemented)
+ | |
+ | |--- OTBM_TOWNS
+ | | |--- OTBM_TOWN
+ | |
+ | |--- OTBM_WAYPOINTS
+ | |--- OTBM_WAYPOINT
+ |
+ |--- OTBM_ITEM_DEF (not implemented)
*/
void IOMap::loadMap(Map* map, const Position &pos) {
@@ -170,9 +170,9 @@ void IOMap::parseTileArea(FileStream &stream, Map &map, const Position &pos) {
if (tile->isHouse() && iType.movable) {
g_logger().warn("[IOMap::loadMap] - "
- "Movable item with ID: {}, in house: {}, "
- "at position: x {}, y {}, z {}",
- id, tile->houseId, x, y, z);
+ "Movable item with ID: {}, in house: {}, "
+ "at position: x {}, y {}, z {}",
+ id, tile->houseId, x, y, z);
} else if (iType.isGroundTile()) {
tile->ground = map.tryReplaceItemFromCache(item);
} else {
@@ -200,9 +200,9 @@ void IOMap::parseTileArea(FileStream &stream, Map &map, const Position &pos) {
// nothing
} else if (tile->isHouse() && iType.movable) {
g_logger().warn("[IOMap::loadMap] - "
- "Movable item with ID: {}, in house: {}, "
- "at position: x {}, y {}, z {}",
- id, tile->houseId, x, y, z);
+ "Movable item with ID: {}, in house: {}, "
+ "at position: x {}, y {}, z {}",
+ id, tile->houseId, x, y, z);
} else if (iType.isGroundTile()) {
tile->ground = map.tryReplaceItemFromCache(item);
} else {
diff --git a/src/io/ioprey.cpp b/src/io/ioprey.cpp
index 642423c2abd..6b2816355ff 100644
--- a/src/io/ioprey.cpp
+++ b/src/io/ioprey.cpp
@@ -73,7 +73,7 @@ void PreySlot::reloadMonsterGrid(std::vector blackList, uint32_t level
uint8_t stageThree;
uint8_t stageFour;
if (auto levelStage = static_cast(std::floor(level / 100));
- levelStage == 0) { // From level 0 to 99
+ levelStage == 0) { // From level 0 to 99
stageOne = 3;
stageTwo = 3;
stageThree = 2;
@@ -154,7 +154,7 @@ void TaskHuntingSlot::reloadMonsterGrid(std::vector blackList, uint32_
uint8_t stageThree;
uint8_t stageFour;
if (auto levelStage = static_cast(std::floor(level / 100));
- levelStage == 0) { // From level 0 to 99
+ levelStage == 0) { // From level 0 to 99
stageOne = 3;
stageTwo = 3;
stageThree = 2;
@@ -253,7 +253,7 @@ void IOPrey::checkPlayerPreys(std::shared_ptr player, uint8_t amount) co
for (uint8_t slotId = PreySlot_First; slotId <= PreySlot_Last; slotId++) {
if (const auto &slot = player->getPreySlotById(static_cast(slotId));
- slot && slot->isOccupied()) {
+ slot && slot->isOccupied()) {
if (slot->bonusTimeLeft <= amount) {
if (slot->option == PreyOption_AutomaticReroll) {
if (player->usePreyCards(static_cast