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/build-ubuntu-dummy.yml b/.github/workflows/build-ubuntu-dummy.yml
index 00c4efdef87..f4ebb00ff27 100644
--- a/.github/workflows/build-ubuntu-dummy.yml
+++ b/.github/workflows/build-ubuntu-dummy.yml
@@ -17,11 +17,9 @@ jobs:
strategy:
fail-fast: false
matrix:
- os: [ubuntu-20.04, ubuntu-22.04]
+ os: [ubuntu-22.04]
buildtype: [linux-release, linux-debug]
include:
- - os: ubuntu-20.04
- triplet: x64-linux
- os: ubuntu-22.04
triplet: x64-linux
diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml
index cdc54dc33f2..2b369e34875 100644
--- a/.github/workflows/build-ubuntu.yml
+++ b/.github/workflows/build-ubuntu.yml
@@ -36,11 +36,9 @@ jobs:
strategy:
fail-fast: false
matrix:
- os: [ubuntu-20.04, ubuntu-22.04]
+ os: [ubuntu-22.04]
buildtype: [linux-release, linux-debug]
include:
- - os: ubuntu-20.04
- triplet: x64-linux
- os: ubuntu-22.04
triplet: x64-linux
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 e4093f4de2d..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 "")
@@ -86,10 +86,9 @@ endif()
# === IPO ===
-option(OPTIONS_ENABLE_IPO "Check and Enable interprocedural optimization (IPO/LTO)" ON)
if(OPTIONS_ENABLE_IPO)
- log_option_enabled("IPO/LTO")
if(MSVC)
+ log_option_enabled("IPO/LTO")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /GL")
set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} /LTCG")
set(CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO} /LTCG")
@@ -97,6 +96,7 @@ if(OPTIONS_ENABLE_IPO)
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} /LTCG")
else()
if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" OR CMAKE_BUILD_TYPE STREQUAL "Release")
+ log_option_enabled("IPO/LTO")
include(CheckIPOSupported)
check_ipo_supported(RESULT result OUTPUT output)
if(result)
@@ -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/cmake/modules/BaseConfig.cmake b/cmake/modules/BaseConfig.cmake
index 7e3f6404b3f..a1980d0f604 100644
--- a/cmake/modules/BaseConfig.cmake
+++ b/cmake/modules/BaseConfig.cmake
@@ -121,7 +121,9 @@ if (MSVC)
endforeach(type)
add_compile_options(/MP /FS /Zf /EHsc)
-endif (MSVC)
+else()
+ add_compile_options(-Wno-unused-parameter -Wno-sign-compare -Wno-switch -Wno-implicit-fallthrough -Wno-extra)
+endif()
## Link compilation files to build/bin folder, else link to the main dir
function(set_output_directory target_name)
diff --git a/config.lua.dist b/config.lua.dist
index 0507b652e29..9d1ed2fa681 100644
--- a/config.lua.dist
+++ b/config.lua.dist
@@ -52,7 +52,8 @@ cleanProtectionZones = false
-- Connection Config
-- NOTE: allowOldProtocol can allow login on 10x protocol. (11.00)
-- NOTE: maxPlayers set to 0 means no limit
--- NOTE: MaxPacketsPerSeconds if you change you will be subject to bugs by WPE, keep the default value of 25
+-- NOTE: MaxPacketsPerSeconds if you change you will be subject to bugs by WPE, keep the default value of 25,
+-- It's recommended to use a range like min 50 in this function, otherwise you will be disconnected after equipping two-handed distance weapons.
ip = "127.0.0.1"
allowOldProtocol = false
bindOnlyGlobalAddress = false
@@ -80,6 +81,17 @@ freeDepotLimit = 2000
premiumDepotLimit = 10000
depotBoxes = 20
+-- Augments System (Get more info in: https://github.com/opentibiabr/canary/pull/2602)
+-- NOTE: the following values are for all weapons and equipments that have type of "increase damage", "powerful impact" and "strong impact".
+-- To customize the percentage of a particular item with these augment types, please add to the item "augments" section on items.xml as the example above.
+-- NOTE: The values represent percentage.
+-- NOTE: augmentIncreasedDamagePercent = value between 1 and 100 (damage percent to increase. ex: 5 = 5%, 50 = 50%)
+-- NOTE: augmentPowerfulImpactPercent = value between 1 and 100 (damage percent to increase. ex: 10 = 10%, 100 = 100%)
+-- NOTE: augmentStrongImpactPercent = value between 1 and 100 (damage percent to increase. ex: 7 = 7%, 70 = 70%)
+augmentIncreasedDamagePercent = 5
+augmentPowerfulImpactPercent = 7
+augmentStrongImpactPercent = 10
+
-- Prey system
-- NOTE: preyRerollPricePerLevel: Price multiplier in gold coin for rerolling prey list.
-- NOTE: preySelectListPrice: Price to manually select creature on list and to lock prey slot.
@@ -365,7 +377,10 @@ partyListMaxDistance = 30
toggleMapCustom = true
-- Market
+-- NOTE: marketRefreshPricesInterval (in minutes, minimum is 1 minute)
+-- NOTE: set it to 0 for disable, is the time in which the task will run updating the prices of the items that will be sent to the client
marketOfferDuration = 30 * 24 * 60 * 60
+marketRefreshPricesInterval = 30
premiumToCreateMarketOffer = true
checkExpiredMarketOffersEachMinutes = 60
maxMarketOffersAtATimePerPlayer = 100
@@ -397,7 +412,6 @@ resetSessionsOnStartup = false
-- Misc.
-- NOTE: experienceDisplayRates: set to false to ignore exp rate or true to include exp rate
-- NOTE: disableLegacyRaids: set to true to disable legacy XML raids
--- NOTE: combatChainDelay: set to minimum 50 miliseconds
allowChangeOutfit = true
toggleMountInProtectionZone = false
freePremium = false
@@ -430,7 +444,8 @@ maxElementalResistance = 200
maxDamageReflection = 200
-- Chain system
-toggleChainSystem = true
+-- NOTE: combatChainDelay: set to minimum 50 miliseconds
+toggleChainSystem = false
combatChainDelay = 50
combatChainTargets = 5
combatChainSkillFormulaAxe = 0.9
@@ -496,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/quests.lua b/data-otservbr-global/lib/core/quests.lua
index ef61ee2549b..a35dcb15dea 100644
--- a/data-otservbr-global/lib/core/quests.lua
+++ b/data-otservbr-global/lib/core/quests.lua
@@ -5592,7 +5592,7 @@ if not Quests then
},
[41] = {
name = "Adventurers Guild",
- startStorageId = Storage.AdventurersGuild.GreatDragonHunt.WarriorSkeleton,
+ startStorageId = Storage.AdventurersGuild.QuestLine,
startStorageValue = 1,
missions = {
[1] = {
@@ -5606,6 +5606,18 @@ if not Quests then
But the dragon hoards might justify the risks. You killed %d/50 dragons and dragon lords."):format(math.max(player:getStorageValue(Storage.AdventurersGuild.GreatDragonHunt.DragonCounter), 0))
end,
},
+ [2] = {
+ name = "The Lost Brother",
+ storageId = Storage.AdventurersGuild.TheLostBrother,
+ missionId = 11000,
+ startValue = 1,
+ endValue = 3,
+ states = {
+ [1] = "At the Kha'zeel Mountains you met the merchant Tarun. His brother has gone missing and was last seen following a beautiful woman into a palace. Tarun fears this woman might have been a demon.",
+ [2] = "You found the remains of Tarun's brother containing a message. Go back to Tarun and report his brother's last words.",
+ [3] = "You told Tarun about his brother's sad fate. He was very downhearted but gave you his sincere thanks. The beautiful asuri have taken a young man's life and the happiness of another one.",
+ },
+ },
},
},
[42] = {
diff --git a/data-otservbr-global/lib/core/storages.lua b/data-otservbr-global/lib/core/storages.lua
index 6ce29967451..2b8d9a7783d 100644
--- a/data-otservbr-global/lib/core/storages.lua
+++ b/data-otservbr-global/lib/core/storages.lua
@@ -1570,6 +1570,8 @@ Storage = {
WarriorSkeleton = 52146,
DragonCounter = 52147,
},
+ QuestLine = 52148,
+ TheLostBrother = 52149,
},
DreamersChallenge = {
-- Reserved storage from 52160 - 52199
@@ -2534,7 +2536,6 @@ Storage = {
NightmareTeddy = {},
PoacherCavesMiniWorldChange = {},
TheGreatDragonHunt = {},
- TheLostBrother = {},
TheTaintedSouls = {},
},
U10_90 = { -- update 10.90 - Reserved Storages 45201 - 45350
@@ -3078,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/lib/quests/the_primal_ordeal.lua b/data-otservbr-global/lib/quests/the_primal_ordeal.lua
index de708ee4de2..24324e1147b 100644
--- a/data-otservbr-global/lib/quests/the_primal_ordeal.lua
+++ b/data-otservbr-global/lib/quests/the_primal_ordeal.lua
@@ -6,12 +6,10 @@ function RegisterPrimalPackBeast(template)
primalMonster.loot = {}
primalMonster.name = "Primal Pack Beast"
primalMonster.description = "a primal pack beast"
-
- primalMonster.health = primalMonster.health
- primalMonster.maxHealth = primalMonster.maxHealth
+ primalMonster.maxHealth = primalMonster.maxHealth * 0.7
+ primalMonster.health = primalMonster.maxHealth
primalMonster.raceId = nil
primalMonster.Bestiary = nil
primalMonster.corpse = 0
-
primal:register(primalMonster)
end
diff --git a/data-otservbr-global/migrations/43.lua b/data-otservbr-global/migrations/43.lua
index 1464703c96e..6d3492a815a 100644
--- a/data-otservbr-global/migrations/43.lua
+++ b/data-otservbr-global/migrations/43.lua
@@ -1,5 +1,5 @@
function onUpdateDatabase()
- logger.info("Updating database to version 43 (feat frags_limit, payment and duration_days in guild wars)")
+ logger.info("Updating database to version 44 (feat frags_limit, payment and duration_days in guild wars)")
db.query([[
ALTER TABLE `guild_wars`
diff --git a/data-otservbr-global/migrations/44.lua b/data-otservbr-global/migrations/44.lua
index 86a6d8ffec1..c551fc79aeb 100644
--- a/data-otservbr-global/migrations/44.lua
+++ b/data-otservbr-global/migrations/44.lua
@@ -1,3 +1,11 @@
function onUpdateDatabase()
- return false -- true = There are others migrations file | false = this is the last migration file
+ logger.info("Updating database to version 45 (fix: mana shield column size for more than 65k)")
+
+ db.query([[
+ ALTER TABLE `players`
+ MODIFY COLUMN `manashield` INT UNSIGNED NOT NULL DEFAULT '0',
+ MODIFY COLUMN `max_manashield` INT UNSIGNED NOT NULL DEFAULT '0';
+ ]])
+
+ return true
end
diff --git a/data-otservbr-global/migrations/45.lua b/data-otservbr-global/migrations/45.lua
new file mode 100644
index 00000000000..4ceb5f7e3fd
--- /dev/null
+++ b/data-otservbr-global/migrations/45.lua
@@ -0,0 +1,56 @@
+function onUpdateDatabase()
+ logger.info("Updating database to version 46 (feat: vip groups)")
+
+ db.query([[
+ CREATE TABLE IF NOT EXISTS `account_vipgroups` (
+ `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',
+ CONSTRAINT `account_vipgroups_pk` PRIMARY KEY (`id`, `account_id`)
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+ ]])
+
+ db.query([[
+ CREATE TRIGGER `oncreate_accounts` AFTER INSERT ON `accounts` FOR EACH ROW BEGIN
+ 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;
+ ]])
+
+ db.query([[
+ 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` 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`),
+ CONSTRAINT `account_vipgrouplist_unique` UNIQUE (`account_id`, `player_id`, `vipgroup_id`),
+ CONSTRAINT `account_vipgrouplist_player_fk`
+ FOREIGN KEY (`player_id`) REFERENCES `players` (`id`)
+ ON DELETE CASCADE,
+ CONSTRAINT `account_vipgrouplist_vipgroup_fk`
+ FOREIGN KEY (`vipgroup_id`, `account_id`) REFERENCES `account_vipgroups` (`id`, `account_id`)
+ ON DELETE CASCADE
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+ ]])
+
+ db.query([[
+ INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`)
+ SELECT 1, id, 'Friends', 0 FROM `accounts`;
+ ]])
+
+ db.query([[
+ INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`)
+ SELECT 2, id, 'Enemies', 0 FROM `accounts`;
+ ]])
+
+ db.query([[
+ INSERT INTO `account_vipgroups` (`id`, `account_id`, `name`, `customizable`)
+ SELECT 3, id, 'Trading Partners', 0 FROM `accounts`;
+ ]])
+
+ return true
+end
diff --git a/data-otservbr-global/migrations/46.lua b/data-otservbr-global/migrations/46.lua
new file mode 100644
index 00000000000..86a6d8ffec1
--- /dev/null
+++ b/data-otservbr-global/migrations/46.lua
@@ -0,0 +1,3 @@
+function onUpdateDatabase()
+ return false -- true = There are others migrations file | false = this is the last migration file
+end
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/primal_ordeal_quest/the_primal_menace.lua b/data-otservbr-global/monster/quests/primal_ordeal_quest/the_primal_menace.lua
index b0bc59364d6..7ad43b016c9 100644
--- a/data-otservbr-global/monster/quests/primal_ordeal_quest/the_primal_menace.lua
+++ b/data-otservbr-global/monster/quests/primal_ordeal_quest/the_primal_menace.lua
@@ -24,7 +24,6 @@ local thePrimalMenaceConfig = {
CountGrowthPerHazard = 1.05,
CountMax = 6,
- HpRateOnSpawn = 0.7,
MonsterPool = {
"Emerald Tortoise (Primal)",
"Gore Horn (Primal)",
@@ -291,8 +290,6 @@ local function spawnMonster(monsterId, spawnPosition)
MonsterId = primalMonster:getId(),
Created = os.time(),
}
- local monsterMaxHealth = primalMonster:getMaxHealth()
- primalMonster:setHealth(monsterMaxHealth * thePrimalMenaceConfig.MonsterConfig.HpRateOnSpawn)
local primalBeasts = monster:getStorageValue(thePrimalMenaceConfig.Storage.PrimalBeasts)
table.insert(primalBeasts, primalBeastEntry)
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/alaistar.lua b/data-otservbr-global/npc/alaistar.lua
index e893975b24b..68aaa75679a 100644
--- a/data-otservbr-global/npc/alaistar.lua
+++ b/data-otservbr-global/npc/alaistar.lua
@@ -36,13 +36,14 @@ local itemsTable = {
{ itemName = "strong health potion", clientId = 236, buy = 115 },
{ itemName = "strong mana potion", clientId = 237, buy = 93 },
{ itemName = "supreme health potion", clientId = 23375, buy = 625 },
- { itemName = "ultimate health potion", clientId = 7643, buy = 438 },
- { itemName = "ultimate mana potion", clientId = 23373, buy = 379 },
+ { itemName = "ultimate health potion", clientId = 7643, buy = 379 },
+ { itemName = "ultimate mana potion", clientId = 23373, buy = 438 },
{ itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
["creature products"] = {
{ itemName = "cowbell", clientId = 21204, sell = 210 },
+ { itemName = "execowtioner mask", clientId = 21201, sell = 240 },
{ itemName = "giant pacifier", clientId = 21199, sell = 170 },
{ itemName = "glob of glooth", clientId = 21182, sell = 125 },
{ itemName = "glooth injection tube", clientId = 21103, sell = 350 },
diff --git a/data-otservbr-global/npc/alexander.lua b/data-otservbr-global/npc/alexander.lua
index 78f51e486c8..fda2799bf4d 100644
--- a/data-otservbr-global/npc/alexander.lua
+++ b/data-otservbr-global/npc/alexander.lua
@@ -30,6 +30,28 @@ npcConfig.voices = {
}
local itemsTable = {
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["creature products"] = {
+ { itemName = "crystal ball", clientId = 3076, buy = 530, sell = 190 },
+ { itemName = "life crystal", clientId = 3061, sell = 83 },
+ { itemName = "mind stone", clientId = 3062, sell = 170 },
+ },
+ ["shields"] = {
+ { itemName = "spellbook of enlightenment", clientId = 8072, sell = 4000 },
+ { itemName = "spellbook of warding", clientId = 8073, sell = 8000 },
+ { itemName = "spellbook of mind control", clientId = 8074, sell = 13000 },
+ { itemName = "spellbook of lost souls", clientId = 8075, sell = 19000 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
["runes"] = {
{ itemName = "animate dead rune", clientId = 3203, buy = 375 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
diff --git a/data-otservbr-global/npc/an_idol.lua b/data-otservbr-global/npc/an_idol.lua
new file mode 100644
index 00000000000..69977dc81ec
--- /dev/null
+++ b/data-otservbr-global/npc/an_idol.lua
@@ -0,0 +1,68 @@
+local internalNpcName = "An Idol"
+local npcType = Game.createNpcType(internalNpcName)
+local npcConfig = {}
+
+npcConfig.name = internalNpcName
+npcConfig.description = internalNpcName
+
+npcConfig.health = 100
+npcConfig.maxHealth = npcConfig.health
+npcConfig.walkInterval = 0
+npcConfig.walkRadius = 2
+
+npcConfig.outfit = {
+ lookTypeEx = 15894,
+}
+
+npcConfig.flags = {
+ floorchange = false,
+}
+
+local keywordHandler = KeywordHandler:new()
+local npcHandler = NpcHandler:new(keywordHandler)
+
+npcType.onThink = function(npc, interval)
+ npcHandler:onThink(npc, interval)
+end
+
+npcType.onAppear = function(npc, creature)
+ npcHandler:onAppear(npc, creature)
+end
+
+npcType.onDisappear = function(npc, creature)
+ npcHandler:onDisappear(npc, creature)
+end
+
+npcType.onMove = function(npc, creature, fromPosition, toPosition)
+ npcHandler:onMove(npc, creature, fromPosition, toPosition)
+end
+
+npcType.onSay = function(npc, creature, type, message)
+ npcHandler:onSay(npc, creature, type, message)
+end
+
+npcType.onCloseChannel = function(npc, creature)
+ npcHandler:onCloseChannel(npc, creature)
+end
+
+local function creatureSayCallback(npc, creature, type, message)
+ local player = Player(creature)
+
+ if not npcHandler:checkInteraction(npc, creature) then
+ return false
+ end
+
+ if MsgContains(message, "VBOX") then
+ npcHandler:say("J-T B^C J^BXT°", npc, creature)
+ player:teleportTo(Position(32366, 32531, 8), false)
+ player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
+ end
+
+ return true
+end
+
+npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
+npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, false)
+
+-- npcType registering the npcConfig table
+npcType:register(npcConfig)
diff --git a/data-otservbr-global/npc/asima.lua b/data-otservbr-global/npc/asima.lua
index 645689c42d4..c3aca3fcd5e 100644
--- a/data-otservbr-global/npc/asima.lua
+++ b/data-otservbr-global/npc/asima.lua
@@ -24,6 +24,14 @@ npcConfig.flags = {
}
local itemsTable = {
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
["potions"] = {
{ itemName = "empty potion flask", clientId = 283, sell = 5 },
{ itemName = "empty potion flask", clientId = 284, sell = 5 },
@@ -41,6 +49,12 @@ local itemsTable = {
{ itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
+ ["shields"] = {
+ { itemName = "spellbook", clientId = 3059, buy = 150 },
+ },
["runes"] = {
{ itemName = "avalanche rune", clientId = 3161, buy = 57 },
{ itemName = "blank rune", clientId = 3147, buy = 10 },
@@ -61,8 +75,27 @@ local itemsTable = {
{ itemName = "poison field rune", clientId = 3172, buy = 21 },
{ itemName = "poison wall rune", clientId = 3176, buy = 52 },
{ itemName = "sudden death rune", clientId = 3155, buy = 135 },
+ { itemName = "stalagmite rune", clientId = 3179, buy = 12 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
+ ["wands"] = {
+ { itemName = "hailstorm rod", clientId = 3067, buy = 15000 },
+ { itemName = "moonlight rod", clientId = 3070, buy = 1000 },
+ { itemName = "necrotic rod", clientId = 3069, buy = 5000 },
+ { itemName = "northwind rod", clientId = 8083, buy = 7500 },
+ { itemName = "snakebite rod", clientId = 3066, buy = 500 },
+ { itemName = "springsprout rod", clientId = 8084, buy = 18000 },
+ { itemName = "terra rod", clientId = 3065, buy = 10000 },
+ { itemName = "underworld rod", clientId = 8082, buy = 22000 },
+ { itemName = "wand of cosmic energy", clientId = 3073, buy = 10000 },
+ { itemName = "wand of decay", clientId = 3072, buy = 5000 },
+ { itemName = "wand of draconia", clientId = 8093, buy = 7500 },
+ { itemName = "wand of dragonbreath", clientId = 3075, buy = 1000 },
+ { itemName = "wand of inferno", clientId = 3071, buy = 15000 },
+ { itemName = "wand of starstorm", clientId = 8092, buy = 18000 },
+ { itemName = "wand of voodoo", clientId = 8094, buy = 22000 },
+ { itemName = "wand of vortex", clientId = 3074, buy = 500 },
+ },
}
npcConfig.shop = {}
diff --git a/data-otservbr-global/npc/briasol.lua b/data-otservbr-global/npc/briasol.lua
index 6905011fee9..38dcd074159 100644
--- a/data-otservbr-global/npc/briasol.lua
+++ b/data-otservbr-global/npc/briasol.lua
@@ -113,7 +113,7 @@ npcConfig.shop = {
{ itemName = "cyan crystal fragment", clientId = 16125, sell = 800 },
{ itemName = "dragon figurine", clientId = 30053, sell = 45000 },
{ itemName = "gemmed figurine", clientId = 24392, sell = 3500 },
- { itemName = "giant amethyst", clientId = 30061, sell = 60000 },
+ { itemName = "giant amethyst", clientId = 32622, sell = 60000 },
{ itemName = "giant emerald", clientId = 30060, sell = 90000 },
{ itemName = "giant ruby", clientId = 30059, sell = 70000 },
{ itemName = "giant sapphire", clientId = 30061, sell = 50000 },
diff --git a/data-otservbr-global/npc/chantalle.lua b/data-otservbr-global/npc/chantalle.lua
index 2615f6557da..3ed42984f5c 100644
--- a/data-otservbr-global/npc/chantalle.lua
+++ b/data-otservbr-global/npc/chantalle.lua
@@ -99,7 +99,7 @@ npcConfig.shop = {
{ itemName = "diamond", clientId = 32770, sell = 15000 },
{ itemName = "dragon figurine", clientId = 30053, sell = 45000 },
{ itemName = "gemmed figurine", clientId = 24392, sell = 3500 },
- { itemName = "giant amethyst", clientId = 30061, sell = 60000 },
+ { itemName = "giant amethyst", clientId = 32622, sell = 60000 },
{ itemName = "giant emerald", clientId = 30060, sell = 90000 },
{ itemName = "giant ruby", clientId = 30059, sell = 70000 },
{ itemName = "giant sapphire", clientId = 30061, sell = 50000 },
diff --git a/data-otservbr-global/npc/chuckles.lua b/data-otservbr-global/npc/chuckles.lua
index 51fb3f2add6..4eb06b6bb3d 100644
--- a/data-otservbr-global/npc/chuckles.lua
+++ b/data-otservbr-global/npc/chuckles.lua
@@ -59,6 +59,38 @@ local itemsTable = {
{ itemName = "sudden death rune", clientId = 3155, buy = 135 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
+ ["wands"] = {
+ { itemName = "hailstorm rod", clientId = 3067, buy = 15000 },
+ { itemName = "moonlight rod", clientId = 3070, buy = 1000 },
+ { itemName = "necrotic rod", clientId = 3069, buy = 5000 },
+ { itemName = "northwind rod", clientId = 8083, buy = 7500 },
+ { itemName = "snakebite rod", clientId = 3066, buy = 500 },
+ { itemName = "springsprout rod", clientId = 8084, buy = 18000 },
+ { itemName = "terra rod", clientId = 3065, buy = 10000 },
+ { itemName = "underworld rod", clientId = 8082, buy = 22000 },
+ { itemName = "wand of cosmic energy", clientId = 3073, buy = 10000 },
+ { itemName = "wand of decay", clientId = 3072, buy = 5000 },
+ { itemName = "wand of draconia", clientId = 8093, buy = 7500 },
+ { itemName = "wand of dragonbreath", clientId = 3075, buy = 1000 },
+ { itemName = "wand of inferno", clientId = 3071, buy = 15000 },
+ { itemName = "wand of starstorm", clientId = 8092, buy = 18000 },
+ { itemName = "wand of voodoo", clientId = 8094, buy = 22000 },
+ { itemName = "wand of vortex", clientId = 3074, buy = 500 },
+ },
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
+ ["shields"] = {
+ { itemName = "spellbook", clientId = 3059, buy = 150 },
+ },
}
npcConfig.shop = {}
diff --git a/data-otservbr-global/npc/edmund.lua b/data-otservbr-global/npc/edmund.lua
index d8635297949..c61a95bcd72 100644
--- a/data-otservbr-global/npc/edmund.lua
+++ b/data-otservbr-global/npc/edmund.lua
@@ -68,7 +68,7 @@ npcConfig.shop = {
{ itemName = "cyan crystal fragment", clientId = 16125, sell = 800 },
{ itemName = "dragon figurine", clientId = 30053, sell = 45000 },
{ itemName = "gemmed figurine", clientId = 24392, sell = 3500 },
- { itemName = "giant amethyst", clientId = 30061, sell = 60000 },
+ { itemName = "giant amethyst", clientId = 32622, sell = 60000 },
{ itemName = "giant emerald", clientId = 30060, sell = 90000 },
{ itemName = "giant ruby", clientId = 30059, sell = 70000 },
{ itemName = "giant sapphire", clientId = 30061, sell = 50000 },
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/fenech.lua b/data-otservbr-global/npc/fenech.lua
index 7152c0ef9c5..f69dcafaa89 100644
--- a/data-otservbr-global/npc/fenech.lua
+++ b/data-otservbr-global/npc/fenech.lua
@@ -71,6 +71,20 @@ local itemsTable = {
{ itemName = "sudden death rune", clientId = 3155, buy = 135 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
+ ["shields"] = {
+ { itemName = "spellbook", clientId = 3059, buy = 150 },
+ },
}
npcConfig.shop = {}
diff --git a/data-otservbr-global/npc/frans.lua b/data-otservbr-global/npc/frans.lua
index 8761a7d89d6..a1b695adf14 100644
--- a/data-otservbr-global/npc/frans.lua
+++ b/data-otservbr-global/npc/frans.lua
@@ -58,6 +58,20 @@ local itemsTable = {
{ itemName = "sudden death rune", clientId = 3155, buy = 135 },
{ itemName = "ultimate healing rune", clientId = 3160, buy = 175 },
},
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
+ ["shields"] = {
+ { itemName = "spellbook", clientId = 3059, buy = 150 },
+ },
}
npcConfig.shop = {}
diff --git a/data-otservbr-global/npc/frederik.lua b/data-otservbr-global/npc/frederik.lua
index 9b33ccf9684..81ff1ec58b6 100644
--- a/data-otservbr-global/npc/frederik.lua
+++ b/data-otservbr-global/npc/frederik.lua
@@ -82,6 +82,20 @@ local itemsTable = {
{ itemName = "ultimate spirit potion", clientId = 23374, buy = 438 },
{ itemName = "vial", clientId = 2874, sell = 5 },
},
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
+ ["shields"] = {
+ { itemName = "spellbook", clientId = 3059, buy = 150 },
+ },
}
npcConfig.shop = {}
diff --git a/data-otservbr-global/npc/gail.lua b/data-otservbr-global/npc/gail.lua
index 80a9b54b3e5..de2a52dc7f4 100644
--- a/data-otservbr-global/npc/gail.lua
+++ b/data-otservbr-global/npc/gail.lua
@@ -109,7 +109,7 @@ npcConfig.shop = {
{ itemName = "cyan crystal fragment", clientId = 16125, sell = 800 },
{ itemName = "dragon figurine", clientId = 30053, sell = 45000 },
{ itemName = "gemmed figurine", clientId = 24392, sell = 3500 },
- { itemName = "giant amethyst", clientId = 30061, sell = 60000 },
+ { itemName = "giant amethyst", clientId = 32622, sell = 60000 },
{ itemName = "giant emerald", clientId = 30060, sell = 90000 },
{ itemName = "giant ruby", clientId = 30059, sell = 70000 },
{ itemName = "giant sapphire", clientId = 30061, sell = 50000 },
diff --git a/data-otservbr-global/npc/gnomegica.lua b/data-otservbr-global/npc/gnomegica.lua
index 0805ca33ace..b860caf3962 100644
--- a/data-otservbr-global/npc/gnomegica.lua
+++ b/data-otservbr-global/npc/gnomegica.lua
@@ -78,6 +78,20 @@ local itemsTable = {
{ itemName = "wand of dragonbreath", clientId = 3075, buy = 1000 },
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
+ ["shields"] = {
+ { itemName = "spellbook", clientId = 3059, buy = 150 },
+ },
}
npcConfig.shop = {}
diff --git a/data-otservbr-global/npc/hanna.lua b/data-otservbr-global/npc/hanna.lua
index 7fca4c908aa..dfea1547135 100644
--- a/data-otservbr-global/npc/hanna.lua
+++ b/data-otservbr-global/npc/hanna.lua
@@ -145,7 +145,7 @@ npcConfig.shop = {
{ itemName = "cyan crystal fragment", clientId = 16125, sell = 800 },
{ itemName = "dragon figurine", clientId = 30053, sell = 45000 },
{ itemName = "gemmed figurine", clientId = 24392, sell = 3500 },
- { itemName = "giant amethyst", clientId = 30061, sell = 60000 },
+ { itemName = "giant amethyst", clientId = 32622, sell = 60000 },
{ itemName = "giant emerald", clientId = 30060, sell = 90000 },
{ itemName = "giant ruby", clientId = 30059, sell = 70000 },
{ itemName = "giant sapphire", clientId = 30061, sell = 50000 },
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/ishina.lua b/data-otservbr-global/npc/ishina.lua
index 1fd61200c15..358ee2619a3 100644
--- a/data-otservbr-global/npc/ishina.lua
+++ b/data-otservbr-global/npc/ishina.lua
@@ -139,7 +139,7 @@ npcConfig.shop = {
{ itemName = "cyan crystal fragment", clientId = 16125, sell = 800 },
{ itemName = "dragon figurine", clientId = 30053, sell = 45000 },
{ itemName = "gemmed figurine", clientId = 24392, sell = 3500 },
- { itemName = "giant amethyst", clientId = 30061, sell = 60000 },
+ { itemName = "giant amethyst", clientId = 32622, sell = 60000 },
{ itemName = "giant emerald", clientId = 30060, sell = 90000 },
{ itemName = "giant ruby", clientId = 30059, sell = 70000 },
{ itemName = "giant sapphire", clientId = 30061, sell = 50000 },
diff --git a/data-otservbr-global/npc/iwan.lua b/data-otservbr-global/npc/iwan.lua
index 2cc24843318..77689b12003 100644
--- a/data-otservbr-global/npc/iwan.lua
+++ b/data-otservbr-global/npc/iwan.lua
@@ -78,7 +78,7 @@ npcConfig.shop = {
{ itemName = "cyan crystal fragment", clientId = 16125, sell = 800 },
{ itemName = "dragon figurine", clientId = 30053, sell = 45000 },
{ itemName = "gemmed figurine", clientId = 24392, sell = 3500 },
- { itemName = "giant amethyst", clientId = 30061, sell = 60000 },
+ { itemName = "giant amethyst", clientId = 32622, sell = 60000 },
{ itemName = "giant emerald", clientId = 30060, sell = 90000 },
{ itemName = "giant ruby", clientId = 30059, sell = 70000 },
{ itemName = "giant sapphire", clientId = 30061, sell = 50000 },
diff --git a/data-otservbr-global/npc/jessica.lua b/data-otservbr-global/npc/jessica.lua
index 37ac18ed54a..43b1839b3ee 100644
--- a/data-otservbr-global/npc/jessica.lua
+++ b/data-otservbr-global/npc/jessica.lua
@@ -98,7 +98,7 @@ npcConfig.shop = {
{ itemName = "diamond", clientId = 32770, sell = 15000 },
{ itemName = "dragon figurine", clientId = 30053, sell = 45000 },
{ itemName = "gemmed figurine", clientId = 24392, sell = 3500 },
- { itemName = "giant amethyst", clientId = 30061, sell = 60000 },
+ { itemName = "giant amethyst", clientId = 32622, sell = 60000 },
{ itemName = "giant emerald", clientId = 30060, sell = 90000 },
{ itemName = "giant ruby", clientId = 30059, sell = 70000 },
{ itemName = "giant sapphire", clientId = 30061, sell = 50000 },
diff --git a/data-otservbr-global/npc/khanna.lua b/data-otservbr-global/npc/khanna.lua
index 02fcee7b6d5..76b5c1e70da 100644
--- a/data-otservbr-global/npc/khanna.lua
+++ b/data-otservbr-global/npc/khanna.lua
@@ -34,7 +34,7 @@ local itemsTable = {
["runes"] = {
{ itemName = "animate dead rune", clientId = 3203, buy = 375 },
{ itemName = "avalanche rune", clientId = 3161, buy = 57 },
- { itemName = "blank rune", clientId = 3147, buy = 10 },
+ { itemName = "blank rune", clientId = 3147, buy = 20 },
{ itemName = "chameleon rune", clientId = 3178, buy = 210 },
{ itemName = "convince creature rune", clientId = 3177, buy = 80 },
{ itemName = "cure poison rune", clientId = 3153, buy = 65 },
@@ -84,6 +84,46 @@ local itemsTable = {
{ itemName = "wand of voodoo", clientId = 8094, buy = 22000 },
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["creature products"] = {
+ { itemName = "bashmu fang", clientId = 36820, sell = 600 },
+ { itemName = "bashmu feather", clientId = 36820, sell = 350 },
+ { itemName = "bashmu tongue", clientId = 36820, sell = 400 },
+ { itemName = "blue goanna scale", clientId = 31559, sell = 230 },
+ { itemName = "crystal ball", clientId = 3076, buy = 650 },
+ { itemName = "fafnar symbol", clientId = 31443, sell = 950 },
+ { itemName = "goanna claw", clientId = 31561, sell = 950 },
+ { itemName = "goanna meat", clientId = 31560, sell = 190 },
+ { itemName = "lamassu hoof", clientId = 31441, sell = 330 },
+ { itemName = "lamassu horn", clientId = 31442, sell = 240 },
+ { itemName = "life crystal", clientId = 3061, sell = 85 },
+ { itemName = "lizard heart", clientId = 31340, sell = 530 },
+ { itemName = "manticore ear", clientId = 31440, sell = 310 },
+ { itemName = "manticore tail", clientId = 31439, sell = 220 },
+ { itemName = "mind stone", clientId = 3062, sell = 170 },
+ { itemName = "old girtablilu carapace", clientId = 36972, sell = 570 },
+ { itemName = "red goanna scale", clientId = 31558, sell = 270 },
+ { itemName = "scorpion charm", clientId = 36822, sell = 620 },
+ { itemName = "sphinx feather", clientId = 31437, sell = 470 },
+ { itemName = "sphinx tiara", clientId = 31438, sell = 360 },
+ },
+ ["shields"] = {
+ { itemName = "spellbook", clientId = 3059, buy = 150 },
+ { itemName = "spellbook of enlightenment", clientId = 8072, sell = 4000 },
+ { itemName = "spellbook of lost souls", clientId = 8075, sell = 19000 },
+ { itemName = "spellbook of mind control", clientId = 8074, sell = 13000 },
+ { itemName = "spellbook of warding", clientId = 8073, sell = 8000 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
}
npcConfig.shop = {}
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/mordecai.lua b/data-otservbr-global/npc/mordecai.lua
index 60063a423ba..dc0e3c07a77 100644
--- a/data-otservbr-global/npc/mordecai.lua
+++ b/data-otservbr-global/npc/mordecai.lua
@@ -88,6 +88,17 @@ local itemsTable = {
{ itemName = "wand of voodoo", clientId = 8094, buy = 22000 },
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
}
npcConfig.shop = {}
diff --git a/data-otservbr-global/npc/nelly.lua b/data-otservbr-global/npc/nelly.lua
index 8c3b123c47a..911464524c6 100644
--- a/data-otservbr-global/npc/nelly.lua
+++ b/data-otservbr-global/npc/nelly.lua
@@ -94,6 +94,20 @@ local itemsTable = {
{ itemName = "letter", clientId = 3505, buy = 8 },
{ itemName = "parcel", clientId = 3503, buy = 15 },
},
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
+ ["shields"] = {
+ { itemName = "spellbook", clientId = 3059, buy = 150 },
+ },
}
npcConfig.shop = {}
diff --git a/data-otservbr-global/npc/nipuna.lua b/data-otservbr-global/npc/nipuna.lua
index aa9d74cec52..ef3211bce42 100644
--- a/data-otservbr-global/npc/nipuna.lua
+++ b/data-otservbr-global/npc/nipuna.lua
@@ -101,6 +101,17 @@ local itemsTable = {
{ itemName = "wand of voodoo", clientId = 8094, buy = 22000 },
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
}
npcConfig.shop = {}
diff --git a/data-otservbr-global/npc/odemara.lua b/data-otservbr-global/npc/odemara.lua
index bcfe94bf8eb..3e1986714c7 100644
--- a/data-otservbr-global/npc/odemara.lua
+++ b/data-otservbr-global/npc/odemara.lua
@@ -70,7 +70,7 @@ npcConfig.shop = {
{ itemName = "diamond", clientId = 32770, sell = 15000 },
{ itemName = "dragon figurine", clientId = 30053, sell = 45000 },
{ itemName = "gemmed figurine", clientId = 24392, sell = 3500 },
- { itemName = "giant amethyst", clientId = 30061, sell = 60000 },
+ { itemName = "giant amethyst", clientId = 32622, sell = 60000 },
{ itemName = "giant emerald", clientId = 30060, sell = 90000 },
{ itemName = "giant ruby", clientId = 30059, sell = 70000 },
{ itemName = "giant sapphire", clientId = 30061, sell = 50000 },
diff --git a/data-otservbr-global/npc/oiriz.lua b/data-otservbr-global/npc/oiriz.lua
index 3a5d3a6b411..4b731c015a2 100644
--- a/data-otservbr-global/npc/oiriz.lua
+++ b/data-otservbr-global/npc/oiriz.lua
@@ -68,7 +68,7 @@ npcConfig.shop = {
{ itemName = "cyan crystal fragment", clientId = 16125, sell = 800 },
{ itemName = "dragon figurine", clientId = 30053, sell = 45000 },
{ itemName = "gemmed figurine", clientId = 24392, sell = 3500 },
- { itemName = "giant amethyst", clientId = 30061, sell = 60000 },
+ { itemName = "giant amethyst", clientId = 32622, sell = 60000 },
{ itemName = "giant emerald", clientId = 30060, sell = 90000 },
{ itemName = "giant ruby", clientId = 30059, sell = 70000 },
{ itemName = "giant sapphire", clientId = 30061, sell = 50000 },
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/rabaz.lua b/data-otservbr-global/npc/rabaz.lua
index 3e47da6a5ca..e532e7def66 100644
--- a/data-otservbr-global/npc/rabaz.lua
+++ b/data-otservbr-global/npc/rabaz.lua
@@ -82,6 +82,20 @@ local itemsTable = {
{ itemName = "wand of voodoo", clientId = 8094, buy = 22000 },
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
+ ["shields"] = {
+ { itemName = "spellbook", clientId = 3059, buy = 150 },
+ },
}
npcConfig.shop = {}
diff --git a/data-otservbr-global/npc/rachel.lua b/data-otservbr-global/npc/rachel.lua
index 3206c99864a..057787692c9 100644
--- a/data-otservbr-global/npc/rachel.lua
+++ b/data-otservbr-global/npc/rachel.lua
@@ -71,6 +71,23 @@ local itemsTable = {
{ itemName = "wand of dragonbreath", clientId = 3075, buy = 1000 },
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
+ ["valuables"] = {
+ { itemName = "talon", clientId = 3034, sell = 320 },
+ },
+ ["shields"] = {
+ { itemName = "spellbook", clientId = 3059, buy = 150 },
+ },
}
npcConfig.shop = {}
diff --git a/data-otservbr-global/npc/romir.lua b/data-otservbr-global/npc/romir.lua
index 11ea038d266..09ab62ab1a4 100644
--- a/data-otservbr-global/npc/romir.lua
+++ b/data-otservbr-global/npc/romir.lua
@@ -82,6 +82,20 @@ local itemsTable = {
{ itemName = "wand of voodoo", clientId = 8094, buy = 22000 },
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
+ ["shields"] = {
+ { itemName = "spellbook", clientId = 3059, buy = 150 },
+ },
}
npcConfig.shop = {}
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/shiriel.lua b/data-otservbr-global/npc/shiriel.lua
index 546bb26447b..fd982f345b9 100644
--- a/data-otservbr-global/npc/shiriel.lua
+++ b/data-otservbr-global/npc/shiriel.lua
@@ -70,6 +70,20 @@ local itemsTable = {
{ itemName = "wand of dragonbreath", clientId = 3075, buy = 1000 },
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
+ ["shields"] = {
+ { itemName = "spellbook", clientId = 3059, buy = 150 },
+ },
}
npcConfig.shop = {}
diff --git a/data-otservbr-global/npc/sigurd.lua b/data-otservbr-global/npc/sigurd.lua
index cca63a33e3e..e4d1b585307 100644
--- a/data-otservbr-global/npc/sigurd.lua
+++ b/data-otservbr-global/npc/sigurd.lua
@@ -72,6 +72,20 @@ local itemsTable = {
{ itemName = "wand of dragonbreath", clientId = 3075, buy = 1000 },
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
+ ["shields"] = {
+ { itemName = "spellbook", clientId = 3059, buy = 150 },
+ },
}
npcConfig.shop = {}
diff --git a/data-otservbr-global/npc/sundara.lua b/data-otservbr-global/npc/sundara.lua
index c1364240fa9..95ff206f2ad 100644
--- a/data-otservbr-global/npc/sundara.lua
+++ b/data-otservbr-global/npc/sundara.lua
@@ -101,6 +101,17 @@ local itemsTable = {
{ itemName = "wand of voodoo", clientId = 8094, buy = 22000 },
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
}
npcConfig.shop = {}
diff --git a/data-otservbr-global/npc/tandros.lua b/data-otservbr-global/npc/tandros.lua
index e25ba65f2a3..ae647b26127 100644
--- a/data-otservbr-global/npc/tandros.lua
+++ b/data-otservbr-global/npc/tandros.lua
@@ -88,6 +88,20 @@ local itemsTable = {
{ itemName = "wand of voodoo", clientId = 8094, buy = 22000 },
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
+ ["shields"] = {
+ { itemName = "spellbook", clientId = 3059, buy = 150 },
+ },
}
npcConfig.shop = {}
diff --git a/data-otservbr-global/npc/tarun.lua b/data-otservbr-global/npc/tarun.lua
index 31f56602f81..9f0b8be5883 100644
--- a/data-otservbr-global/npc/tarun.lua
+++ b/data-otservbr-global/npc/tarun.lua
@@ -51,6 +51,74 @@ npcType.onCloseChannel = function(npc, creature)
npcHandler:onCloseChannel(npc, creature)
end
+local function creatureSayCallback(npc, creature, type, message)
+ local player = Player(creature)
+ if not player then
+ return false
+ end
+ local playerId = player:getId()
+
+ if not npcHandler:checkInteraction(npc, creature) then
+ return false
+ end
+
+ local theLostBrotherStorage = player:getStorageValue(Storage.AdventurersGuild.TheLostBrother)
+ if MsgContains(message, "mission") then
+ if theLostBrotherStorage < 1 then
+ npcHandler:say({
+ "My brother is missing. I fear, he went to this evil palace north of here. A place of great beauty, certainly filled with riches and luxury. But in truth it is a threshold to hell and demonesses are after his blood. ...",
+ "He is my brother, and I am deeply ashamed to admit but I don't dare to go there. Perhaps your heart is more courageous than mine. Would you go to see this place and search for my brother?",
+ }, npc, creature)
+ npcHandler:setTopic(playerId, 1)
+ elseif theLostBrotherStorage == 1 then
+ npcHandler:say("I hope you will find my brother.", npc, creature)
+ npcHandler:setTopic(playerId, 0)
+ elseif theLostBrotherStorage == 2 then
+ npcHandler:say({
+ "So, he is dead as I feared. I warned him not to go with this woman, but he gave in to temptation. My heart darkens and moans. But you have my sincere thanks. ...",
+ "Without your help I would have stayed in the dark about his fate. Please, take this as a little recompense.",
+ }, npc, creature)
+ player:addItem(3039, 1)
+ player:addExperience(3000, true)
+ player:setStorageValue(Storage.AdventurersGuild.TheLostBrother, 3)
+ npcHandler:setTopic(playerId, 0)
+ end
+ elseif npcHandler:getTopic(playerId) == 1 then
+ if MsgContains(message, "yes") then
+ npcHandler:say("I thank you! This is more than I could hope!", npc, creature)
+ if theLostBrotherStorage < 1 then
+ player:setStorageValue(Storage.AdventurersGuild.QuestLine, 1)
+ end
+ player:setStorageValue(Storage.AdventurersGuild.TheLostBrother, 1)
+ elseif MsgContains(message, "no") then
+ npcHandler:say("As you wish.", npc, creature)
+ end
+ npcHandler:setTopic(playerId, 0)
+ end
+
+ return true
+end
+
+local function onTradeRequest(npc, creature)
+ local player = Player(creature)
+ if not player then
+ return false
+ end
+ local playerId = player:getId()
+
+ if player:getStorageValue(Storage.AdventurersGuild.TheLostBrother) ~= 3 then
+ return false
+ end
+
+ return true
+end
+
+npcHandler:setMessage(MESSAGE_GREET, "Greetings!")
+npcHandler:setMessage(MESSAGE_FAREWELL, "Farewell.")
+npcHandler:setMessage(MESSAGE_SENDTRADE, "Of course, just have a look.")
+npcHandler:setCallback(CALLBACK_ON_TRADE_REQUEST, onTradeRequest)
+npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
+npcHandler:setMessage(MESSAGE_WALKAWAY, "Farewell.")
npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
npcConfig.shop = {
diff --git a/data-otservbr-global/npc/tesha.lua b/data-otservbr-global/npc/tesha.lua
index 95e31f97cb7..6a3c4b9fadb 100644
--- a/data-otservbr-global/npc/tesha.lua
+++ b/data-otservbr-global/npc/tesha.lua
@@ -98,7 +98,7 @@ npcConfig.shop = {
{ itemName = "diamond", clientId = 32770, sell = 15000 },
{ itemName = "dragon figurine", clientId = 30053, sell = 45000 },
{ itemName = "gemmed figurine", clientId = 24392, sell = 3500 },
- { itemName = "giant amethyst", clientId = 30061, sell = 60000 },
+ { itemName = "giant amethyst", clientId = 32622, sell = 60000 },
{ itemName = "giant emerald", clientId = 30060, sell = 90000 },
{ itemName = "giant ruby", clientId = 30059, sell = 70000 },
{ itemName = "giant sapphire", clientId = 30061, sell = 50000 },
diff --git a/data-otservbr-global/npc/tezila.lua b/data-otservbr-global/npc/tezila.lua
index dab92c807f5..fe667133ad7 100644
--- a/data-otservbr-global/npc/tezila.lua
+++ b/data-otservbr-global/npc/tezila.lua
@@ -67,7 +67,7 @@ npcConfig.shop = {
{ itemName = "cyan crystal fragment", clientId = 16125, sell = 800 },
{ itemName = "dragon figurine", clientId = 30053, sell = 45000 },
{ itemName = "gemmed figurine", clientId = 24392, sell = 3500 },
- { itemName = "giant amethyst", clientId = 30061, sell = 60000 },
+ { itemName = "giant amethyst", clientId = 32622, sell = 60000 },
{ itemName = "giant emerald", clientId = 30060, sell = 90000 },
{ itemName = "giant ruby", clientId = 30059, sell = 70000 },
{ itemName = "giant sapphire", clientId = 30061, sell = 50000 },
diff --git a/data-otservbr-global/npc/topsy.lua b/data-otservbr-global/npc/topsy.lua
index 395fd23cbeb..66ea6f27e6a 100644
--- a/data-otservbr-global/npc/topsy.lua
+++ b/data-otservbr-global/npc/topsy.lua
@@ -78,6 +78,20 @@ local itemsTable = {
{ itemName = "wand of dragonbreath", clientId = 3075, buy = 1000 },
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
+ ["shields"] = {
+ { itemName = "spellbook", clientId = 3059, buy = 150 },
+ },
}
npcConfig.shop = {}
diff --git a/data-otservbr-global/npc/valindara.lua b/data-otservbr-global/npc/valindara.lua
index 69655358dfe..cf9506fcdd5 100644
--- a/data-otservbr-global/npc/valindara.lua
+++ b/data-otservbr-global/npc/valindara.lua
@@ -111,7 +111,7 @@ npcConfig.shop = {
{ itemName = "fire wall rune", clientId = 3190, buy = 61 },
{ itemName = "fireball rune", clientId = 3189, buy = 30 },
{ itemName = "gemmed figurine", clientId = 24392, sell = 3500 },
- { itemName = "giant amethyst", clientId = 30061, sell = 60000 },
+ { itemName = "giant amethyst", clientId = 32622, sell = 60000 },
{ itemName = "giant emerald", clientId = 30060, sell = 90000 },
{ itemName = "giant ruby", clientId = 30059, sell = 70000 },
{ itemName = "giant sapphire", clientId = 30061, sell = 50000 },
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/npc/xodet.lua b/data-otservbr-global/npc/xodet.lua
index 2d8832bd964..f687b491e89 100644
--- a/data-otservbr-global/npc/xodet.lua
+++ b/data-otservbr-global/npc/xodet.lua
@@ -72,6 +72,20 @@ local itemsTable = {
{ itemName = "wand of dragonbreath", clientId = 3075, buy = 1000 },
{ itemName = "wand of vortex", clientId = 3074, buy = 500 },
},
+ ["exercise weapons"] = {
+ { itemName = "durable exercise rod", clientId = 35283, buy = 945000, count = 1800 },
+ { itemName = "durable exercise wand", clientId = 35284, buy = 945000, count = 1800 },
+ { itemName = "exercise rod", clientId = 28556, buy = 262500, count = 500 },
+ { itemName = "exercise wand", clientId = 28557, buy = 262500, count = 500 },
+ { itemName = "lasting exercise rod", clientId = 35289, buy = 7560000, count = 14400 },
+ { itemName = "lasting exercise wand", clientId = 35290, buy = 7560000, count = 14400 },
+ },
+ ["others"] = {
+ { itemName = "spellwand", clientId = 651, sell = 299 },
+ },
+ ["shields"] = {
+ { itemName = "spellbook", clientId = 3059, buy = 150 },
+ },
}
npcConfig.shop = {}
diff --git a/data-otservbr-global/npc/yasir.lua b/data-otservbr-global/npc/yasir.lua
index d576b611e53..9c5cf3dbf69 100644
--- a/data-otservbr-global/npc/yasir.lua
+++ b/data-otservbr-global/npc/yasir.lua
@@ -60,6 +60,7 @@ npcConfig.shop = {
{ itemName = "ape fur", clientId = 5883, sell = 120 },
{ itemName = "apron", clientId = 33933, sell = 1300 },
{ itemName = "badger fur", clientId = 903, sell = 15 },
+ { itemName = "bakragore's amalgamation", clientId = 43968, sell = 2000000 },
{ itemName = "bamboo stick", clientId = 11445, sell = 30 },
{ itemName = "banana sash", clientId = 11511, sell = 55 },
{ itemName = "basalt fetish", clientId = 17856, sell = 210 },
@@ -75,6 +76,7 @@ npcConfig.shop = {
{ itemName = "black hood", clientId = 9645, sell = 190 },
{ itemName = "black wool", clientId = 11448, sell = 300 },
{ itemName = "blazing bone", clientId = 16131, sell = 610 },
+ { itemName = "bloated maggot", clientId = 43856, sell = 5200 },
{ itemName = "blood preservation", clientId = 11449, sell = 320 },
{ itemName = "blood tincture in a vial", clientId = 18928, sell = 360 },
{ itemName = "bloody dwarven beard", clientId = 17827, sell = 110 },
@@ -173,7 +175,11 @@ npcConfig.shop = {
{ itemName = "dandelion seeds", clientId = 25695, sell = 200 },
{ itemName = "dangerous proto matter", clientId = 23515, sell = 300 },
{ itemName = "dark bell", clientId = 32596, sell = 310000 },
+ { itemName = "dark obsidian splinter", clientId = 43850, sell = 4400 },
{ itemName = "dark rosary", clientId = 10303, sell = 48 },
+ { itemName = "darklight core", clientId = 43853, sell = 4100 },
+ { itemName = "darklight figurine", clientId = 43961, sell = 3400000 },
+ { itemName = "darklight matter", clientId = 43851, sell = 5500 },
{ itemName = "dead weight", clientId = 20202, sell = 450 },
{ itemName = "deepling breaktime snack", clientId = 14011, sell = 90 },
{ itemName = "deepling claw", clientId = 14044, sell = 430 },
@@ -229,6 +235,7 @@ npcConfig.shop = {
{ itemName = "falcon crest", clientId = 28823, sell = 650 },
{ itemName = "fern", clientId = 3737, sell = 20 },
{ itemName = "fiery heart", clientId = 9636, sell = 375 },
+ { itemName = "fiery tear", clientId = 39040, sell = 1070000 },
{ itemName = "fig leaf", clientId = 25742, sell = 200 },
{ itemName = "figurine of cruelty", clientId = 34019, sell = 3100000 },
{ itemName = "figurine of greed", clientId = 34021, sell = 2900000 },
@@ -480,6 +487,8 @@ npcConfig.shop = {
{ itemName = "rorc feather", clientId = 18993, sell = 70 },
{ itemName = "rotten heart", clientId = 31589, sell = 74000 },
{ itemName = "rotten piece of cloth", clientId = 10291, sell = 30 },
+ { itemName = "rotten roots", clientId = 43849, sell = 3800 },
+ { itemName = "rotten vermin ichor", clientId = 43847, sell = 4500 },
{ itemName = "sabretooth", clientId = 10311, sell = 400 },
{ itemName = "sabretooth fur", clientId = 39378, sell = 2500 },
{ itemName = "safety pin", clientId = 11493, sell = 120 },
@@ -636,6 +645,7 @@ npcConfig.shop = {
{ itemName = "wolf paw", clientId = 5897, sell = 70 },
{ itemName = "wood", clientId = 5901, sell = 5 },
{ itemName = "wool", clientId = 10319, sell = 15 },
+ { itemName = "worm sponge", clientId = 43848, sell = 4200 },
{ itemName = "writhing brain", clientId = 32600, sell = 370000 },
{ itemName = "writhing heart", clientId = 32599, sell = 185000 },
{ itemName = "wyrm scale", clientId = 9665, sell = 400 },
diff --git a/data-otservbr-global/npc/yonan.lua b/data-otservbr-global/npc/yonan.lua
index 44dbb8dd83c..69bddee5cb3 100644
--- a/data-otservbr-global/npc/yonan.lua
+++ b/data-otservbr-global/npc/yonan.lua
@@ -39,7 +39,7 @@ npcConfig.shop = {
{ itemName = "cyan crystal fragment", clientId = 16125, sell = 800 },
{ itemName = "dragon figurine", clientId = 30053, sell = 45000 },
{ itemName = "gemmed figurine", clientId = 24392, sell = 3500 },
- { itemName = "giant amethyst", clientId = 30061, sell = 60000 },
+ { itemName = "giant amethyst", clientId = 32622, sell = 60000 },
{ itemName = "giant emerald", clientId = 30060, sell = 90000 },
{ itemName = "giant ruby", clientId = 30059, sell = 70000 },
{ itemName = "giant sapphire", clientId = 30061, sell = 50000 },
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/actions/quests/adventurers_guild/warrior_skeleton.lua b/data-otservbr-global/scripts/actions/quests/adventurers_guild/warrior_skeleton.lua
index a1aec80aaee..73a5d3f8acb 100644
--- a/data-otservbr-global/scripts/actions/quests/adventurers_guild/warrior_skeleton.lua
+++ b/data-otservbr-global/scripts/actions/quests/adventurers_guild/warrior_skeleton.lua
@@ -3,6 +3,11 @@ function adventurersWarriorSkeleton.onUse(player, item, fromPosition, target, to
if player:getStorageValue(Storage.AdventurersGuild.GreatDragonHunt.WarriorSkeleton) < 1 then
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have discovered a deceased warrior's skeleton. It seems he tried to hunt the dragons around here - and failed.")
player:addItem(5882, 1) -- red dragon scale
+
+ if player:getStorageValue(Storage.AdventurersGuild.QuestLine) < 1 then
+ player:setStorageValue(Storage.AdventurersGuild.QuestLine, 1)
+ end
+
player:setStorageValue(Storage.AdventurersGuild.GreatDragonHunt.WarriorSkeleton, 1)
player:setStorageValue(Storage.AdventurersGuild.GreatDragonHunt.DragonCounter, 0)
else
diff --git a/data-otservbr-global/scripts/actions/quests/adventures_of_galthen/galthens_tree.lua b/data-otservbr-global/scripts/actions/quests/adventures_of_galthen/galthens_tree.lua
new file mode 100644
index 00000000000..441b91fa345
--- /dev/null
+++ b/data-otservbr-global/scripts/actions/quests/adventures_of_galthen/galthens_tree.lua
@@ -0,0 +1,19 @@
+local galthensTree = Action()
+function galthensTree.onUse(player, item, fromPosition, target, toPosition, isHotkey)
+ local hasExhaustion, message = player:kv():get("galthens-satchel") or 0, "Empty."
+ if hasExhaustion < os.time() then
+ local container = player:addItem(36813)
+ container:addItem(36810, 1)
+ player:kv():set("galthens-satchel", os.time() + 30 * 24 * 60 * 60)
+ message = "You have found a galthens satchel."
+ end
+
+ player:teleportTo(Position(32396, 32520, 7))
+ player:getPosition():sendMagicEffect(CONST_ME_WATERSPLASH)
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, message)
+
+ return true
+end
+
+galthensTree:position(Position(32366, 32542, 8))
+galthensTree:register()
diff --git a/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_brain_head.lua b/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_brain_head.lua
index 725359eb64a..aebc0b3ca7a 100644
--- a/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_brain_head.lua
+++ b/data-otservbr-global/scripts/actions/quests/feaster_of_souls/portal_brain_head.lua
@@ -103,26 +103,26 @@ function teleportBoss.onStepIn(creature, item, position, fromPosition)
end
local player = creature
if player:getLevel() < config.requiredLevel then
- player:teleportTo(fromPosition, true)
+ player:teleportTo(config.exitPosition, true)
player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need to be level " .. config.requiredLevel .. " or higher.")
return true
end
if locked then
- player:teleportTo(fromPosition, true)
+ player:teleportTo(config.exitPosition, true)
player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There's already someone fighting with " .. config.bossName .. ".")
return false
end
if zone:countPlayers(IgnoredByMonsters) >= 5 then
- player:teleportTo(fromPosition, true)
+ player:teleportTo(config.exitPosition, true)
player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The boss room is full.")
return false
end
local timeLeft = player:getBossCooldown(config.bossName) - os.time()
if timeLeft > 0 then
- player:teleportTo(fromPosition, true)
+ player:teleportTo(config.exitPosition, true)
player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have to wait " .. getTimeInWords(timeLeft) .. " to face " .. config.bossName .. " again!")
player:getPosition():sendMagicEffect(CONST_ME_POFF)
diff --git a/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua b/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua
index ac8f4506c70..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 },
@@ -275,8 +276,10 @@ local questTable = {
{ storage = Storage.Quest.U11_40.ThreatenedDreams.QuestLine, storageValue = 1 },
{ storage = Storage.Quest.U11_40.ThreatenedDreams.Mission01[1], storageValue = 16 },
{ storage = Storage.Quest.U11_40.ThreatenedDreams.Mission02.KroazurAccess, storageValue = 1 },
+ { storage = Storage.AdventurersGuild.QuestLine, storageValue = 1 },
{ storage = Storage.AdventurersGuild.GreatDragonHunt.WarriorSkeleton, storageValue = 1 },
{ storage = Storage.AdventurersGuild.GreatDragonHunt.WarriorSkeleton, storageValue = 2 },
+ { storage = Storage.AdventurersGuild.TheLostBrother, storageValue = 3 },
{ storage = Storage.Quest.U10_55.Dawnport.Questline, storageValue = 1 },
{ storage = Storage.Quest.U10_55.Dawnport.GoMain, storageValue = 1 },
{ storage = Storage.ForgottenKnowledge.AccessDeath, storageValue = 1 },
@@ -312,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 },
@@ -370,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/creaturescripts/quests/dark_trails/kill_the_ravager.lua b/data-otservbr-global/scripts/creaturescripts/quests/dark_trails/kill_the_ravager.lua
index 97798dadfeb..f11bfa41efe 100644
--- a/data-otservbr-global/scripts/creaturescripts/quests/dark_trails/kill_the_ravager.lua
+++ b/data-otservbr-global/scripts/creaturescripts/quests/dark_trails/kill_the_ravager.lua
@@ -7,7 +7,7 @@ local function removeTeleport(position)
end
local theRavager = CreatureEvent("TheRavagerDeath")
-function theRavager.onDeath(player, creature)
+function theRavager.onDeath(creature)
local position = creature:getPosition()
position:sendMagicEffect(CONST_ME_TELEPORT)
local item = Game.createItem(1949, 1, { x = 33496, y = 32070, z = 8 })
diff --git a/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua b/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua
index b2ba681d251..f8f7ddc5c6b 100644
--- a/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua
+++ b/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua
@@ -57,7 +57,7 @@ function deathEvent.onDeath(creature, _corpse, _lastHitKiller, mostDamageKiller)
end
end
-- Minotaurs
- killCheck(player, targetName, Storage.KillingInTheNameOf.BudrikMinos, 0, tasks.Budrik[1].creatures, nil, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.MinotaurCount)
+ killCheck(player, targetName, Storage.KillingInTheNameOf.BudrikMinos, 0, tasks.Budrik[1].creatures, Storage.Quest.U8_5.KillingInTheNameOf.AltKillCount.MinotaurCount, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.MinotaurCount)
-- Necromancers and Priestesses
killCheck(player, targetName, Storage.KillingInTheNameOf.LugriNecromancers, 0, tasks.Lugri[1].creatures, Storage.Quest.U8_5.KillingInTheNameOf.AltKillCount.NecromancerCount, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.NecromancerCount)
killCheck(player, targetName, Storage.KillingInTheNameOf.LugriNecromancers, 3, tasks.Lugri[1].creatures, Storage.Quest.U8_5.KillingInTheNameOf.AltKillCount.NecromancerCount, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.NecromancerCount)
diff --git a/data-otservbr-global/scripts/creaturescripts/quests/liquid_black/deepling_boss_kill.lua b/data-otservbr-global/scripts/creaturescripts/quests/liquid_black/deepling_boss_kill.lua
index 6916f3dfa5a..49fb3e17c11 100644
--- a/data-otservbr-global/scripts/creaturescripts/quests/liquid_black/deepling_boss_kill.lua
+++ b/data-otservbr-global/scripts/creaturescripts/quests/liquid_black/deepling_boss_kill.lua
@@ -5,12 +5,11 @@ local bosses = {
}
local deeplingBosses = CreatureEvent("DeeplingBossDeath")
-function deeplingBosses.onDeath(player, creature)
+function deeplingBosses.onDeath(creature)
local bossConfig = bosses[creature:getName():lower()]
if not bossConfig then
return true
end
-
onDeathForDamagingPlayers(creature, function(creature, player)
if player:getStorageValue(Storage.DeeplingBosses.DeeplingStatus) < bossConfig.status then
player:setStorageValue(Storage.DeeplingBosses.DeeplingStatus, bossConfig.status)
diff --git a/data-otservbr-global/scripts/game_migrations/20241715984279_move_wheel_scrolls_from_storagename_to_kv.lua b/data-otservbr-global/scripts/game_migrations/20241715984279_move_wheel_scrolls_from_storagename_to_kv.lua
new file mode 100644
index 00000000000..a5cc9a123f4
--- /dev/null
+++ b/data-otservbr-global/scripts/game_migrations/20241715984279_move_wheel_scrolls_from_storagename_to_kv.lua
@@ -0,0 +1,24 @@
+local promotionScrolls = {
+ { oldScroll = "wheel.scroll.abridged", newScroll = "abridged" },
+ { oldScroll = "wheel.scroll.basic", newScroll = "basic" },
+ { oldScroll = "wheel.scroll.revised", newScroll = "revised" },
+ { oldScroll = "wheel.scroll.extended", newScroll = "extended" },
+ { oldScroll = "wheel.scroll.advanced", newScroll = "advanced" },
+}
+
+local function migrate(player)
+ for _, scrollTable in ipairs(promotionScrolls) do
+ local oldStorage = player:getStorageValueByName(scrollTable.oldScroll)
+ if oldStorage > 0 then
+ player:kv():scoped("wheel-of-destiny"):scoped("scrolls"):set(scrollTable.newScroll, true)
+ end
+ end
+end
+
+local migration = Migration("20241715984279_move_wheel_scrolls_from_storagename_to_kv")
+
+function migration:onExecute()
+ self:forEachPlayer(migrate)
+end
+
+migration: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-otservbr-global/scripts/quests/the_lost_brother/movement-find-remains.lua b/data-otservbr-global/scripts/quests/the_lost_brother/movement-find-remains.lua
new file mode 100644
index 00000000000..c2ddb8efcb7
--- /dev/null
+++ b/data-otservbr-global/scripts/quests/the_lost_brother/movement-find-remains.lua
@@ -0,0 +1,19 @@
+local findRemains = MoveEvent()
+
+function findRemains.onStepIn(creature, item, position, fromPosition)
+ local player = creature:getPlayer()
+ if not player then
+ return true
+ end
+
+ if player:getStorageValue(Storage.AdventurersGuild.TheLostBrother) == 1 then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You stumble over some old bones. Something is carved into the stone wall here: 'Tarun, my brother, you were right. She's evil.'")
+ player:setStorageValue(Storage.AdventurersGuild.TheLostBrother, 2)
+ player:getPosition():sendMagicEffect(CONST_ME_MAGIC_GREEN)
+ end
+
+ return true
+end
+
+findRemains:position(Position(32959, 32674, 4))
+findRemains:register()
diff --git a/data-otservbr-global/scripts/spells/monster/doctor_marrow_explosion.lua b/data-otservbr-global/scripts/spells/monster/doctor_marrow_explosion.lua
index f616e8b8cee..491d9f2516d 100644
--- a/data-otservbr-global/scripts/spells/monster/doctor_marrow_explosion.lua
+++ b/data-otservbr-global/scripts/spells/monster/doctor_marrow_explosion.lua
@@ -21,7 +21,7 @@ end
local spell = Spell("instant")
function onTargetCreature(creature, target)
- if not targetPos then
+ if not target then
return true
end
local master = target:getMaster()
@@ -29,7 +29,7 @@ function onTargetCreature(creature, target)
return true
end
- local distance = math.floor(targetPos:getDistance(target:getPosition()))
+ local distance = math.floor(creature:getPosition():getDistance(target:getPosition()))
local actualDamage = damage / (2 ^ distance)
doTargetCombatHealth(0, target, COMBAT_EARTHDAMAGE, actualDamage, actualDamage, CONST_ME_NONE)
if crit then
@@ -63,21 +63,26 @@ function spell.onCastSpell(creature, var)
end, i * 100, targetPos)
end
- addEvent(function(cid)
+ addEvent(function(cid, pos)
local creature = Creature(cid)
- creature:getPosition():sendMagicEffect(CONST_ME_ORANGE_ENERGY_SPARK)
- targetPos:sendMagicEffect(CONST_ME_ORANGETELEPORT)
- end, 2000, creature:getId())
+ if creature then
+ creature:getPosition():sendMagicEffect(CONST_ME_ORANGE_ENERGY_SPARK)
+ pos:sendMagicEffect(CONST_ME_ORANGETELEPORT)
+ end
+ end, 2000, creature:getId(), targetPos)
addEvent(function(cid, pos)
- damage = -math.random(3500, 7000)
- if math.random(1, 100) <= 10 then
- crit = true
- damage = damage * 1.5
- else
- crit = false
+ local creature = Creature(cid)
+ if creature then
+ damage = -math.random(3500, 7000)
+ if math.random(1, 100) <= 10 then
+ crit = true
+ damage = damage * 1.5
+ else
+ crit = false
+ end
+ spellCombat:execute(creature, Variant(pos))
end
- spellCombat:execute(creature, Variant(pos))
end, totalDelay, creature:getId(), targetPos)
return true
end
diff --git a/data-otservbr-global/world/otservbr-monster.xml b/data-otservbr-global/world/otservbr-monster.xml
index d3bbc69e858..974c2ea6809 100644
--- a/data-otservbr-global/world/otservbr-monster.xml
+++ b/data-otservbr-global/world/otservbr-monster.xml
@@ -4076,7 +4076,6 @@
-
@@ -4122,9 +4121,6 @@
-
-
-
@@ -6928,19 +6924,13 @@
-
-
-
-
-
-
@@ -6953,9 +6943,6 @@
-
-
-
@@ -6963,7 +6950,6 @@
-
@@ -6985,15 +6971,11 @@
-
-
-
-
@@ -7009,9 +6991,6 @@
-
-
-
@@ -63284,9 +63263,6 @@
-
-
-
@@ -63491,9 +63467,6 @@
-
-
-
@@ -96125,15 +96098,15 @@
+
+
+
-
-
-
@@ -96718,6 +96691,9 @@
+
+
+
@@ -96725,9 +96701,6 @@
-
-
-
@@ -118889,12 +118862,12 @@
-
-
-
+
+
+
diff --git a/data-otservbr-global/world/otservbr-npc.xml b/data-otservbr-global/world/otservbr-npc.xml
index 10772902341..f97bc118fdc 100644
--- a/data-otservbr-global/world/otservbr-npc.xml
+++ b/data-otservbr-global/world/otservbr-npc.xml
@@ -2994,5 +2994,8 @@
-
+
+
+
+
diff --git a/data/events/scripts/player.lua b/data/events/scripts/player.lua
index 35e0bb17e7f..ec1a92f3528 100644
--- a/data/events/scripts/player.lua
+++ b/data/events/scripts/player.lua
@@ -140,8 +140,8 @@ local function useStaminaXpBoost(player)
return false
end
- local staminaMinutes = player:getExpBoostStamina() / 60
- if staminaMinutes == 0 then
+ local xpBoostMinutes = player:getXpBoostTime() / 60
+ if xpBoostMinutes == 0 then
return
end
@@ -156,18 +156,26 @@ local function useStaminaXpBoost(player)
return
end
+ local xpBoostLeftMinutesByDailyReward = player:kv():get("daily-reward-xp-boost") or 0
if timePassed > 60 then
- if staminaMinutes > 2 then
- staminaMinutes = staminaMinutes - 2
+ if xpBoostMinutes > 2 then
+ xpBoostMinutes = xpBoostMinutes - 2
+ if xpBoostLeftMinutesByDailyReward > 2 then
+ player:kv():set("daily-reward-xp-boost", xpBoostLeftMinutesByDailyReward - 2)
+ end
else
- staminaMinutes = 0
+ xpBoostMinutes = 0
+ player:kv():remove("daily-reward-xp-boost")
end
_G.NextUseXpStamina[playerId] = currentTime + 120
else
- staminaMinutes = staminaMinutes - 1
+ xpBoostMinutes = xpBoostMinutes - 1
+ if xpBoostLeftMinutesByDailyReward > 0 then
+ player:kv():set("daily-reward-xp-boost", xpBoostLeftMinutesByDailyReward - 1)
+ end
_G.NextUseXpStamina[playerId] = currentTime + 60
end
- player:setExpBoostStamina(staminaMinutes * 60)
+ player:setXpBoostTime(xpBoostMinutes * 60)
end
local function useConcoctionTime(player)
@@ -519,14 +527,14 @@ function Player:onGainExperience(target, exp, rawExp)
self:addCondition(soulCondition)
end
- -- Store Bonus
- useStaminaXpBoost(self) -- Use store boost stamina
+ -- XP Boost Bonus
+ useStaminaXpBoost(self) -- Use stamina XP boost (store or daily reward)
- local Boost = self:getExpBoostStamina()
- local stillHasBoost = Boost > 0
- local storeXpBoostAmount = stillHasBoost and self:getStoreXpBoost() or 0
+ local xpBoostTimeLeft = self:getXpBoostTime()
+ local stillHasXpBoost = xpBoostTimeLeft > 0
+ local xpBoostPercent = stillHasXpBoost and self:getXpBoostPercent() or 0
- self:setStoreXpBoost(storeXpBoostAmount)
+ self:setXpBoostPercent(xpBoostPercent)
-- Stamina Bonus
local staminaBonusXp = 1
@@ -564,7 +572,7 @@ function Player:onGainExperience(target, exp, rawExp)
local lowLevelBonuxExp = self:getFinalLowLevelBonus()
local baseRate = self:getFinalBaseRateExperience()
- return (exp + (exp * (storeXpBoostAmount / 100) + (exp * (lowLevelBonuxExp / 100)))) * staminaBonusXp * baseRate
+ return (exp + (exp * (xpBoostPercent / 100) + (exp * (lowLevelBonuxExp / 100)))) * staminaBonusXp * baseRate
end
function Player:onLoseExperience(exp)
diff --git a/data/items/items.xml b/data/items/items.xml
index 0bc1c782452..9fdc89bbd8f 100644
--- a/data/items/items.xml
+++ b/data/items/items.xml
@@ -53105,6 +53105,9 @@ hands of its owner. Granted by TibiaRoyal.com"/>
+
+
+
-
@@ -64463,8 +64466,8 @@ hands of its owner. Granted by TibiaRoyal.com"/>
+
-
@@ -74870,11 +74873,14 @@ Granted by TibiaGoals.com"/>
-
-
+
+
+
+
-
@@ -74991,6 +74997,15 @@ Granted by TibiaGoals.com"/>
+ -
+
+
+
+
+
+
+
+
-
@@ -75006,6 +75021,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75029,6 +75047,12 @@ Granted by TibiaGoals.com"/>
+
+
+
+
+
+
@@ -75052,6 +75076,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75075,6 +75102,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75098,6 +75128,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75121,6 +75154,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75146,6 +75182,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75170,6 +75209,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75194,6 +75236,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75218,6 +75263,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75242,6 +75290,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75266,6 +75317,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75282,6 +75336,14 @@ Granted by TibiaGoals.com"/>
+
+
+
+
+
+
+
+
@@ -75308,6 +75370,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75335,6 +75400,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75362,6 +75430,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75389,6 +75460,9 @@ Granted by TibiaGoals.com"/>
+
+
+
@@ -75404,6 +75478,14 @@ Granted by TibiaGoals.com"/>
+
+
+
+
+
+
+
+
@@ -75419,6 +75501,7 @@ Granted by TibiaGoals.com"/>
+
@@ -75426,6 +75509,10 @@ Granted by TibiaGoals.com"/>
+
+
+
+
@@ -75446,6 +75533,7 @@ Granted by TibiaGoals.com"/>
+
@@ -75454,6 +75542,10 @@ Granted by TibiaGoals.com"/>
+
+
+
+
@@ -75478,6 +75570,17 @@ Granted by TibiaGoals.com"/>
+
+
+
+
+
+
+
+
+
+
+
@@ -75489,6 +75592,7 @@ Granted by TibiaGoals.com"/>
+
@@ -75500,6 +75604,10 @@ Granted by TibiaGoals.com"/>
+
+
+
+
@@ -75519,6 +75627,7 @@ Granted by TibiaGoals.com"/>
+
@@ -75528,6 +75637,10 @@ Granted by TibiaGoals.com"/>
+
+
+
+
@@ -75552,6 +75665,17 @@ Granted by TibiaGoals.com"/>
+
+
+
+
+
+
+
+
+
+
+
@@ -75696,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/functions.lua b/data/libs/functions/functions.lua
index 9eb1b0b57b5..c2349ce7fd6 100644
--- a/data/libs/functions/functions.lua
+++ b/data/libs/functions/functions.lua
@@ -67,17 +67,28 @@ end
function getTimeInWords(secsParam)
local secs = tonumber(secsParam)
+ local days = math.floor(secs / (24 * 3600))
+ secs = secs - (days * 24 * 3600)
local hours, minutes, seconds = getHours(secs), getMinutes(secs), getSeconds(secs)
local timeStr = ""
+ if days > 0 then
+ timeStr = days .. (days > 1 and " days" or " day")
+ end
+
if hours > 0 then
- timeStr = hours .. (hours > 1 and " hours" or " hour")
+ if timeStr ~= "" then
+ timeStr = timeStr .. ", "
+ end
+
+ timeStr = timeStr .. hours .. (hours > 1 and " hours" or " hour")
end
if minutes > 0 then
if timeStr ~= "" then
timeStr = timeStr .. ", "
end
+
timeStr = timeStr .. minutes .. (minutes > 1 and " minutes" or " minute")
end
@@ -85,9 +96,9 @@ function getTimeInWords(secsParam)
if timeStr ~= "" then
timeStr = timeStr .. " and "
end
+
timeStr = timeStr .. seconds .. (seconds > 1 and " seconds" or " second")
end
-
return timeStr
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 1f0e805704c..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
@@ -476,8 +476,15 @@ function Player.selectDailyReward(self, msg)
end
dailyRewardMessage = "Picked items: " .. description
elseif dailyTable.type == DAILY_REWARD_TYPE_XP_BOOST then
- self:setExpBoostStamina(self:getExpBoostStamina() + (rewardCount * 60))
- self:setStoreXpBoost(50)
+ local rewardCountReviewed = rewardCount
+ local xpBoostLeftMinutes = self:kv():get("daily-reward-xp-boost") or 0
+ if xpBoostLeftMinutes > 0 then
+ rewardCountReviewed = rewardCountReviewed - xpBoostLeftMinutes
+ end
+
+ self:setXpBoostTime(self:getXpBoostTime() + (rewardCountReviewed * 60))
+ self:kv():set("daily-reward-xp-boost", rewardCount)
+ self:setXpBoostPercent(50)
dailyRewardMessage = "Picked reward: XP Bonus for " .. rewardCount .. " minutes."
elseif dailyTable.type == DAILY_REWARD_TYPE_PREY_REROLL then
self:addPreyCards(rewardCount)
diff --git a/data/modules/scripts/gamestore/init.lua b/data/modules/scripts/gamestore/init.lua
index e53bab813f0..322a7749c93 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)
@@ -479,7 +480,7 @@ function parseBuyStoreOffer(playerId, msg)
elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_SEXCHANGE then
GameStore.processSexChangePurchase(player)
elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_EXPBOOST then
- GameStore.processExpBoostPuchase(player)
+ GameStore.processExpBoostPurchase(player)
elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HUNTINGSLOT then
GameStore.processTaskHuntingThirdSlot(player)
elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_PREYSLOT then
@@ -532,19 +533,33 @@ function parseBuyStoreOffer(playerId, msg)
sendUpdatedStoreBalances(playerId)
return addPlayerEvent(sendStorePurchaseSuccessful, 650, playerId, message)
end
+
+ player:updateUIExhausted()
return true
end
-- Both functions use same formula!
function parseOpenTransactionHistory(playerId, msg)
+ local player = Player(playerId)
+ if not player then
+ return
+ end
+
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 player = Player(playerId)
+ if not player then
+ return
+ end
+
local page = msg:getU32()
sendStoreTransactionHistory(playerId, page + 1, GameStore.DefaultValues.DEFAULT_VALUE_ENTRIES_PER_PAGE)
+ player:updateUIExhausted()
end
local function getCategoriesRook()
@@ -732,7 +747,7 @@ function Player.canBuyOffer(self, offer)
disabled = 1
disabledReason = "You can't buy XP Boost for today."
end
- if self:getExpBoostStamina() > 0 then
+ if self:getXpBoostTime() > 0 then
disabled = 1
disabledReason = "You already have an active XP boost."
end
@@ -1742,12 +1757,12 @@ function GameStore.processSexChangePurchase(player)
player:toggleSex()
end
-function GameStore.processExpBoostPuchase(player)
- local currentExpBoostTime = player:getExpBoostStamina()
+function GameStore.processExpBoostPurchase(player)
+ local currentXpBoostTime = player:getXpBoostTime()
local expBoostCount = player:getStorageValue(GameStore.Storages.expBoostCount)
- player:setStoreXpBoost(50)
- player:setExpBoostStamina(currentExpBoostTime + 3600)
+ player:setXpBoostPercent(50)
+ player:setXpBoostTime(currentXpBoostTime + 3600)
if expBoostCount == -1 or expBoostCount == 6 then
expBoostCount = 1
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/scripts/actions/items/exercise_training_weapons.lua b/data/scripts/actions/items/exercise_training_weapons.lua
index 4a72f19e5c8..3c62d7c1183 100644
--- a/data/scripts/actions/items/exercise_training_weapons.lua
+++ b/data/scripts/actions/items/exercise_training_weapons.lua
@@ -133,7 +133,7 @@ end
local exerciseTraining = Action()
function exerciseTraining.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if not target or not target:getId() then
+ if not target or type(target) == "table" or not target:getId() then
return true
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/items/wheel_scrolls.lua b/data/scripts/actions/items/wheel_scrolls.lua
index b42339a706e..61aaa6fe40a 100644
--- a/data/scripts/actions/items/wheel_scrolls.lua
+++ b/data/scripts/actions/items/wheel_scrolls.lua
@@ -1,9 +1,9 @@
local promotionScrolls = {
- [43946] = { storageName = "wheel.scroll.abridged", points = 3, name = "abridged promotion scroll" },
- [43947] = { storageName = "wheel.scroll.basic", points = 5, name = "basic promotion scroll" },
- [43948] = { storageName = "wheel.scroll.revised", points = 9, name = "revised promotion scroll" },
- [43949] = { storageName = "wheel.scroll.extended", points = 13, name = "extended promotion scroll" },
- [43950] = { storageName = "wheel.scroll.advanced", points = 20, name = "advanced promotion scroll" },
+ [43946] = { name = "abridged", points = 3, itemName = "abridged promotion scroll" },
+ [43947] = { name = "basic", points = 5, itemName = "basic promotion scroll" },
+ [43948] = { name = "revised", points = 9, itemName = "revised promotion scroll" },
+ [43949] = { name = "extended", points = 13, itemName = "extended promotion scroll" },
+ [43950] = { name = "advanced", points = 20, itemName = "advanced promotion scroll" },
}
local scroll = Action()
@@ -15,13 +15,14 @@ function scroll.onUse(player, item, fromPosition, target, toPosition, isHotkey)
end
local scrollData = promotionScrolls[item:getId()]
- if player:getStorageValueByName(scrollData.storageName) == 1 then
+ local scrollKV = player:kv():scoped("wheel-of-destiny"):scoped("scrolls")
+ if scrollKV:get(scrollData.name) then
player:sendTextMessage(MESSAGE_LOOK, "You have already deciphered this scroll.")
return true
end
- player:setStorageValueByName(scrollData.storageName, 1)
- player:sendTextMessage(MESSAGE_LOOK, "You have gained " .. scrollData.points .. " promotion points for the Wheel of Destiny by deciphering the " .. scrollData.name .. ".")
+ scrollKV:set(scrollData.name, true)
+ player:sendTextMessage(MESSAGE_LOOK, "You have gained " .. scrollData.points .. " promotion points for the Wheel of Destiny by deciphering the " .. scrollData.itemName .. ".")
item:remove(1)
return true
end
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 bdafcb41b33..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`
@@ -47,6 +47,7 @@ Event callbacks are available for several categories of game entities, such as `
- `(void)` `playerOnCombat`
- `(void)` `playerOnInventoryUpdate`
- `(bool)` `playerOnRotateItem`
+- `(void)` `playerOnWalk`
- `(void)` `monsterOnDropLoot`
- `(void)` `monsterPostDropLoot`
- `(void)` `monsterOnSpawn`
@@ -65,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()
@@ -130,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()
@@ -145,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/lib/register_achievements.lua b/data/scripts/lib/register_achievements.lua
index 18a2d6e41ef..23d4bbd93ca 100644
--- a/data/scripts/lib/register_achievements.lua
+++ b/data/scripts/lib/register_achievements.lua
@@ -58,7 +58,7 @@ ACHIEVEMENTS = {
[57] = { name = "Annihilator", grade = 2, points = 5, description = "You've daringly jumped into the infamous Annihilator and survived - taking home fame, glory and your reward." },
[58] = { name = "Master of the Nexus", grade = 2, points = 6, description = "You were able to fight your way through the countless hordes in the Demon Forge. Once more you proved that nothing is impossible." },
[59] = { name = "Talented Dancer", grade = 1, points = 1, description = "You're a lord or lady of the dance - and not afraid to use your skills to impress tribal gods. One step to the left, one jump to the right, twist and shout!" },
- [60] = { name = "Ministrel", grade = 1, points = 2, secret = true, description = "You can handle any music instrument you're given - and actually manage to produce a pleasant sound with it. You're a welcome guest and entertainer in most taverns." },
+ [60] = { name = "Allow Cookies?", grade = 1, points = 2, description = "With a perfectly harmless smile, you tricked all the funny guys into eating your exploding cookies. Next time you pull this prank, consider wearing a Boy Scout outfit to make it even better." },
[61] = { name = "Ruthless", grade = 2, points = 5, description = "You've touched all thrones of the Ruthless Seven and absorbed some of their evil spirit. It may have changed you forever." },
[62] = { name = "Champion of Chazorai", grade = 2, points = 4, description = "You won the merciless 2 vs. 2 team tournament on the Isle of Strife and wiped out wave after wave of fearsome opponents. Death or victory - you certainly chose the latter." },
[63] = { name = "Wayfarer", grade = 1, points = 3, secret = true, description = "Dragon dreams are golden." },
@@ -193,7 +193,7 @@ ACHIEVEMENTS = {
[192] = { name = "I Like it Fancy", grade = 1, points = 1, secret = true, description = "You definitely know how to bring out the best in your furniture and decoration pieces. Beautiful." },
[193] = { name = "Skin-Deep", grade = 2, points = 4, secret = true, description = "You always carry your obsidian knife with you and won't hesitate to use it. You've skinned countless little - and bigger - critters and yeah: they usually don't get any more beautiful on the inside. It's rather blood and gore and all that..." },
[194] = { name = "Ashes to Dust", grade = 2, points = 4, secret = true, description = "Staking vampires and demons has almost turned into your profession. You make sure to gather even the tiniest amount of evil dust particles. Beware of silicosis." },
- [195] = { name = "Silent Pet", grade = 1, points = 1, secret = true, description = "Awww. Your very own little goldfish friend - he's cute, he's shiny and he can't complain should you forget to feed him. He'll definitely brighten up your day!" },
+ -- [195] = Unknown/non-existent
[196] = { name = "Safely Stored Away", grade = 1, points = 2, secret = true, description = "Don't worry, no one will be able to take it from you. Probably." },
[197] = { name = "Something's in There", grade = 1, points = 1, secret = true, description = "By the gods! What was that?" },
[198] = { name = "Silent Pet", grade = 1, points = 1, secret = true, description = "Awww. Your very own little goldfish friend - he's cute, he's shiny and he can't complain should you forget to feed him. He'll definitely brighten up your day!" },
@@ -218,7 +218,7 @@ ACHIEVEMENTS = {
[217] = { name = "Doctor! Doctor!", grade = 1, points = 2, secret = true, description = "Did someone call a doctor? You delivered 100 medicine bags to Ottokar of the Venore poor house in times of dire need, well done!" },
[218] = { name = "Beak Doctor", grade = 2, points = 4, description = "You significantly helped the afflicted citizens of Venore in times of dire need. Somehow you still feel close to the victims of the fever outbreak. Your clothes make you one of them, one poor soul amongst the countless afflicted." },
[219] = { name = "Mystic Fabric Magic", grade = 2, points = 4, description = "You vanquished the mad mage, you subdued the raging mage - no spellweaving self-exposer can stand in your way. Yet you are quite absorbed in magical studies yourself. This very fabric reflects this personal approval of the magic arts." },
- [220] = { name = "Breaking the Ice", grade = 1, points = 1, description = "You almost made friends with Shardhead... before he died. Poor guy only seems to attract violence with his frosty attitude." },
+ -- [220] = Unknown/non-existent
[221] = { name = "Arachnoise", grade = 1, points = 1, description = "You've shattered each of Bloodweb's eight frozen legs. As they say: break a leg, and then some more." },
[222] = { name = "Rootless Behaviour", grade = 1, points = 1, description = "You've descended into the swampy depths of Deathbine's lair and made quick work of it." },
[223] = { name = "Twisted Mutation", grade = 1, points = 1, description = "You've slain Esmeralda, the most hideous and aggressive of the mutated rats. No one will know that you almost lost a finger in the process." },
@@ -248,7 +248,7 @@ ACHIEVEMENTS = {
[247] = { name = "Torn Treasures", grade = 1, points = 1, secret = true, description = "Wyda seems to be really, really bored. You also found out that she doesn't really need all those blood herbs that adventurers brought her. Still, she was nice enough to take one from you and gave you something quite cool in exchange." },
[248] = { name = "Loyal Subject", grade = 1, points = 1, secret = true, description = "You joined the Kingsday festivities and payed King Tibianus your respects. Now, off to party!" },
[249] = { name = "Desert Fisher", grade = 1, points = 1, description = "You managed to catch a fish in a surrounding that usually doesn't even carry water. Everything is subject to change, probably..." },
- [250] = { name = "Gem Cutter", grade = 1, points = 1, secret = true, description = 'You cut your first gem - and it bears your own name! Now that would be a nice gift! This does not make it a "true" Heart of the Sea, however...' },
+ -- [250] = Unknown/non-existent
[251] = { name = "Dog Sitter", grade = 1, points = 1, description = "You showed Noodles the way home. How long will it take this time until he's on the loose again? That dog must be really bored in the throne room by now." },
[252] = { name = "Ice Harvester", grade = 1, points = 1, description = "You witnessed the thawing of Svargrond and harvested rare seeds from some strange icy plants. They must be good for something." },
[253] = { name = "Preservationist", grade = 1, points = 1, secret = true, description = "You are a pretty smart thinker and managed to create everlasting flowers. They might become a big hit with all the people who aren't blessed with a green thumb or just forgetful." },
@@ -273,7 +273,7 @@ ACHIEVEMENTS = {
[272] = { name = "Headache", grade = 1, points = 2, description = "Even in the deepest structures of the hive, you began to strike against the mighty foe. Your actions probably already gave the hive a headache." },
[273] = { name = "Confusion", grade = 1, points = 3, description = "The destruction you have caused by now can be felt throughout the whole hive. The mayhem that follows your step caused significant confusion in the consciousness of the hive." },
[274] = { name = "Manic", grade = 2, points = 4, description = "You have destroyed a significant amount of the hive's vital nerve centers and caused massive destruction to the hive's awareness. You are probably causing the hive horrible nightmares." },
- [275] = { name = "Suppressor", grade = 2, points = 4, description = "A war is won by those who have the best supply of troops. The hive's troops have been dealt a significant blow by your actions. You interrupted the hive's replenishment of troops lastingly and severely." },
+ -- [275] = Unknown/non-existent
[276] = { name = "Navigational Error", grade = 2, points = 5, secret = true, description = "You confronted the Navigator." },
[277] = { name = "Si, Ariki!", grade = 1, points = 1, description = "You've found the oriental traveller Yasir and were able to trade with him - even if you didn't really understand his language." },
[278] = { name = "Guardian Downfall", grade = 2, points = 4, description = "You ended the life of over three hundred Deepling Guards. Not quite the guardian of the Deeplings, are you?" },
@@ -282,7 +282,7 @@ ACHIEVEMENTS = {
[281] = { name = "Gem Cutter", grade = 1, points = 1, secret = true, description = 'You cut your first gem - and it bears your own name! Now that would be a nice gift! This does not make it a "true" Heart of the Sea, however...' },
[282] = { name = "Spolium Profundis", grade = 2, points = 4, description = "You travelled the depths of this very world. You entered the blackness of the deep sea to conquer the realm of the Deeplings. May this suit remind you of the strange beauty below." },
[283] = { name = "Bane of the Hive", grade = 1, points = 2, description = "Countless fights and never tiring effort in the war against the hive grant you the experience to finish your outfit with the last remaining part. Your chitin outfit is a testament of your skills and dedication for the cause." },
- [284] = { name = "King of the Ring", grade = 1, points = 2, description = "Bretzecutioner's body just got slammed away. You are a true king of the ring!" },
+ -- [284] = Unknown/non-existent
[285] = { name = "Hive War Veteran", grade = 1, points = 1, description = "Your invaluable experience in fighting the hive allows you to add another piece of armor to your chitin outfit to prove your dedication for the cause." },
[286] = { name = "Hive Fighter", grade = 1, points = 1, description = "You have participated that much in the hive war, that you are able to create some makeshift armor from the remains of dead hive born that can be found in the major hive, to show of your skill." },
[287] = { name = "Howly Silence", grade = 1, points = 1, description = "You muted the everlasting howling of Hemming." },
@@ -294,7 +294,7 @@ ACHIEVEMENTS = {
[293] = { name = "King of the Ring", grade = 1, points = 2, description = "Bretzecutioner's body just got slammed away. You are a true king of the ring!" },
[294] = { name = "Back from the Dead", grade = 1, points = 2, description = "You overcame the undead Zanakeph and sent him back into the darkness that spawned him." },
[295] = { name = "Pwned All Fur", grade = 3, points = 8, secret = true, description = "You've faced and defeated each of the mighty bosses the Paw and Fur society sent you out to kill. All by yourself. What a hunt!" },
- [296] = { name = "Diplomatic Immunity", grade = 2, points = 4, secret = true, description = "You killed the ambassador of the abyss that often that they might consider sending another one. Perhaps that will one day stop further intrusions." },
+ -- [296] = Unknown/non-existent
[297] = { name = "Bibby's Bloodbath", grade = 1, points = 1, secret = true, description = "You lend a helping hand in defeating invading Orcs by destroying their warcamp along with their leader. Bibby's personal bloodbath..." },
[298] = { name = "Nestling", grade = 1, points = 1, description = "You cleansed the land from an eight legged nuisance by defeating Mamma Longlegs three times. She won't be back soon... or will she?" },
[299] = { name = "Becoming a Bigfoot", grade = 1, points = 1, description = "You did it! You convinced the reclusive gnomes to accept you as one of their Bigfoots. Now you are ready to help them. With big feet big missions seen to come." },
@@ -321,14 +321,14 @@ ACHIEVEMENTS = {
[320] = { name = "Funghitastic", grade = 1, points = 3, description = "Finally your dream to become a walking mushroom has come true ... No, wait a minute!" },
[321] = { name = "Crystal Clear", grade = 1, points = 3, description = "If the gnomes had told you that crystal armor is see-through you had probably changed your underwear in time." },
[322] = { name = "Gnomish Art Of War", grade = 1, points = 3, description = "You have unleashed your inner gnome and slain some of the most fearsome threats that gnomekind has ever faced. Now you can come and go to the warzones as it pleases you. The enemies of gnomekind will never be safe again." },
- [323] = { name = "Never Surrender", grade = 1, points = 3, description = "You did not show any signs of surrender to any sight of... you get the picture. Even a hundred of them did not pose a threat to you." },
+ -- [323] = Unknown/non-existent
[324] = { name = "True Dedication", grade = 2, points = 5, secret = true, description = "You conquered the demon challenge and prevailed... now show off your success in style!" },
[325] = { name = "Task Manager", grade = 1, points = 2, secret = true, description = "Helping a poor, stupid goblin to feed his starving children and wifes feels good ... if you'd only get rid of the strange feeling that you're missing something." },
[326] = { name = "Gravedigger", grade = 1, points = 3, description = "Assisting Omrabas' sick plan to resurrect made you dig your way through the blood-soaked halls of Drefia. Maybe better he failed!" },
[327] = { name = "Repenter", grade = 1, points = 1, secret = true, description = "You cleansed your soul in serving the Repenter enclave and purified thine self in completing all tasks in a single day of labour." },
[328] = { name = "Umbral Swordsman", grade = 2, points = 6, description = "You managed to transform, improve and sacrify your blade into a master state and have proven yourself worthy in a nightmarish world." },
- [329] = { name = "Umbral Berserker", grade = 2, points = 6, description = "You managed to transform, improve and sacrify your hammer into a master state and have proven yourself worthy in a nightmarish world." },
- [330] = { name = "Umbral Bladelord", grade = 2, points = 6, description = "You managed to transform, improve and sacrify your slayer into a master state and have proven yourself worthy in a nightmarish world." },
+ -- [329] = Unknown/non-existent
+ -- [330] = Unknown/non-existent
[331] = { name = "Cave Completionist", grade = 1, points = 2, description = "You have helped the gnomes of the spike in securing the caves and explored enough of the lightles depths to earn you a complete cave explorers outfit. Well done!" },
[332] = { name = "Umbral Bladelord", grade = 2, points = 6, description = "You managed to transform, improve and sacrify your slayer into a master state and have proven yourself worthy in a nightmarish world." },
[333] = { name = "Umbral Headsman", grade = 2, points = 6, description = "You managed to transform, improve and sacrify your axe into a master state and have proven yourself worthy in a nightmarish world." },
@@ -367,7 +367,7 @@ ACHIEVEMENTS = {
[366] = { name = "Publicity", grade = 1, points = 1, description = "You are a man of the public. Or of good publicity at least. Through your efforts in advertising the airtight cloth, Zeronex might yet be redeemed - and Rathleton might yet see its first working Gloud Ship." },
[367] = { name = "Snake Charmer", grade = 1, points = 1, description = "By restoring the Everhungry Altar, you charmed the Fire-Feathered Sea Serpent back into its fitful sleep, twenty miles beneath the sea." },
[368] = { name = "Hoard of the Dragon", grade = 1, points = 1, secret = true, description = "Your adventurous way through countless dragon lairs earned you a pretty treasure - and surely the enmity of many a dragon." },
- [369] = { name = "Icy Glare", grade = 1, points = 1, description = "Here's looking at you, kid. This ancient creature seems to size you up with its brilliant eyes and barely tolerates you riding it. Maybe it thinks you're the defrosted snack, after all?" },
+ -- [369] = Unknown/non-existent
[370] = { name = "Little Ball of Wool", grade = 1, points = 1, description = "You found a lost sheep and thus a steady source of black wool. But careful: don't get entangled." },
[371] = { name = "Luminous Kitty", grade = 1, points = 3, description = "You made some efforts to bring a little more light into the world. And what a nice present you got in return!" },
[372] = { name = "The Right Tone", grade = 1, points = 1, description = "By setting the right tone you convinced a crystal wolf to accompany you. Remember it is made of crystal, though, so be careful in a banshee's presence." },
@@ -404,10 +404,10 @@ ACHIEVEMENTS = {
[403] = { name = "Icy Glare", grade = 1, points = 1, description = "Here's looking at you, kid. This ancient creature seems to size you up with its brilliant eyes and barely tolerates you riding it. Maybe it thinks you're the defrosted snack, after all?" },
[404] = { name = "Cartography 101", grade = 1, points = 2, description = "You succeeded in finding and charting several previously unexplored landmarks and locations for the Adventurer's Guild, you probably never need to ask anyone for the way - do you?" },
[405] = { name = "Lost Palace Raider", grade = 1, points = 2, secret = true, description = "Lifting the secrets of a fabulous palace and defeating a beautiful demon princess was a thrilling experience indeed. This site's marvels nearly matched its terrors. Nearly." },
- [406] = { name = "The More the Merrier", grade = 0, points = 0, secret = true, description = "It's dangerous to go alone... Take ten friends." },
- [407] = { name = "Contender", grade = 1, points = 3, description = "You have fully unlocked 10 medium monsters in the cyclopedia." },
+ [406] = { name = "The More the Merrier", grade = 1, points = 0, secret = true, description = "It's dangerous to go alone... Take ten friends." },
+ -- [407] = Unknown/non-existent
[408] = { name = "Rift Warrior", grade = 1, points = 3, description = "You went through hell. Seven times. You defeated the demons. Countless times. You put an end to Ferumbras claims to ascendancy. Once and for all." },
- [409] = { name = "Duked It Out", grade = 1, points = 1, description = "You defeated the Duke of the Depths and destroyed his lava pump!" },
+ -- [409] = Unknown/non-existent
[410] = { name = "Hat Hunter", grade = 2, points = 5, description = "You sucessfully fought against all odds to protect your world from an ascending god! – You weren't there for the hat only after all?" },
[411] = { name = "Ogre Chef", grade = 1, points = 1, description = "You didn't manage to become an ogre chief. But at least you are, beyond doubt, a worthy ogre chef." },
[412] = { name = "The Call of the Wild", grade = 1, points = 2, description = "You opposed man-eating ogres and clumsy clomps. You grappled with hungry chieftains, desperate goblins and angry spirits. So you truly overcame the wild vastness of Krailos." },
@@ -421,13 +421,13 @@ ACHIEVEMENTS = {
[420] = { name = "Toothfairy Assistant", grade = 1, points = 1, description = "You assisted a very prominent fae and you fought tooth and nail to earn this title." },
[421] = { name = "Fairy Teasing", grade = 1, points = 1, secret = true, description = "Teasing fairies is fun. They leave behind such pretty clouds of glittering dust when chased. Just hope they don't get you back for it." },
[422] = { name = "Corruption Contained", grade = 2, points = 5, description = "You have managed to stall the worst incursion of corruption. Still this is just one battle won in an all out war for your world." },
- [423] = { name = "Daraman's Footsteps", grade = 1, points = 1, description = "You journeyed through Darashia and the sea of sand around it, while fighting the perils of the desert." },
- [424] = { name = "Dwarven Mines", grade = 1, points = 1, description = "Vast mines, an orc fortress and the magnificence of Kazordoon - you really know every corner of North-Eastern Mainland now." },
- [425] = { name = "Elven Woods", grade = 1, points = 1, description = "Tall trees, deep forests and and the beauty of Ab'Dendriel - you really know every corner of the elven lands now." },
- [426] = { name = "Glooth Punk", grade = 1, points = 1, description = "Glooth is the substance that powers a whole continent and all its weird inhabitants, workshops and factories. You travelled this strange smorgasbord of curiosities in its entirety - just in time for tea." },
- [427] = { name = "High and Dry", grade = 1, points = 2, description = "You asked Captain Charles to take a shortcut quite a few times. Now you are all too familiar with desert islands all over Tibia." },
- [428] = { name = "Jewel in the Swamp", grade = 1, points = 1, description = "Damp swamps, a dry desert and the opulence of Venore - you really know every corner of Eastern Mainland now." },
- [429] = { name = "King of the Jungle", grade = 1, points = 1, description = "You have searched Port Hope and the jungle that thoroughly, that you are up to adoption by a friendly ape family." },
+ -- [423] = Unknown/non-existent
+ -- [424] = Unknown/non-existent
+ -- [425] = Unknown/non-existent
+ -- [426] = Unknown/non-existent
+ -- [427] = Unknown/non-existent
+ -- [428] = Unknown/non-existent
+ -- [429] = Unknown/non-existent
[430] = { name = "Little Adventure", grade = 1, points = 1, description = "You have fully unlocked 10 easy monsters in the cyclopedia." },
[431] = { name = "Little Big Adventure", grade = 1, points = 2, secret = true, description = "You have fully unlocked 100 easy monsters in the cyclopedia." },
[432] = { name = "Contender", grade = 1, points = 3, description = "You have fully unlocked 10 medium monsters in the cyclopedia." },
@@ -470,7 +470,7 @@ ACHIEVEMENTS = {
[469] = { name = "Battle Mage", grade = 2, points = 6, description = "Wielding dangerous knowledge as well as the sword is your expertise. You have proven yourself versatile in all manner of situations." },
[470] = { name = "Widely Travelled", grade = 3, points = 7, description = "As a true globetrotter you can now show your colours proudly with this extraordinary outfit." },
[471] = { name = "Running the Rift", grade = 1, points = 3, description = "You don't just have a permission to ride a rift runner, you literally went through hell and earned it!" },
- [472] = { name = "Nothing but Hot Air", grade = 1, points = 2, description = "You have tamed the ghostly mists to do your bidding. For now ..." },
+ -- [472] = Unknown/non-existent
[473] = { name = "Exalted Battle Mage", grade = 1, points = 2, description = "Not only did you master the battlefield as a mage, you were also induced to the most inner secrets of the art of magical warfare and prevailed." },
[474] = { name = "Areas of Effect", grade = 1, points = 3, secret = true, description = "Wisely contributing your resources to areas, you pushed creatures to maximum effect, allowing improved respawn for everyone! Well done!" },
[475] = { name = "Tied the Knot", grade = 1, points = 1, secret = true, description = "You figured out the right order of spells in the buried cathedral, how enchanting!" },
@@ -483,7 +483,7 @@ ACHIEVEMENTS = {
[482] = { name = "Dream Catcher", grade = 1, points = 3, description = "You are the slayer of the ancient nightmare beast and prevented the nightmare to spread its madness." },
[483] = { name = "Champion of Summer", grade = 1, points = 2, secret = true, description = "You have vanquished numerous arena champions in the name of the Summer Court." },
[484] = { name = "Champion of Winter", grade = 1, points = 2, secret = true, description = "You have vanquished numerous arena champions in the name of the Winter Court." },
- [485] = { name = "Allow Cookies?", grade = 2, points = 6, description = "With a perfectly harmless smile, you tricked all the funny guys into eating your exploding cookies. Next time you pull this prank, consider wearing a Boy Scout outfit to make it even better." },
+ -- [485] = Unknown/non-existent
[486] = { name = "Bewitcher", grade = 2, points = 5, secret = true, description = "You literally put everything in that cauldron except lilac and gooseberries." },
[487] = { name = "Gryphon Rider", grade = 1, points = 3, description = "Unmasking spies, killing demons, discovering omens, solving puzzles and fighting ogres, manticores and feral sphinxes. - Nobody said it was easy to become a gryphon rider." },
[488] = { name = "Sculptor Apprentice", grade = 1, points = 2, secret = true, description = "Granted, you didn't carve those lifelike animal figurines yourself. But helping a medusa to find proper objects and even watching her using her petrifying gaze is almost as rewarding." },
@@ -496,6 +496,7 @@ ACHIEVEMENTS = {
[495] = { name = "Inquisition's Arm", grade = 1, points = 2, description = "Your special garb, solely awarded to a dedicated Hand of the Inquisition, is now complete." },
[496] = { name = "Traditionalist", grade = 2, points = 6, description = "You proudly wear the traditional Orcsoberfest garb, same as it ever was and as it always will be." },
[497] = { name = "Do a Barrel Roll!", grade = 1, points = 3, description = "Riding a traditional beer barrel from the Orcsoberfest is a once-in-a-lifetime experience. Beer sold separately." },
+ -- [498] = Unknown/non-existent
[499] = { name = "Orcsoberfest Welcome", grade = 1, points = 3, secret = true, description = 'The Orcsoberfest is not only known for its traditional food, beer and customs but also fun events and excitement! You took part in all of that and can now truly say: "I survived!"' },
[500] = { name = "Prospectre", grade = 1, points = 1, secret = true, description = "You made acquaintance with the Thaian. A strange contemporary with a dark history. No man but a derivate of greed and obsession." },
[501] = { name = "Nothing but Hot Air", grade = 1, points = 3, description = "You have tamed the ghostly mists to do your bidding. For now ..." },
@@ -506,19 +507,24 @@ ACHIEVEMENTS = {
[506] = { name = "Falconer", grade = 1, points = 2, description = "A true beastmaster learns the language of his animal companions. Now you as well can bolster your unique bond with nature and help preserve the balance of life as a proud falconer." },
[507] = { name = "Steppe Elegance", grade = 1, points = 3, description = "Champion of the wildlands, a swift strider among the creatures of the wild. The elegant nature of the gallop, this envoy of speed has mastered, indicates the precise understanding of its terrain and environment." },
[508] = { name = "Beyonder", grade = 1, points = 3, description = "Adventurous beyond death, you travelled the Netherworld. Although you had just the ghost of a chance you survived and even came back from the realm of the dead." },
+ -- [509] = Unknown/non-existent
[510] = { name = "Drama in Darama", grade = 1, points = 3, description = "If a pride of lions and a pack of hyaenas feud, it is not called a catfight but a ... whatsoever. For sure, it caused a lot of drama in the Darama Desert." },
[511] = { name = "Malefitz", grade = 1, points = 1, secret = true, description = "Made acquaintance with three brothers Fitz." },
[512] = { name = "Lionheart", grade = 1, points = 3, description = "You bested the maleficent duo Drume and Fugue and restored order to the besieged town of Bounac. You conquered the exotic stronghold of the Order of the Cobra and bested the undead knights of the Order of the Falcon. A true knight in heart and mind." },
+ [513] = { name = "Soul Mender", grade = 4, points = 10, description = "Brought back to the realm of the living this magnificent creature will carry you through death and everything that lays beyond." },
[514] = { name = "You Got Horse Power", grade = 3, points = 8, description = "Brought back to the realm of the living this magnificent creature will carry you through death and everything that lays beyond." },
[515] = { name = "Unleash the Beast", grade = 3, points = 8, description = "You defeated the manifestation of Goshnar's evil traits by fighting your way through beasts you didn't even want to imagine. It transformed you and now you can also look the part." },
[516] = { name = "Well Roared, Lion!", grade = 1, points = 1, description = "You helped Domizian and thus proved yourself worthy to enter the werelion sanctum underneath Lion's Rock. You faced the mighty werelions there and one of the rare white lions even chose to accompany you." },
+ -- [517] = Unknown/non-existent
[518] = { name = "Honorary Rascoohan", grade = 1, points = 2, description = "When in Rascacoon, do as the Rascoohans do!" },
[519] = { name = "Release the Kraken", grade = 1, points = 3, description = "Riding around on this squishy companion gives you the feeling of flying through the air... uhm... swimming through the seven seas!" },
+ -- [520] = Unknown/non-existent
[521] = { name = "Pied Piper", grade = 1, points = 3, secret = true, description = "You are not exactly the Pied Piper of Hamelin but at least you managed to fend off a decent amount of pirats and helped to keep them out of the cities." },
[522] = { name = "Woodcarver", grade = 1, points = 3, secret = true, description = "You defeated Megasylvan Yselda in the wake of the sleeping carnisylvan menace deep under Bounac." },
[523] = { name = "Bounacean Chivalry", grade = 1, points = 2, secret = true, description = "Yselda forever stands watch against the carnisylvan menace. Ever awake, waiting in the dark, her heart longs to be united with her king once again. Deep empathy let a hero to bring her Kesar's tulip as a token of his love. That hero was you." },
[524] = { name = "Knowledge Raider", grade = 1, points = 3, description = "Your thirst for knowledge is insatiable. In the task of helping your gnomish friends, flawless execution is just the icing on the cake." },
[525] = { name = "Citizen of Issavi", grade = 1, points = 2, description = "It was not the first time that you helped the Sapphire Blade or the Midnight Flame with a difficult task. You may now wear the Kilmareshian robes as well as the tagralt blade and the eye-embroidered veil of the seers as a sign of Issavi's gratitude." },
+ [526] = { name = "King's Council", grade = 1, points = 0, description = "Your continued efforts in keeping Bounac and the people of Kesar the Younger safe, earned you a permanent place at the royal court as an advisor to the king." },
[527] = { name = "Hot on the Trail", grade = 1, points = 3, description = "Since it is fireproof, this flaming creature feels right at home in raging infernos. But remember: just because it doesn't burn, you still do!" },
[528] = { name = "Shell We Take a Ride", grade = 1, points = 3, description = "Equipped with the shell of a tortoise and claws of a lobster this insect like companion will help you through every hardship." },
[529] = { name = "Phantastic!", grade = 1, points = 3, description = "This mighty pachyderm will march into battle as if just taking its Sunday stroll. The cost of friendship was only a few drome points!" },
@@ -526,7 +532,22 @@ ACHIEVEMENTS = {
[531] = { name = "First Achievement", grade = 1, points = 1, secret = true, description = "Congratulations to your very first achievement! ... Well, not really. But imagine, it is. Because at this point during your journey into Tibia's past, achievements have been introduced." },
[532] = { name = "Sharp Dressed", grade = 1, points = 2, description = "Just everyone will be crazy about you if you are wearing this formal dress. They will come running, promise!" },
[533] = { name = "Engine Driver", grade = 1, points = 3, description = "This glooth-driven locomotive will bring you to any party in the blink of an eye." },
+ [534] = { name = "Friendly Fire", grade = 1, points = 2, description = "You mastered the fire and tamed a supervulcano!" },
+ [535] = { name = "Wedding Planner", grade = 1, points = 3, description = "Alas! What could be more beautiful and satisfying than bringing two loving hearts together? So romantic!" },
+ [536] = { name = "Beaver Away", grade = 1, points = 1, description = "You really were as busy as a beaver in order to help the nagas. Enjoy some eager company!" },
+ [537] = { name = "Snake Pit", grade = 1, points = 1, description = "Mysterious nagas, a vibrant jungle and a sinking island - you really know every corner of Marapur now." },
+ [538] = { name = "Royalty of Hazard", grade = 1, points = 1, description = "For some it can't be hazardous enough." },
+ [539] = { name = "Measuring the World", grade = 1, points = 2, description = "Step by step you discovered many of the secrets hidden in the world, thus gaining the right to wear the Discoverer outfit and hat. Made-to-measure for a brave traveller of the Tibian wilds." },
[540] = { name = "Ripp-Ripp Hooray!", grade = 1, points = 3, description = "Don't get carried away by your success. Get carried away by your Ripptor." },
+ [541] = { name = "Warrior of the Iks", grade = 1, points = 2, description = "Combining unabating courage in combat and respect for the traditions and culture of the ancient Iks earned you the honours of true Aucar." },
+ [542] = { name = "Mutagenius", grade = 1, points = 2, description = "You accomplished the impossible and created 16 mutagens of corresponding colours." },
+ [543] = { name = "Strangest Thing", grade = 1, points = 3, description = "Only its rider can love this abomination of a mount." },
+ [544] = { name = "Fully Decayed", grade = 1, points = 2, description = "You defeated the embodiments of decay and live to tell the tale, wear the rotting attire of the unfaltering defender proudly." },
+ [545] = { name = "Like Fox and Mouse", grade = 1, points = 3, description = "Sly as a fox, quiet as a mouse - the perfect mount for a stealthy foray." },
+ [546] = { name = "The Spirit of Purity", grade = 1, points = 3, description = "Withstanding both filth and desolation of the rotten darkness that corrupted the very core of this world, you embodied the weapon of purity and light to defy all that was tainted. This spirit will continue guide you on all future paths." },
+ [547] = { name = "Museum Goer", grade = 1, points = 2, description = "You unveiled the secret plot of the Mitmah who stole away an entire civilisation for their own entertainment. Let the death of their outpost vanguard be an eternal lesson to them." },
+ [548] = { name = "Mystic Predator", grade = 1, points = 3, description = "Proving your true worth to a mystic creature like the jaguar, king of the hunt, granted you not only respect but also its heart." },
+ [549] = { name = "The Rule of Raccool", grade = 1, points = 2, description = "You almost feel as cool as a raccoon. Now, where's the trash?" },
}
--[[
diff --git a/data/scripts/lib/register_migrations.lua b/data/scripts/lib/register_migrations.lua
index 5a2734dfc51..26b9a7b1a94 100644
--- a/data/scripts/lib/register_migrations.lua
+++ b/data/scripts/lib/register_migrations.lua
@@ -45,7 +45,7 @@ function Migration:register()
return
end
if not self:_validateName() then
- error("Invalid migration name: " .. self.name .. ". Migration names must be in the format: _. Example: 20231128213149_add_new_monsters")
+ logger.error("Invalid migration name: " .. self.name .. ". Migration names must be in the format: _. Example: 20231128213149_add_new_monsters")
end
table.insert(Migration.registry, self)
diff --git a/data/scripts/runes/destroy_field_rune.lua b/data/scripts/runes/destroy_field_rune.lua
index 024dbc1bcd7..8752be7eb51 100644
--- a/data/scripts/runes/destroy_field_rune.lua
+++ b/data/scripts/runes/destroy_field_rune.lua
@@ -4,6 +4,13 @@ local fields = { 105, 2118, 2119, 2120, 2121, 2122, 2123, 2124, 2125, 2126, 2132
local rune = Spell("rune")
function rune.onCastSpell(creature, variant, isHotkey)
+ local inPz = creature:getTile():hasFlag(TILESTATE_PROTECTIONZONE)
+ if inPz then
+ creature:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
+ creature:getPosition():sendMagicEffect(CONST_ME_POFF)
+ return false
+ end
+
local position = Variant.getPosition(variant)
local tile = Tile(position)
local field = tile and tile:getItemByType(ITEM_TYPE_MAGICFIELD)
diff --git a/data/scripts/spells/house/kick.lua b/data/scripts/spells/house/kick.lua
index b4b583c1007..265ac48f796 100644
--- a/data/scripts/spells/house/kick.lua
+++ b/data/scripts/spells/house/kick.lua
@@ -4,6 +4,7 @@ function spell.onCastSpell(player, variant)
local targetPlayer = Player(variant:getString()) or player
local guest = targetPlayer:getTile():getHouse()
local owner = player:getTile():getHouse()
+
-- Owner kick yourself from house
if targetPlayer == player then
player:getPosition():sendMagicEffect(CONST_ME_POFF)
@@ -11,6 +12,13 @@ function spell.onCastSpell(player, variant)
player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
return true
end
+
+ if not owner:canEditAccessList(GUEST_LIST, player) then
+ player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
+ player:getPosition():sendMagicEffect(CONST_ME_POFF)
+ return false
+ end
+
if not owner or not guest or not guest:kickPlayer(player, targetPlayer) then
player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE)
player:getPosition():sendMagicEffect(CONST_ME_POFF)
diff --git a/data/scripts/spells/support/avatar_of_light.lua b/data/scripts/spells/support/avatar_of_light.lua
index edc25c4f66e..a84fd3b8c8e 100644
--- a/data/scripts/spells/support/avatar_of_light.lua
+++ b/data/scripts/spells/support/avatar_of_light.lua
@@ -15,20 +15,9 @@ function spell.onCastSpell(creature, variant)
return false
end
- local cooldown = 0
- if grade >= 3 then
- cooldown = 60
- elseif grade >= 2 then
- cooldown = 90
- elseif grade >= 1 then
- cooldown = 120
- end
local duration = 15000
condition:setTicks(duration)
- local conditionCooldown = Condition(CONDITION_SPELLCOOLDOWN, CONDITIONID_DEFAULT, 265)
- conditionCooldown:setTicks((cooldown * 1000 * 60) / configManager.getFloat(configKeys.RATE_SPELL_COOLDOWN))
- -- creature:getPosition():sendMagicEffect(CONST_ME_AVATAR_APPEAR)
- creature:addCondition(conditionCooldown)
+ creature:getPosition():sendMagicEffect(CONST_ME_AVATAR_APPEAR)
creature:addCondition(condition)
creature:avatarTimer((os.time() * 1000) + duration)
creature:reloadData()
@@ -43,7 +32,7 @@ spell:words("uteta res sac")
spell:level(300)
spell:mana(1500)
spell:isPremium(true)
-spell:cooldown(1000) -- Cooldown is calculated on the casting
+spell:cooldown(2 * 60 * 60 * 1000) -- Default cooldown = 2 hours
spell:groupCooldown(2 * 1000)
spell:vocation("paladin;true", "royal paladin;true")
spell:hasParams(true)
diff --git a/data/scripts/spells/support/avatar_of_nature.lua b/data/scripts/spells/support/avatar_of_nature.lua
index 0bd1473c3ca..fa32dd6b821 100644
--- a/data/scripts/spells/support/avatar_of_nature.lua
+++ b/data/scripts/spells/support/avatar_of_nature.lua
@@ -15,20 +15,9 @@ function spell.onCastSpell(creature, variant)
return false
end
- local cooldown = 0
- if grade >= 3 then
- cooldown = 60
- elseif grade >= 2 then
- cooldown = 90
- elseif grade >= 1 then
- cooldown = 120
- end
local duration = 15000
condition:setTicks(duration)
- local conditionCooldown = Condition(CONDITION_SPELLCOOLDOWN, CONDITIONID_DEFAULT, 267)
- conditionCooldown:setTicks((cooldown * 1000 * 60) / configManager.getFloat(configKeys.RATE_SPELL_COOLDOWN))
- -- creature:getPosition():sendMagicEffect(CONST_ME_AVATAR_APPEAR)
- creature:addCondition(conditionCooldown)
+ creature:getPosition():sendMagicEffect(CONST_ME_AVATAR_APPEAR)
creature:addCondition(condition)
creature:avatarTimer((os.time() * 1000) + duration)
creature:reloadData()
@@ -43,7 +32,7 @@ spell:words("uteta res dru")
spell:level(300)
spell:mana(2200)
spell:isPremium(true)
-spell:cooldown(1000) -- Cooldown is calculated on the casting
+spell:cooldown(2 * 60 * 60 * 1000) -- Default cooldown = 2 hours
spell:groupCooldown(2 * 1000)
spell:vocation("druid;true", "elder druid;true")
spell:hasParams(true)
diff --git a/data/scripts/spells/support/avatar_of_steel.lua b/data/scripts/spells/support/avatar_of_steel.lua
index 8380a524c6d..da71ff14f96 100644
--- a/data/scripts/spells/support/avatar_of_steel.lua
+++ b/data/scripts/spells/support/avatar_of_steel.lua
@@ -15,20 +15,9 @@ function spell.onCastSpell(creature, variant)
return false
end
- local cooldown = 0
- if grade >= 3 then
- cooldown = 60
- elseif grade >= 2 then
- cooldown = 90
- elseif grade >= 1 then
- cooldown = 120
- end
local duration = 15000
condition:setTicks(duration)
- local conditionCooldown = Condition(CONDITION_SPELLCOOLDOWN, CONDITIONID_DEFAULT, 264)
- conditionCooldown:setTicks((cooldown * 1000 * 60) / configManager.getFloat(configKeys.RATE_SPELL_COOLDOWN))
- -- creature:getPosition():sendMagicEffect(CONST_ME_AVATAR_APPEAR)
- creature:addCondition(conditionCooldown)
+ creature:getPosition():sendMagicEffect(CONST_ME_AVATAR_APPEAR)
creature:addCondition(condition)
creature:avatarTimer((os.time() * 1000) + duration)
creature:reloadData()
@@ -43,7 +32,7 @@ spell:words("uteta res eq")
spell:level(300)
spell:mana(800)
spell:isPremium(true)
-spell:cooldown(1000) -- Cooldown is calculated on the casting
+spell:cooldown(2 * 60 * 60 * 1000) -- Default cooldown = 2 hours
spell:groupCooldown(2 * 1000)
spell:vocation("knight;true", "elite knight;true")
spell:hasParams(true)
diff --git a/data/scripts/spells/support/avatar_of_storm.lua b/data/scripts/spells/support/avatar_of_storm.lua
index 79bb4e3b37e..ad644de01ba 100644
--- a/data/scripts/spells/support/avatar_of_storm.lua
+++ b/data/scripts/spells/support/avatar_of_storm.lua
@@ -15,20 +15,9 @@ function spell.onCastSpell(creature, variant)
return false
end
- local cooldown = 0
- if grade >= 3 then
- cooldown = 60
- elseif grade >= 2 then
- cooldown = 90
- elseif grade >= 1 then
- cooldown = 120
- end
local duration = 15000
condition:setTicks(duration)
- local conditionCooldown = Condition(CONDITION_SPELLCOOLDOWN, CONDITIONID_DEFAULT, 266)
- conditionCooldown:setTicks((cooldown * 1000 * 60) / configManager.getFloat(configKeys.RATE_SPELL_COOLDOWN))
- -- creature:getPosition():sendMagicEffect(CONST_ME_AVATAR_APPEAR)
- creature:addCondition(conditionCooldown)
+ creature:getPosition():sendMagicEffect(CONST_ME_AVATAR_APPEAR)
creature:addCondition(condition)
creature:avatarTimer((os.time() * 1000) + duration)
creature:reloadData()
@@ -43,7 +32,7 @@ spell:words("uteta res ven")
spell:level(300)
spell:mana(2200)
spell:isPremium(true)
-spell:cooldown(1000) -- Cooldown is calculated on the casting
+spell:cooldown(2 * 60 * 60 * 1000) -- Default cooldown = 2 hours
spell:groupCooldown(2 * 1000)
spell:vocation("sorcerer;true", "master sorcerer;true")
spell:hasParams(true)
diff --git a/data/scripts/talkactions/gm/afk.lua b/data/scripts/talkactions/gm/afk.lua
new file mode 100644
index 00000000000..6167a2b6068
--- /dev/null
+++ b/data/scripts/talkactions/gm/afk.lua
@@ -0,0 +1,94 @@
+local afk = TalkAction("/afk")
+
+playersAFKs = {}
+
+local function checkIsAFK(id)
+ for index, item in pairs(playersAFKs) do
+ if id == item.id then
+ return { afk = true, index = index }
+ end
+ end
+ return { afk = false }
+end
+
+local function showAfkMessage(playerPosition)
+ local spectators = Game.getSpectators(playerPosition, false, true, 8, 8, 8, 8)
+ if #spectators > 0 then
+ for _, spectator in ipairs(spectators) do
+ spectator:say("AFK !", TALKTYPE_MONSTER_SAY, false, spectator, playerPosition)
+ end
+ end
+end
+
+function afk.onSay(player, words, param)
+ if param == "" then
+ player:sendCancelMessage("You need to specify on/off param.")
+ return true
+ end
+
+ local id, playerPosition = player:getId(), player:getPosition()
+ local isAfk = checkIsAFK(id)
+ if param == "on" then
+ if isAfk.afk then
+ player:sendCancelMessage("You are already AFK!")
+ return true
+ end
+
+ table.insert(playersAFKs, { id = id, position = playerPosition })
+ if player:isInGhostMode() then
+ player:setGhostMode(false)
+ end
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are now AFK!")
+ playerPosition:sendMagicEffect(CONST_ME_REDSMOKE)
+ showAfkMessage(playerPosition)
+ elseif param == "off" then
+ if isAfk.afk then
+ table.remove(playersAFKs, isAfk.index)
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are no longer AFK!")
+ playerPosition:sendMagicEffect(CONST_ME_REDSMOKE)
+ end
+ end
+
+ return true
+end
+
+afk:separator(" ")
+afk:groupType("gamemaster")
+afk:register()
+
+------------------ AFK Effect Message ------------------
+local afkEffect = GlobalEvent("GodAfkEffect")
+function afkEffect.onThink(interval)
+ for _, player in ipairs(playersAFKs) do
+ showAfkMessage(player.position)
+ end
+ return true
+end
+
+afkEffect:interval(5000)
+afkEffect:register()
+
+------------------ Stop AFK Message when moves ------------------
+local callback = EventCallback()
+function callback.playerOnWalk(player, creature, creaturePos, toPos)
+ local isAfk = checkIsAFK(player:getId())
+ if isAfk.afk then
+ table.remove(playersAFKs, isAfk.index)
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are no longer AFK!")
+ end
+ return true
+end
+
+callback:register()
+
+------------------ Player Logout ------------------
+local godAfkLogout = CreatureEvent("GodAfkLogout")
+function godAfkLogout.onLogout(player)
+ local isAfk = checkIsAFK(player:getId())
+ if isAfk.afk then
+ table.remove(playersAFKs, isAfk.index)
+ end
+ return true
+end
+
+godAfkLogout:register()
diff --git a/data/scripts/talkactions/god/add_skill.lua b/data/scripts/talkactions/god/add_skill.lua
index f1c6244fba6..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)
@@ -44,8 +39,7 @@ function addSkill.onSay(player, words, param)
return true
end
- -- Trim left
- split[2] = split[2]:gsub("^%s*(.-)$", "%1")
+ split[2] = split[2]:trimSpace()
local count = 1
if split[3] then
@@ -55,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/charms.lua b/data/scripts/talkactions/god/charms.lua
index 14dde0a58d7..f06c17ac7e7 100644
--- a/data/scripts/talkactions/god/charms.lua
+++ b/data/scripts/talkactions/god/charms.lua
@@ -19,8 +19,8 @@ function addCharm.onSay(player, words, param)
player:sendCancelMessage("A player with that name is not online.")
return true
end
- --trim left
- split[2] = split[2]:gsub("^%s*(.-)$", "%1")
+
+ split[2] = split[2]:trimSpace()
player:sendCancelMessage("Added " .. split[2] .. " charm points to character '" .. target:getName() .. "'.")
target:sendCancelMessage("Received " .. split[2] .. " charm points!")
@@ -133,8 +133,8 @@ function setBestiary.onSay(player, words, param)
return true
end
- split[2] = split[2]:gsub("^%s*(.-)$", "%1") --Trim left
- split[3] = split[3]:gsub("^%s*(.-)$", "%1") --Trim left
+ split[2] = split[2]:trimSpace()
+ split[3] = split[3]:trimSpace()
local monsterName = split[2]
local mType = MonsterType(monsterName)
diff --git a/data/scripts/talkactions/god/create_npc.lua b/data/scripts/talkactions/god/create_npc.lua
index 4aeec3dde80..b6d0412d391 100644
--- a/data/scripts/talkactions/god/create_npc.lua
+++ b/data/scripts/talkactions/god/create_npc.lua
@@ -1,3 +1,6 @@
+-- To summon a temporary npc use /n npcname
+-- To summon a permanent npc use /n npcname,true
+
local createNpc = TalkAction("/n")
function createNpc.onSay(player, words, param)
@@ -9,11 +12,44 @@ function createNpc.onSay(player, words, param)
return true
end
+ local split = param:split(",")
+ local name = split[1]
+ local permanentStr = split[2]
+
local position = player:getPosition()
- local npc = Game.createNpc(param, position)
+ local npc = Game.createNpc(name, position)
if npc then
npc:setMasterPos(position)
position:sendMagicEffect(CONST_ME_MAGIC_RED)
+
+ if permanentStr and permanentStr == "true" then
+ local mapName = configManager.getString(configKeys.MAP_NAME)
+ local mapNpcsPath = mapName .. "-npc.xml"
+ local filePath = string.format("%s/world/%s", DATA_DIRECTORY, mapNpcsPath)
+ local npcsFile = io.open(filePath, "r")
+ if not npcsFile then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There was an error when trying to add permanent NPC. NPC File not found.")
+ return true
+ end
+ local fileContent = npcsFile:read("*all")
+ npcsFile:close()
+ local endTag = ""
+ if not fileContent:find(endTag, 1, true) then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There was an error when trying to add permanent NPC. The NPC file format is incorrect. Missing end tag " .. endTag .. ".")
+ return true
+ end
+ local textToAdd = string.format('\t\n\t\t\n\t', position.x, position.y, position.z, name, position.z)
+ local newFileContent = fileContent:gsub(endTag, textToAdd .. "\n" .. endTag)
+ npcsFile = io.open(filePath, "w")
+ if not npcsFile then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There was an error when trying to write to the NPC file.")
+ return true
+ end
+ npcsFile:write(newFileContent)
+ npcsFile:close()
+
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Permanent NPC added successfully.")
+ end
else
player:sendCancelMessage("There is not enough room.")
position:sendMagicEffect(CONST_ME_POFF)
diff --git a/data/scripts/talkactions/god/manage_badge.lua b/data/scripts/talkactions/god/manage_badge.lua
new file mode 100644
index 00000000000..310cce247b0
--- /dev/null
+++ b/data/scripts/talkactions/god/manage_badge.lua
@@ -0,0 +1,35 @@
+local addBadge = TalkAction("/addbadge")
+
+function addBadge.onSay(player, words, param)
+ -- create log
+ logCommand(player, words, param)
+
+ if param == "" then
+ player:sendCancelMessage("Command param required.")
+ return true
+ end
+
+ local split = param:split(",")
+ if not split[2] then
+ player:sendCancelMessage("Insufficient parameters. Usage: /addbadge playerName, badgeID")
+ return true
+ end
+
+ local target = Player(split[1])
+ if not target then
+ player:sendCancelMessage("A player with that name is not online.")
+ return true
+ end
+
+ split[2] = split[2]:trimSpace()
+ local id = tonumber(split[2])
+ if target:addBadge(id) then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format('You added a badge with ID "%i" to player "%s".', id, target:getName()))
+ target:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("%s added a badge to you.", player:getName()))
+ end
+ return true
+end
+
+addBadge:separator(" ")
+addBadge:groupType("god")
+addBadge:register()
diff --git a/data/scripts/talkactions/god/create_monster.lua b/data/scripts/talkactions/god/manage_monster.lua
similarity index 81%
rename from data/scripts/talkactions/god/create_monster.lua
rename to data/scripts/talkactions/god/manage_monster.lua
index 924a0d0906d..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
@@ -73,20 +73,20 @@ function createMonster.onSay(player, words, param)
local monsterName = split[1]
local monsterCount = 0
if split[2] then
- split[2] = split[2]:gsub("^%s*(.-)$", "%1") --Trim left
+ split[2] = split[2]:trimSpace()
monsterCount = tonumber(split[2])
end
local monsterForge = nil
if split[3] then
- split[3] = split[3]:gsub("^%s*(.-)$", "%1") --Trim left
+ split[3] = split[3]:trimSpace()
monsterForge = split[3]
end
if monsterCount > 1 then
local spawnRadius = 5
if split[4] then
- split[4] = split[4]:gsub("^%s*(.-)$", "%1") --Trim left
+ split[4] = split[4]:trimSpace()
spawnRadius = split[4]
print(spawnRadius)
end
@@ -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/manage_storage.lua b/data/scripts/talkactions/god/manage_storage.lua
index 929ef7b4417..e1973ebde00 100644
--- a/data/scripts/talkactions/god/manage_storage.lua
+++ b/data/scripts/talkactions/god/manage_storage.lua
@@ -18,8 +18,7 @@ function Player.getStorageValueTalkaction(self, param)
return true
end
- -- Trim left
- split[2] = split[2]:gsub("^%s*(.-)$", "%1")
+ split[2] = split[2]:trimSpace()
-- Try to convert the second parameter to a number. If it's not a number, treat it as a storage name
local storageKey = tonumber(split[2])
diff --git a/data/scripts/talkactions/god/manage_title.lua b/data/scripts/talkactions/god/manage_title.lua
new file mode 100644
index 00000000000..d99adbe5dc0
--- /dev/null
+++ b/data/scripts/talkactions/god/manage_title.lua
@@ -0,0 +1,70 @@
+local addTitle = TalkAction("/addtitle")
+
+function addTitle.onSay(player, words, param)
+ -- create log
+ logCommand(player, words, param)
+
+ if param == "" then
+ player:sendCancelMessage("Command param required.")
+ return true
+ end
+
+ local split = param:split(",")
+ if not split[2] then
+ player:sendCancelMessage("Insufficient parameters. Usage: /addtitle playerName, badgeID")
+ return true
+ end
+
+ local target = Player(split[1])
+ if not target then
+ player:sendCancelMessage("A player with that name is not online.")
+ return true
+ end
+
+ split[2] = split[2]:trimSpace()
+ local id = tonumber(split[2])
+ if target:addTitle(id) then
+ player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format('You added a title with ID "%i" to player "%s".', id, target:getName()))
+ target:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("%s added a title to you.", player:getName()))
+ end
+
+ return true
+end
+
+addTitle:separator(" ")
+addTitle:groupType("god")
+addTitle:register()
+
+-----------------------------------------
+local setTitle = TalkAction("/settitle")
+
+function setTitle.onSay(player, words, param)
+ -- create log
+ logCommand(player, words, param)
+
+ if param == "" then
+ player:sendCancelMessage("Command param required.")
+ return true
+ end
+
+ local split = param:split(",")
+ if not split[2] then
+ player:sendCancelMessage("Insufficient parameters. Usage: /settitle playerName, badgeID")
+ return true
+ end
+
+ local target = Player(split[1])
+ if not target then
+ player:sendCancelMessage("A player with that name is not online.")
+ return true
+ end
+
+ split[2] = split[2]:trimSpace()
+ local id = tonumber(split[2])
+ target:setCurrentTitle(id)
+ return true
+end
+
+setTitle:separator(" ")
+setTitle:groupType("god")
+setTitle:register()
diff --git a/data/scripts/talkactions/god/vip_manager.lua b/data/scripts/talkactions/god/manage_vip.lua
similarity index 100%
rename from data/scripts/talkactions/god/vip_manager.lua
rename to data/scripts/talkactions/god/manage_vip.lua
diff --git a/data/scripts/talkactions/god/reload.lua b/data/scripts/talkactions/god/reload.lua
index 5bf72868320..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,
@@ -30,6 +33,7 @@ local reloadTypes = {
["scripts"] = RELOAD_TYPE_SCRIPTS,
["stage"] = RELOAD_TYPE_CORE,
["stages"] = RELOAD_TYPE_CORE,
+ ["vocations"] = RELOAD_TYPE_VOCATIONS,
}
local reload = TalkAction("/reload")
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 94%
rename from data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua
rename to data/scripts/weapons/scripts/diamond_arrow.lua
index 61984209287..36be8dc38b4 100644
--- a/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua
+++ b/data/scripts/weapons/scripts/diamond_arrow.lua
@@ -16,7 +16,7 @@ combat:setParameter(COMBAT_PARAM_BLOCKARMOR, true)
function onGetFormulaValues(player, skill, attack, factor)
local distanceSkill = player:getEffectiveSkillLevel(SKILL_DISTANCE)
local min = (player:getLevel() / 5)
- local max = (0.09 * factor) * distanceSkill * 37 + (player:getLevel() / 5)
+ local max = (0.09 * factor) * distanceSkill * attack + (player:getLevel() / 5)
return -min, -max
end
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 58e6ea216ce..af245067057 100644
--- a/schema.sql
+++ b/schema.sql
@@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS `server_config` (
CONSTRAINT `server_config_pk` PRIMARY KEY (`config`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-INSERT INTO `server_config` (`config`, `value`) VALUES ('db_version', '44'), ('motd_hash', ''), ('motd_num', '0'), ('players_record', '0');
+INSERT INTO `server_config` (`config`, `value`) VALUES ('db_version', '46'), ('motd_hash', ''), ('motd_num', '0'), ('players_record', '0');
-- Table structure `accounts`
CREATE TABLE IF NOT EXISTS `accounts` (
@@ -127,8 +127,8 @@ CREATE TABLE IF NOT EXISTS `players` (
`skill_lifeleech_amount` bigint(20) UNSIGNED NOT NULL DEFAULT '0',
`skill_manaleech_chance` bigint(20) UNSIGNED NOT NULL DEFAULT '0',
`skill_manaleech_amount` bigint(20) UNSIGNED NOT NULL DEFAULT '0',
- `manashield` SMALLINT UNSIGNED NOT NULL DEFAULT '0',
- `max_manashield` SMALLINT UNSIGNED NOT NULL DEFAULT '0',
+ `manashield` INT UNSIGNED NOT NULL DEFAULT '0',
+ `max_manashield` INT UNSIGNED NOT NULL DEFAULT '0',
`xpboost_stamina` smallint(5) UNSIGNED DEFAULT NULL,
`xpboost_value` tinyint(4) UNSIGNED DEFAULT NULL,
`marriage_status` bigint(20) UNSIGNED NOT NULL DEFAULT '0',
@@ -215,40 +215,79 @@ CREATE TABLE IF NOT EXISTS `account_viplist` (
ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+-- Table structure `account_vipgroup`
+CREATE TABLE IF NOT EXISTS `account_vipgroups` (
+ `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',
+ CONSTRAINT `account_vipgroups_pk` PRIMARY KEY (`id`, `account_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Trigger
+--
+DELIMITER //
+CREATE TRIGGER `oncreate_accounts` AFTER INSERT ON `accounts` FOR EACH ROW BEGIN
+ 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 ;
+
+-- Table structure `account_vipgrouplist`
+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` 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`),
+ CONSTRAINT `account_vipgrouplist_unique` UNIQUE (`account_id`, `player_id`, `vipgroup_id`),
+ CONSTRAINT `account_vipgrouplist_player_fk`
+ FOREIGN KEY (`player_id`) REFERENCES `players` (`id`)
+ ON DELETE CASCADE,
+ CONSTRAINT `account_vipgrouplist_vipgroup_fk`
+ FOREIGN KEY (`vipgroup_id`, `account_id`) REFERENCES `account_vipgroups` (`id`, `account_id`)
+ ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
-- Table structure `boosted_boss`
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,
@@ -372,9 +411,9 @@ CREATE TABLE IF NOT EXISTS `guild_ranks` (
--
DELIMITER //
CREATE TRIGGER `oncreate_guilds` AFTER INSERT ON `guilds` FOR EACH ROW BEGIN
- INSERT INTO `guild_ranks` (`name`, `level`, `guild_id`) VALUES ('The Leader', 3, NEW.`id`);
- INSERT INTO `guild_ranks` (`name`, `level`, `guild_id`) VALUES ('Vice-Leader', 2, NEW.`id`);
- INSERT INTO `guild_ranks` (`name`, `level`, `guild_id`) VALUES ('Member', 1, NEW.`id`);
+ INSERT INTO `guild_ranks` (`name`, `level`, `guild_id`) VALUES ('The Leader', 3, NEW.`id`);
+ INSERT INTO `guild_ranks` (`name`, `level`, `guild_id`) VALUES ('Vice-Leader', 2, NEW.`id`);
+ INSERT INTO `guild_ranks` (`name`, `level`, `guild_id`) VALUES ('Member', 1, NEW.`id`);
END
//
DELIMITER ;
@@ -428,15 +467,13 @@ CREATE TABLE IF NOT EXISTS `houses` (
-- trigger
--
DELIMITER //
-CREATE TRIGGER `ondelete_players` BEFORE DELETE ON `players`
- FOR EACH ROW BEGIN
- UPDATE `houses` SET `owner` = 0 WHERE `owner` = OLD.`id`;
+CREATE TRIGGER `ondelete_players` BEFORE DELETE ON `players` FOR EACH ROW BEGIN
+ UPDATE `houses` SET `owner` = 0 WHERE `owner` = OLD.`id`;
END
//
DELIMITER ;
-- Table structure `house_lists`
-
CREATE TABLE IF NOT EXISTS `house_lists` (
`house_id` int NOT NULL,
`listid` int NOT NULL,
@@ -448,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,
@@ -503,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,
@@ -634,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,
@@ -793,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,
diff --git a/src/canary_server.cpp b/src/canary_server.cpp
index 0961bc93fde..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");
}
}
@@ -327,7 +327,7 @@ void CanaryServer::loadModules() {
// If "USE_ANY_DATAPACK_FOLDER" is set to true then you can choose any datapack folder for your server
const auto useAnyDatapack = g_configManager().getBoolean(USE_ANY_DATAPACK_FOLDER, __FUNCTION__);
auto datapackName = g_configManager().getString(DATA_DIRECTORY, __FUNCTION__);
- if (!useAnyDatapack && (datapackName != "data-canary" && datapackName != "data-otservbr-global" || datapackName != "data-otservbr-global" && datapackName != "data-canary")) {
+ if (!useAnyDatapack && datapackName != "data-canary" && datapackName != "data-otservbr-global") {
throw FailedToInitializeCanary(fmt::format(
"The datapack folder name '{}' is wrong, please select valid "
"datapack name 'data-canary' or 'data-otservbr-global "
@@ -376,6 +376,7 @@ void CanaryServer::loadModules() {
g_game().loadBoostedCreature();
g_ioBosstiary().loadBoostedBoss();
g_ioprey().initializeTaskHuntOptions();
+ g_game().logCyclopediaStats();
}
void CanaryServer::modulesLoadHelper(bool loaded, std::string moduleName) {
@@ -389,4 +390,5 @@ void CanaryServer::shutdown() {
g_dispatcher().shutdown();
g_metrics().shutdown();
inject().shutdown();
+ std::exit(0);
}
diff --git a/src/canary_server.hpp b/src/canary_server.hpp
index bb22232b8e4..57c931cd133 100644
--- a/src/canary_server.hpp
+++ b/src/canary_server.hpp
@@ -46,8 +46,8 @@ class CanaryServer {
FAILED
};
- RSA &rsa;
Logger &logger;
+ RSA &rsa;
ServiceManager &serviceManager;
std::atomic loaderStatus = LoaderStatus::LOADING;
diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp
index 4abf29c04cb..0e53c97546e 100644
--- a/src/config/config_enums.hpp
+++ b/src/config/config_enums.hpp
@@ -17,6 +17,9 @@ enum ConfigKey_t : uint16_t {
ALLOW_BLOCK_SPAWN,
ALLOW_CHANGEOUTFIT,
ALLOW_RELOAD,
+ AUGMENT_INCREASED_DAMAGE_PERCENT,
+ AUGMENT_POWERFUL_IMPACT_PERCENT,
+ AUGMENT_STRONG_IMPACT_PERCENT,
AUTH_TYPE,
AUTOBANK,
AUTOLOOT,
@@ -47,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,
@@ -136,6 +140,7 @@ enum ConfigKey_t : uint16_t {
MAP_DOWNLOAD_URL,
MAP_NAME,
MARKET_OFFER_DURATION,
+ MARKET_REFRESH_PRICES,
MARKET_PREMIUM,
MAX_ALLOWED_ON_A_DUMMY,
MAX_CONTAINER_ITEM,
@@ -324,5 +329,5 @@ enum ConfigKey_t : uint16_t {
WHEEL_POINTS_PER_LEVEL,
WHITE_SKULL_TIME,
WORLD_TYPE,
- XP_DISPLAY_MODE,
+ XP_DISPLAY_MODE
};
diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp
index 8d4ba7a77da..35d2cf3b1be 100644
--- a/src/config/configmanager.cpp
+++ b/src/config/configmanager.cpp
@@ -11,7 +11,6 @@
#include "config/configmanager.hpp"
#include "lib/di/container.hpp"
-#include "declarations.hpp"
#include "game/game.hpp"
#include "server/network/webhook/webhook.hpp"
@@ -62,6 +61,7 @@ bool ConfigManager::load() {
loadIntConfig(L, GAME_PORT, "gameProtocolPort", 7172);
loadIntConfig(L, LOGIN_PORT, "loginProtocolPort", 7171);
loadIntConfig(L, MARKET_OFFER_DURATION, "marketOfferDuration", 30 * 24 * 60 * 60);
+ loadIntConfig(L, MARKET_REFRESH_PRICES, "marketRefreshPricesInterval", 30);
loadIntConfig(L, PREMIUM_DEPOT_LIMIT, "premiumDepotLimit", 8000);
loadIntConfig(L, SQL_PORT, "mysqlPort", 3306);
loadIntConfig(L, STASH_ITEMS, "stashItemCount", 5000);
@@ -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);
@@ -345,6 +346,9 @@ bool ConfigManager::load() {
loadIntConfig(L, WHEEL_ATELIER_ROTATE_REGULAR_COST, "wheelAtelierRotateRegularCost", 250000);
loadIntConfig(L, WHEEL_POINTS_PER_LEVEL, "wheelPointsPerLevel", 1);
loadIntConfig(L, WHITE_SKULL_TIME, "whiteSkullTime", 15 * 60 * 1000);
+ loadIntConfig(L, AUGMENT_INCREASED_DAMAGE_PERCENT, "augmentIncreasedDamagePercent", 5);
+ loadIntConfig(L, AUGMENT_POWERFUL_IMPACT_PERCENT, "augmentPowerfulImpactPercent", 10);
+ loadIntConfig(L, AUGMENT_STRONG_IMPACT_PERCENT, "augmentStrongImpactPercent", 7);
loadStringConfig(L, CORE_DIRECTORY, "coreDirectory", "data");
loadStringConfig(L, DATA_DIRECTORY, "dataPackDirectory", "data-otservbr-global");
diff --git a/src/creatures/CMakeLists.txt b/src/creatures/CMakeLists.txt
index f3dedf3a8f3..af6ba129eac 100644
--- a/src/creatures/CMakeLists.txt
+++ b/src/creatures/CMakeLists.txt
@@ -22,7 +22,10 @@ target_sources(${PROJECT_NAME}_lib PRIVATE
players/storages/storages.cpp
players/player.cpp
players/achievement/player_achievement.cpp
+ players/cyclopedia/player_badge.cpp
+ players/cyclopedia/player_title.cpp
players/wheel/player_wheel.cpp
players/wheel/wheel_gems.cpp
players/vocations/vocation.cpp
+ players/vip/player_vip.cpp
)
diff --git a/src/creatures/appearance/mounts/mounts.cpp b/src/creatures/appearance/mounts/mounts.cpp
index dff0f02a451..7014d0b026b 100644
--- a/src/creatures/appearance/mounts/mounts.cpp
+++ b/src/creatures/appearance/mounts/mounts.cpp
@@ -29,13 +29,13 @@ 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;
}
- mounts.emplace_back(std::make_shared(
+ mounts.emplace(std::make_shared(
static_cast(pugi::cast(mountNode.attribute("id").value())),
lookType,
mountNode.attribute("name").as_string(),
@@ -44,12 +44,11 @@ bool Mounts::loadFromXml() {
mountNode.attribute("type").as_string()
));
}
- mounts.shrink_to_fit();
return true;
}
std::shared_ptr Mounts::getMountByID(uint8_t id) {
- auto it = std::find_if(mounts.begin(), mounts.end(), [id](const std::shared_ptr mount) {
+ auto it = std::find_if(mounts.begin(), mounts.end(), [id](const std::shared_ptr &mount) {
return mount->id == id; // Note the use of -> operator to access the members of the Mount object
});
@@ -58,7 +57,7 @@ std::shared_ptr Mounts::getMountByID(uint8_t id) {
std::shared_ptr Mounts::getMountByName(const std::string &name) {
auto mountName = name.c_str();
- auto it = std::find_if(mounts.begin(), mounts.end(), [mountName](const std::shared_ptr mount) {
+ auto it = std::find_if(mounts.begin(), mounts.end(), [mountName](const std::shared_ptr &mount) {
return strcasecmp(mountName, mount->name.c_str()) == 0;
});
@@ -66,7 +65,7 @@ std::shared_ptr Mounts::getMountByName(const std::string &name) {
}
std::shared_ptr Mounts::getMountByClientID(uint16_t clientId) {
- auto it = std::find_if(mounts.begin(), mounts.end(), [clientId](const std::shared_ptr mount) {
+ auto it = std::find_if(mounts.begin(), mounts.end(), [clientId](const std::shared_ptr &mount) {
return mount->clientId == clientId; // Note the use of -> operator to access the members of the Mount object
});
diff --git a/src/creatures/appearance/mounts/mounts.hpp b/src/creatures/appearance/mounts/mounts.hpp
index 05bd330a66b..ca4f969842a 100644
--- a/src/creatures/appearance/mounts/mounts.hpp
+++ b/src/creatures/appearance/mounts/mounts.hpp
@@ -11,8 +11,8 @@
struct Mount {
Mount(uint8_t initId, uint16_t initClientId, std::string initName, int32_t initSpeed, bool initPremium, std::string initType) :
- name(initName), speed(initSpeed), clientId(initClientId), id(initId), premium(initPremium),
- type(initType) { }
+ name(std::move(initName)), speed(initSpeed), clientId(initClientId), id(initId), premium(initPremium),
+ type(std::move(initType)) { }
std::string name;
int32_t speed;
@@ -30,10 +30,10 @@ class Mounts {
std::shared_ptr getMountByName(const std::string &name);
std::shared_ptr getMountByClientID(uint16_t clientId);
- [[nodiscard]] const std::vector> &getMounts() const {
+ [[nodiscard]] const phmap::parallel_flat_hash_set> &getMounts() const {
return mounts;
}
private:
- std::vector> mounts;
+ phmap::parallel_flat_hash_set> mounts;
};
diff --git a/src/creatures/appearance/outfit/outfit.cpp b/src/creatures/appearance/outfit/outfit.cpp
index 6f0490339de..251bf7bde35 100644
--- a/src/creatures/appearance/outfit/outfit.cpp
+++ b/src/creatures/appearance/outfit/outfit.cpp
@@ -14,6 +14,17 @@
#include "utils/tools.hpp"
#include "game/game.hpp"
+Outfits &Outfits::getInstance() {
+ return inject();
+}
+
+bool Outfits::reload() {
+ for (auto &outfitsVector : outfits) {
+ outfitsVector.clear();
+ }
+ return loadFromXml();
+}
+
bool Outfits::loadFromXml() {
pugi::xml_document doc;
auto folder = g_configManager().getString(CORE_DIRECTORY, __FUNCTION__) + "/XML/outfits.xml";
@@ -34,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;
@@ -46,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;
}
@@ -67,33 +78,28 @@ bool Outfits::loadFromXml() {
return true;
}
-std::shared_ptr Outfits::getOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const {
- for (const auto &outfit : outfits[sex]) {
- if (outfit->lookType == lookType) {
- return outfit;
- }
+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;
}
- 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.
- */
+ auto sex = player->getSex();
+ if (sex != PLAYERSEX_FEMALE && sex != PLAYERSEX_MALE) {
+ g_logger().error("[{}] - Sex invalid or player: {}", __FUNCTION__, player->getName());
+ return nullptr;
+ }
-std::shared_ptr Outfits::getOpositeSexOutfitByLookType(PlayerSex_t sex, uint16_t lookType) {
- PlayerSex_t searchSex = (sex == PLAYERSEX_MALE) ? PLAYERSEX_FEMALE : PLAYERSEX_MALE;
+ if (isOppositeOutfit) {
+ sex = (sex == PLAYERSEX_MALE) ? PLAYERSEX_FEMALE : PLAYERSEX_MALE;
+ }
- for (uint16_t i = 0; i < outfits[sex].size(); i++) {
- if (outfits[sex].at(i)->lookType == lookType) {
- if (outfits[searchSex].size() > i) {
- return outfits[searchSex].at(i);
- } else { // looktype found but the oposite sex array doesn't have this index.
- return nullptr;
- }
- }
+ auto it = std::ranges::find_if(outfits[sex], [&lookType](const auto &outfit) {
+ return outfit->lookType == lookType;
+ });
+
+ if (it != outfits[sex].end()) {
+ return *it;
}
return nullptr;
}
diff --git a/src/creatures/appearance/outfit/outfit.hpp b/src/creatures/appearance/outfit/outfit.hpp
index e1a5143c673..0d89a2c932d 100644
--- a/src/creatures/appearance/outfit/outfit.hpp
+++ b/src/creatures/appearance/outfit/outfit.hpp
@@ -12,9 +12,19 @@
#include "declarations.hpp"
#include "lib/di/container.hpp"
+class Player;
+
+struct OutfitEntry {
+ constexpr explicit OutfitEntry(uint16_t initLookType, uint8_t initAddons) :
+ lookType(initLookType), addons(initAddons) { }
+
+ uint16_t lookType;
+ uint8_t addons;
+};
+
struct Outfit {
Outfit(std::string initName, uint16_t initLookType, bool initPremium, bool initUnlocked, std::string initFrom) :
- name(initName), lookType(initLookType), premium(initPremium), unlocked(initUnlocked), from(initFrom) { }
+ name(std::move(initName)), lookType(initLookType), premium(initPremium), unlocked(initUnlocked), from(std::move(initFrom)) { }
std::string name;
uint16_t lookType;
@@ -34,15 +44,12 @@ struct ProtocolOutfit {
class Outfits {
public:
- static Outfits &getInstance() {
- return inject();
- }
-
- std::shared_ptr getOpositeSexOutfitByLookType(PlayerSex_t sex, uint16_t lookType);
+ static Outfits &getInstance();
+ 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 cf98fcb6a7b..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) {
@@ -98,7 +100,11 @@ CombatDamage Combat::getCombatDamage(std::shared_ptr creature, std::sh
}
}
}
+ if (attackerPlayer && wheelSpell && wheelSpell->isInstant()) {
+ wheelSpell->getCombatDataAugment(attackerPlayer, damage);
+ }
}
+
return damage;
}
@@ -269,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) {
@@ -283,7 +292,7 @@ bool Combat::isProtected(std::shared_ptr attacker, std::shared_ptrgetVocation()->canCombat() || !target->getVocation()->canCombat() && (attacker->getVocationId() == VOCATION_NONE || target->getVocationId() == VOCATION_NONE)) {
+ if ((!attacker->getVocation()->canCombat() || !target->getVocation()->canCombat()) && (attacker->getVocationId() == VOCATION_NONE || target->getVocationId() == VOCATION_NONE)) {
return true;
}
@@ -405,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) {
@@ -644,11 +657,15 @@ 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;
}
+ if (damage.primary.type != COMBAT_PHYSICALDAMAGE) {
+ break;
+ }
+
float damagePercent = imbuementInfo.imbuement->elementDamage / 100.0;
damage.secondary.type = imbuementInfo.imbuement->combatType;
@@ -699,7 +716,7 @@ bool Combat::checkFearConditionAffected(std::shared_ptr player) {
auto affectedCount = (party->getMemberCount() + 5) / 5;
g_logger().debug("[{}] Player is member of a party, {} members can be feared", __FUNCTION__, affectedCount);
- for (const auto member : party->getMembers()) {
+ for (const auto &member : party->getMembers()) {
if (member->hasCondition(CONDITION_FEARED)) {
affectedCount -= 1;
}
@@ -751,7 +768,7 @@ void Combat::CombatConditionFunc(std::shared_ptr caster, std::shared_p
}
}
- if (caster == target || target && !target->isImmune(condition->getType())) {
+ if (caster == target || (target && !target->isImmune(condition->getType()))) {
auto conditionCopy = condition->clone();
if (caster) {
conditionCopy->setParam(CONDITION_PARAM_OWNER, caster->getID());
@@ -1022,10 +1039,10 @@ bool Combat::doCombatChain(std::shared_ptr caster, std::shared_ptrgetChainValues(caster, maxTargets, chainDistance, backtracking);
- auto targets = pickChainTargets(caster, params, chainDistance, maxTargets, backtracking, aggressive, target);
+ auto targets = pickChainTargets(caster, params, chainDistance, maxTargets, aggressive, backtracking, std::move(target));
g_logger().debug("[{}] Chain targets: {}", __FUNCTION__, targets.size());
- if (targets.empty() || targets.size() == 1 && targets.begin()->second.empty()) {
+ if (targets.empty() || (targets.size() == 1 && targets.begin()->second.empty())) {
return false;
}
@@ -1114,7 +1131,7 @@ void Combat::CombatFunc(std::shared_ptr caster, const Position &origin
uint32_t maxY = 0;
// calculate the max viewable range
- for (std::shared_ptr tile : tileList) {
+ for (const std::shared_ptr &tile : tileList) {
const Position &tilePos = tile->getPosition();
uint32_t diff = Position::getDistanceX(tilePos, pos);
@@ -1132,7 +1149,7 @@ void Combat::CombatFunc(std::shared_ptr caster, const Position &origin
const int32_t rangeY = maxY + MAP_MAX_VIEW_PORT_Y;
int affected = 0;
- for (std::shared_ptr tile : tileList) {
+ for (const std::shared_ptr &tile : tileList) {
if (canDoCombat(caster, tile, params.aggressive) != RETURNVALUE_NOERROR) {
continue;
}
@@ -1187,7 +1204,7 @@ void Combat::CombatFunc(std::shared_ptr caster, const Position &origin
uint8_t beamAffectedCurrent = 0;
tmpDamage.affected = affected;
- for (std::shared_ptr tile : tileList) {
+ for (const std::shared_ptr &tile : tileList) {
if (canDoCombat(caster, tile, params.aggressive) != RETURNVALUE_NOERROR) {
continue;
}
@@ -1233,14 +1250,14 @@ void Combat::CombatFunc(std::shared_ptr caster, const Position &origin
}
void Combat::doCombatHealth(std::shared_ptr caster, std::shared_ptr target, CombatDamage &damage, const CombatParams ¶ms) {
- doCombatHealth(caster, target, caster ? caster->getPosition() : Position(), damage, params);
+ doCombatHealth(caster, std::move(target), caster ? caster->getPosition() : Position(), damage, params);
}
void Combat::doCombatHealth(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);
}
@@ -1283,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);
}
@@ -1351,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);
}
@@ -1374,7 +1391,7 @@ void Combat::doCombatDispel(std::shared_ptr caster, std::shared_ptr caster, std::shared_ptr target, const CombatParams ¶ms) {
+[[maybe_unused]] void Combat::doCombatDefault(std::shared_ptr caster, std::shared_ptr target, const CombatParams ¶ms) {
doCombatDefault(caster, target, caster ? caster->getPosition() : Position(), params);
}
@@ -1391,7 +1408,7 @@ void Combat::doCombatDefault(std::shared_ptr caster, std::shared_ptrgetPosition(), params.impactEffect);
+ g_game().addMagicEffect(target->getPosition(), params.impactEffect);
}
*/
@@ -1523,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;
}
@@ -1616,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;
}
@@ -1647,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;
}
@@ -1707,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;
}
@@ -1749,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;
}
@@ -2068,7 +2085,7 @@ void AreaCombat::setupExtArea(const std::list &list, uint32_t rows) {
void MagicField::onStepInField(const std::shared_ptr &creature) {
// remove magic walls/wild growth
- if (!isBlocking() && g_game().getWorldType() == WORLD_TYPE_NO_PVP && id == ITEM_MAGICWALL_SAFE || id == ITEM_WILDGROWTH_SAFE) {
+ if ((!isBlocking() && g_game().getWorldType() == WORLD_TYPE_NO_PVP && id == ITEM_MAGICWALL_SAFE) || id == ITEM_WILDGROWTH_SAFE) {
if (!creature->isInGhostMode()) {
g_game().internalRemoveItem(static_self_cast- (), 1);
}
@@ -2083,7 +2100,7 @@ void MagicField::onStepInField(const std::shared_ptr &creature) {
if (ownerId) {
bool harmfulField = true;
auto itemTile = getTile();
- if (g_game().getWorldType() == WORLD_TYPE_NO_PVP || itemTile && itemTile->hasFlag(TILESTATE_NOPVPZONE)) {
+ if (g_game().getWorldType() == WORLD_TYPE_NO_PVP || (itemTile && itemTile->hasFlag(TILESTATE_NOPVPZONE))) {
auto ownerPlayer = g_game().getPlayerByGUID(ownerId);
if (ownerPlayer) {
harmfulField = false;
@@ -2161,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/combat.hpp b/src/creatures/combat/combat.hpp
index 6a79455c7c4..93b02502f25 100644
--- a/src/creatures/combat/combat.hpp
+++ b/src/creatures/combat/combat.hpp
@@ -72,8 +72,8 @@ class ChainCallback final : public CallBack {
private:
void onChainCombat(std::shared_ptr creature, uint8_t &chainTargets, uint8_t &chainDistance, bool &backtracking);
- uint8_t m_chainTargets = 0;
uint8_t m_chainDistance = 0;
+ uint8_t m_chainTargets = 0;
bool m_backtracking = false;
bool m_fromLua = false;
};
@@ -125,7 +125,7 @@ class MatrixArea {
data_[row] = new bool[cols];
for (uint32_t col = 0; col < cols; ++col) {
- data_[row][col] = 0;
+ data_[row][col] = false;
}
}
}
@@ -324,7 +324,7 @@ class Combat {
}
void setPlayerCombatValues(formulaType_t formulaType, double mina, double minb, double maxa, double maxb);
void postCombatEffects(std::shared_ptr caster, const Position &origin, const Position &pos) const {
- postCombatEffects(caster, origin, pos, params);
+ postCombatEffects(std::move(caster), origin, pos, params);
}
void setOrigin(CombatOrigin origin) {
@@ -393,7 +393,7 @@ class Combat {
* @param damage The combat damage.
* @return The calculated level formula.
*/
- int32_t getLevelFormula(std::shared_ptr player, const std::shared_ptr wheelSpell, const CombatDamage &damage) const;
+ int32_t getLevelFormula(std::shared_ptr player, std::shared_ptr wheelSpell, const CombatDamage &damage) const;
CombatDamage getCombatDamage(std::shared_ptr creature, std::shared_ptr target) const;
// configureable
diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp
index 9d0f6962884..b9603d010a2 100644
--- a/src/creatures/combat/condition.cpp
+++ b/src/creatures/combat/condition.cpp
@@ -1308,7 +1308,7 @@ void ConditionManaShield::addCondition(std::shared_ptr creature, const
bool ConditionManaShield::unserializeProp(ConditionAttr_t attr, PropStream &propStream) {
if (attr == CONDITIONATTR_MANASHIELD) {
- return propStream.read(manaShield);
+ return propStream.read(manaShield);
}
return Condition::unserializeProp(attr, propStream);
}
@@ -1317,7 +1317,7 @@ void ConditionManaShield::serialize(PropWriteStream &propWriteStream) {
Condition::serialize(propWriteStream);
propWriteStream.write(CONDITIONATTR_MANASHIELD);
- propWriteStream.write(manaShield);
+ propWriteStream.write(manaShield);
}
bool ConditionManaShield::setParam(ConditionParam_t param, int32_t value) {
@@ -1472,7 +1472,7 @@ bool ConditionDamage::unserializeProp(ConditionAttr_t attr, PropStream &propStre
} else if (attr == CONDITIONATTR_OWNER) {
return propStream.skip(4);
} else if (attr == CONDITIONATTR_INTERVALDATA) {
- IntervalInfo damageInfo;
+ IntervalInfo damageInfo {};
if (!propStream.read(damageInfo)) {
return false;
}
@@ -1530,7 +1530,7 @@ bool ConditionDamage::addDamage(int32_t rounds, int32_t time, int32_t value) {
// rounds, time, damage
for (int32_t i = 0; i < rounds; ++i) {
- IntervalInfo damageInfo;
+ IntervalInfo damageInfo {};
damageInfo.interval = time;
damageInfo.timeLeft = time;
damageInfo.value = value;
@@ -1803,13 +1803,9 @@ void ConditionDamage::generateDamageList(int32_t amount, int32_t start, std::lis
* ConditionFeared
*/
bool ConditionFeared::isStuck(std::shared_ptr creature, Position pos) const {
- for (Direction dir : m_directionsVector) {
- if (canWalkTo(creature, pos, dir)) {
- return false;
- }
- }
-
- return true;
+ return std::ranges::all_of(m_directionsVector, [&](Direction dir) {
+ return !canWalkTo(creature, pos, dir);
+ });
}
bool ConditionFeared::getRandomDirection(std::shared_ptr creature, Position pos) {
@@ -1995,6 +1991,8 @@ bool ConditionFeared::getFleePath(std::shared_ptr creature, const Posi
futurePos.y -= wsize;
g_logger().debug("[{}] Trying to flee to NORTHWEST to {} [{}]", __FUNCTION__, futurePos.toString(), wsize);
break;
+ case DIRECTION_NONE:
+ break;
}
found = creature->getPathTo(futurePos, dirList, 0, 30);
@@ -2044,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/condition.hpp b/src/creatures/combat/condition.hpp
index 1cffba99b50..07b31b42d15 100644
--- a/src/creatures/combat/condition.hpp
+++ b/src/creatures/combat/condition.hpp
@@ -27,7 +27,7 @@ class Condition : public SharedObject {
virtual bool startCondition(std::shared_ptr creature);
virtual bool executeCondition(std::shared_ptr creature, int32_t interval);
virtual void endCondition(std::shared_ptr creature) = 0;
- virtual void addCondition(std::shared_ptr creature, const std::shared_ptr condition) = 0;
+ virtual void addCondition(std::shared_ptr creature, std::shared_ptr condition) = 0;
virtual uint32_t getIcons() const;
ConditionId_t getId() const {
return id;
@@ -65,14 +65,14 @@ class Condition : public SharedObject {
protected:
uint8_t drainBodyStage = 0;
- int64_t endTime;
- uint32_t subId;
- int32_t ticks;
- ConditionType_t conditionType;
- ConditionId_t id;
- bool isBuff;
+ int64_t endTime {};
+ uint32_t subId {};
+ int32_t ticks {};
+ ConditionType_t conditionType {};
+ ConditionId_t id {};
+ bool isBuff {};
- virtual bool updateCondition(const std::shared_ptr addCondition);
+ virtual bool updateCondition(std::shared_ptr addCondition);
private:
SoundEffect_t tickSound = SoundEffect_t::SILENCE;
@@ -90,7 +90,7 @@ class ConditionGeneric : public Condition {
bool startCondition(std::shared_ptr creature) override;
bool executeCondition(std::shared_ptr creature, int32_t interval) override;
void endCondition(std::shared_ptr creature) override;
- void addCondition(std::shared_ptr creature, const std::shared_ptr condition) override;
+ void addCondition(std::shared_ptr creature, std::shared_ptr condition) override;
uint32_t getIcons() const override;
std::shared_ptr clone() const override {
@@ -106,7 +106,7 @@ class ConditionAttributes final : public ConditionGeneric {
bool startCondition(std::shared_ptr creature) final;
bool executeCondition(std::shared_ptr creature, int32_t interval) final;
void endCondition(std::shared_ptr creature) final;
- void addCondition(std::shared_ptr creature, const std::shared_ptr condition) final;
+ void addCondition(std::shared_ptr creature, std::shared_ptr condition) final;
bool setParam(ConditionParam_t param, int32_t value) final;
@@ -172,7 +172,7 @@ class ConditionRegeneration final : public ConditionGeneric {
bool startCondition(std::shared_ptr creature) override;
void endCondition(std::shared_ptr creature) override;
- void addCondition(std::shared_ptr creature, const std::shared_ptr addCondition) override;
+ void addCondition(std::shared_ptr creature, std::shared_ptr addCondition) override;
bool executeCondition(std::shared_ptr creature, int32_t interval) override;
bool setParam(ConditionParam_t param, int32_t value) override;
@@ -205,7 +205,7 @@ class ConditionManaShield final : public Condition {
bool startCondition(std::shared_ptr creature) override;
void endCondition(std::shared_ptr creature) override;
- void addCondition(std::shared_ptr creature, const std::shared_ptr addCondition) override;
+ void addCondition(std::shared_ptr creature, std::shared_ptr addCondition) override;
uint32_t getIcons() const override;
bool setParam(ConditionParam_t param, int32_t value) override;
@@ -219,7 +219,7 @@ class ConditionManaShield final : public Condition {
bool unserializeProp(ConditionAttr_t attr, PropStream &propStream) override;
private:
- uint16_t manaShield = 0;
+ uint32_t manaShield = 0;
};
class ConditionSoul final : public ConditionGeneric {
@@ -227,7 +227,7 @@ class ConditionSoul final : public ConditionGeneric {
ConditionSoul(ConditionId_t initId, ConditionType_t initType, int32_t iniTicks, bool initBuff = false, uint32_t initSubId = 0) :
ConditionGeneric(initId, initType, iniTicks, initBuff, initSubId) { }
- void addCondition(std::shared_ptr creature, const std::shared_ptr addCondition) override;
+ void addCondition(std::shared_ptr creature, std::shared_ptr addCondition) override;
bool executeCondition(std::shared_ptr creature, int32_t interval) override;
bool setParam(ConditionParam_t param, int32_t value) override;
@@ -270,7 +270,7 @@ class ConditionDamage final : public Condition {
bool startCondition(std::shared_ptr creature) override;
bool executeCondition(std::shared_ptr creature, int32_t interval) override;
void endCondition(std::shared_ptr creature) override;
- void addCondition(std::shared_ptr creature, const std::shared_ptr condition) override;
+ void addCondition(std::shared_ptr creature, std::shared_ptr condition) override;
uint32_t getIcons() const override;
std::shared_ptr clone() const override {
@@ -309,7 +309,7 @@ class ConditionDamage final : public Condition {
bool getNextDamage(int32_t &damage);
bool doDamage(std::shared_ptr creature, int32_t healthChange);
- bool updateCondition(const std::shared_ptr addCondition) override;
+ bool updateCondition(std::shared_ptr addCondition) override;
};
class ConditionFeared final : public Condition {
@@ -321,7 +321,7 @@ class ConditionFeared final : public Condition {
bool startCondition(std::shared_ptr creature) override;
bool executeCondition(std::shared_ptr creature, int32_t interval) override;
void endCondition(std::shared_ptr creature) override;
- void addCondition(std::shared_ptr creature, const std::shared_ptr condition) override;
+ void addCondition(std::shared_ptr creature, std::shared_ptr condition) override;
uint32_t getIcons() const override;
std::shared_ptr clone() const override {
@@ -360,7 +360,7 @@ class ConditionSpeed final : public Condition {
bool startCondition(std::shared_ptr creature) override;
bool executeCondition(std::shared_ptr creature, int32_t interval) override;
void endCondition(std::shared_ptr creature) override;
- void addCondition(std::shared_ptr creature, const std::shared_ptr condition) override;
+ void addCondition(std::shared_ptr creature, std::shared_ptr condition) override;
uint32_t getIcons() const override;
std::shared_ptr clone() const override {
@@ -395,7 +395,7 @@ class ConditionOutfit final : public Condition {
bool startCondition(std::shared_ptr creature) override;
bool executeCondition(std::shared_ptr creature, int32_t interval) override;
void endCondition(std::shared_ptr creature) override;
- void addCondition(std::shared_ptr creature, const std::shared_ptr condition) override;
+ void addCondition(std::shared_ptr creature, std::shared_ptr condition) override;
std::shared_ptr clone() const override {
return std::make_shared(*this);
@@ -421,7 +421,7 @@ class ConditionLight final : public Condition {
bool startCondition(std::shared_ptr creature) override;
bool executeCondition(std::shared_ptr creature, int32_t interval) override;
void endCondition(std::shared_ptr creature) override;
- void addCondition(std::shared_ptr creature, const std::shared_ptr addCondition) override;
+ void addCondition(std::shared_ptr creature, std::shared_ptr addCondition) override;
std::shared_ptr clone() const override {
return std::make_shared(*this);
@@ -445,7 +445,7 @@ class ConditionSpellCooldown final : public ConditionGeneric {
ConditionGeneric(initId, initType, initTicks, initBuff, initSubId) { }
bool startCondition(std::shared_ptr creature) override;
- void addCondition(std::shared_ptr creature, const std::shared_ptr condition) override;
+ void addCondition(std::shared_ptr creature, std::shared_ptr condition) override;
std::shared_ptr clone() const override {
return std::make_shared(*this);
@@ -458,7 +458,7 @@ class ConditionSpellGroupCooldown final : public ConditionGeneric {
ConditionGeneric(initId, initType, initTicks, initBuff, initSubId) { }
bool startCondition(std::shared_ptr creature) override;
- void addCondition(std::shared_ptr creature, const std::shared_ptr condition) override;
+ void addCondition(std::shared_ptr creature, std::shared_ptr condition) override;
std::shared_ptr clone() const override {
return std::make_shared(*this);
diff --git a/src/creatures/combat/spells.cpp b/src/creatures/combat/spells.cpp
index 60cc5e7d2a9..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;
}
@@ -630,6 +630,43 @@ void Spell::setWheelOfDestinyBoost(WheelSpellBoost_t boost, WheelSpellGrade_t gr
}
}
+void Spell::getCombatDataAugment(std::shared_ptr player, CombatDamage &damage) {
+ if (!(damage.instantSpellName).empty()) {
+ const auto equippedAugmentItems = player->getEquippedAugmentItems();
+ for (const auto &item : equippedAugmentItems) {
+ const auto augments = item->getAugmentsBySpellName(damage.instantSpellName);
+ for (auto &augment : augments) {
+ if (augment->value == 0) {
+ continue;
+ }
+ if (augment->type == Augment_t::IncreasedDamage || augment->type == Augment_t::PowerfulImpact || augment->type == Augment_t::StrongImpact) {
+ const float augmentPercent = augment->value / 100.0;
+ damage.primary.value += static_cast(damage.primary.value * augmentPercent);
+ damage.secondary.value += static_cast(damage.secondary.value * augmentPercent);
+ } else if (augment->type != Augment_t::Cooldown) {
+ const int32_t augmentValue = augment->value * 100;
+ damage.lifeLeech += augment->type == Augment_t::LifeLeech ? augmentValue : 0;
+ damage.manaLeech += augment->type == Augment_t::ManaLeech ? augmentValue : 0;
+ damage.criticalDamage += augment->type == Augment_t::CriticalExtraDamage ? augmentValue : 0;
+ }
+ }
+ }
+ }
+};
+
+int32_t Spell::calculateAugmentSpellCooldownReduction(std::shared_ptr player) const {
+ int32_t spellCooldown = 0;
+ const auto equippedAugmentItems = player->getEquippedAugmentItemsByType(Augment_t::Cooldown);
+ for (const auto &item : equippedAugmentItems) {
+ const auto augments = item->getAugmentsBySpellNameAndType(getName(), Augment_t::Cooldown);
+ for (auto &augment : augments) {
+ spellCooldown += augment->value;
+ }
+ }
+
+ return spellCooldown;
+}
+
void Spell::applyCooldownConditions(std::shared_ptr player) const {
WheelSpellGrade_t spellGrade = player->wheel()->getSpellUpgrade(getName());
bool isUpgraded = getWheelOfDestinyUpgraded() && static_cast(spellGrade) > 0;
@@ -644,8 +681,10 @@ void Spell::applyCooldownConditions(std::shared_ptr player) const {
if (isUpgraded) {
spellCooldown -= getWheelOfDestinyBoost(WheelSpellBoost_t::COOLDOWN, spellGrade);
}
- g_logger().debug("[{}] spell name: {}, spellCooldown: {}, bonus: {}", __FUNCTION__, name, spellCooldown, player->wheel()->getSpellBonus(name, WheelSpellBoost_t::COOLDOWN));
+ int32_t augmentCooldownReduction = calculateAugmentSpellCooldownReduction(player);
+ g_logger().debug("[{}] spell name: {}, spellCooldown: {}, bonus: {}, augment {}", __FUNCTION__, name, spellCooldown, player->wheel()->getSpellBonus(name, WheelSpellBoost_t::COOLDOWN), augmentCooldownReduction);
spellCooldown -= player->wheel()->getSpellBonus(name, WheelSpellBoost_t::COOLDOWN);
+ spellCooldown -= augmentCooldownReduction;
if (spellCooldown > 0) {
std::shared_ptr condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SPELLCOOLDOWN, spellCooldown / rateCooldown, 0, false, m_spellId);
player->addCondition(condition);
@@ -727,7 +766,6 @@ uint32_t Spell::getManaCost(std::shared_ptr player) const {
if (manaPercent != 0) {
uint32_t maxMana = player->getMaxMana();
uint32_t manaCost = (maxMana * manaPercent) / 100;
- WheelSpellGrade_t spellGrade = player->wheel()->getSpellUpgrade(getName());
if (manaRedution > manaCost) {
return 0;
}
@@ -750,7 +788,7 @@ bool InstantSpell::playerCastInstant(std::shared_ptr player, std::string
var.type = VARIANT_NUMBER;
var.number = player->getID();
} else if (needTarget || casterTargetOrDirection) {
- std::shared_ptr target = nullptr;
+ std::shared_ptr target;
bool useDirection = false;
if (hasParam) {
@@ -912,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;
}
@@ -1046,7 +1084,7 @@ bool RuneSpell::castSpell(std::shared_ptr creature, std::shared_ptr creature, const LuaVariant &var, bool isHotkey) {
bool result;
if (isLoadedCallback()) {
- result = executeCastSpell(creature, var, isHotkey);
+ result = executeCastSpell(std::move(creature), var, isHotkey);
} else {
result = false;
}
@@ -1057,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/combat/spells.hpp b/src/creatures/combat/spells.hpp
index cd1a7aaa623..fc23bbf9030 100644
--- a/src/creatures/combat/spells.hpp
+++ b/src/creatures/combat/spells.hpp
@@ -63,8 +63,8 @@ class Spells final : public Scripts {
}
void clear();
- bool registerInstantLuaEvent(const std::shared_ptr instant);
- bool registerRuneLuaEvent(const std::shared_ptr rune);
+ bool registerInstantLuaEvent(std::shared_ptr instant);
+ bool registerRuneLuaEvent(std::shared_ptr rune);
private:
std::map> runes;
@@ -92,7 +92,7 @@ class BaseSpell {
class CombatSpell final : public Script, public BaseSpell, public std::enable_shared_from_this {
public:
// Constructor
- CombatSpell(const std::shared_ptr newCombat, bool newNeedTarget, bool newNeedDirection);
+ CombatSpell(std::shared_ptr newCombat, bool newNeedTarget, bool newNeedDirection);
// The copy constructor and the assignment operator have been deleted to prevent accidental copying.
CombatSpell(const CombatSpell &) = delete;
@@ -345,6 +345,9 @@ class Spell : public BaseSpell {
m_separator = newSeparator.data();
}
+ void getCombatDataAugment(std::shared_ptr player, CombatDamage &damage);
+ int32_t calculateAugmentSpellCooldownReduction(std::shared_ptr player) const;
+
protected:
void applyCooldownConditions(std::shared_ptr player) const;
bool playerSpellCheck(std::shared_ptr player) const;
diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp
index 6101ff0473d..c68ab4f351b 100644
--- a/src/creatures/creature.cpp
+++ b/src/creatures/creature.cpp
@@ -125,7 +125,7 @@ void Creature::onThink(uint32_t interval) {
auto onThink = [self = getCreature(), interval] {
// scripting event - onThink
const auto &thinkEvents = self->getCreatureEvents(CREATURE_EVENT_THINK);
- for (const auto creatureEventPtr : thinkEvents) {
+ for (const auto &creatureEventPtr : thinkEvents) {
creatureEventPtr->executeOnThink(self->static_self_cast(), interval);
}
};
@@ -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"
);
});
}
@@ -299,7 +299,7 @@ void Creature::updateTileCache(std::shared_ptr upTile, const Position &pos
if (pos.z == myPos.z) {
int32_t dx = Position::getOffsetX(pos, myPos);
int32_t dy = Position::getOffsetY(pos, myPos);
- updateTileCache(upTile, dx, dy);
+ updateTileCache(std::move(upTile), dx, dy);
}
}
@@ -332,7 +332,7 @@ int32_t Creature::getWalkCache(const Position &pos) {
void Creature::onAddTileItem(std::shared_ptr tileItem, const Position &pos) {
if (isMapLoaded && pos.z == getPosition().z) {
- updateTileCache(tileItem, pos);
+ updateTileCache(std::move(tileItem), pos);
}
}
@@ -343,7 +343,7 @@ void Creature::onUpdateTileItem(std::shared_ptr updateTile, const Position
if (oldType.blockSolid || oldType.blockPathFind || newType.blockPathFind || newType.blockSolid) {
if (pos.z == getPosition().z) {
- updateTileCache(updateTile, pos);
+ updateTileCache(std::move(updateTile), pos);
}
}
}
@@ -355,7 +355,7 @@ void Creature::onRemoveTileItem(std::shared_ptr