From 4863c1d36685fa6bde8576a4b0becda7c158640e Mon Sep 17 00:00:00 2001 From: Renato Machado Date: Wed, 4 Oct 2023 23:39:56 -0300 Subject: [PATCH 01/10] improve: AreaCombat::areas to array (#1653) Accessing the array index is almost ~2x faster than using a std::map. There would only be advantages to using std::map if there were a large number of records, which is not the case. Memory consumption may be a little higher with an array, as it will allocate memory to directions that may not be used, but it is irrelevant compared to the performance gain. Benchmark: ![image](https://github.com/opentibiabr/canary/assets/2267386/e409577f-bbae-4c1b-91ea-18063296fe62)
struct Teste { bool any = false; }; std::array, 8> arrayTest; std::map> mapTest; arrayTest[3] = std::make_unique(); arrayTest[4] = std::make_unique(); arrayTest[5] = std::make_unique(); arrayTest[6] = std::make_unique(); Benchmark bm; for (size_t i = 0; i < 999999999; ++i) { const auto &v = arrayTest[3 + (rand() % 6)]; } g_logger().info("Array: " + std::to_string(bm.duration()) + "ms"); bm.reset(); bm.start(); for (size_t i = 0; i < 999999999; ++i) { const auto &v = mapTest[3 + (rand() % 6)]; } g_logger().info("Map: " + std::to_string(bm.duration()) + "ms");
--- src/creatures/combat/combat.cpp | 8 +++++--- src/creatures/combat/combat.hpp | 11 ++--------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 4a8e6bd7060..606a1c132fb 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -1768,13 +1768,15 @@ bool ChainPickerCallback::onChainCombat(std::shared_ptr creature, std: //**********************************************************// void AreaCombat::clear() { - areas.clear(); + std::ranges::fill(areas, nullptr); } AreaCombat::AreaCombat(const AreaCombat &rhs) { hasExtArea = rhs.hasExtArea; - for (const auto &it : rhs.areas) { - areas[it.first] = it.second->clone(); + for (uint_fast8_t i = 0; i <= Direction::DIRECTION_LAST; ++i) { + if (const auto &area = rhs.areas[i]) { + areas[i] = area->clone(); + } } } diff --git a/src/creatures/combat/combat.hpp b/src/creatures/combat/combat.hpp index 88c2db31312..16062cb685d 100644 --- a/src/creatures/combat/combat.hpp +++ b/src/creatures/combat/combat.hpp @@ -21,8 +21,6 @@ class Spell; class Player; class MatrixArea; -static const std::unique_ptr &MatrixAreaNull {}; - // for luascript callback class ValueCallback final : public CallBack { public: @@ -247,15 +245,10 @@ class AreaCombat { } } - auto it = areas.find(dir); - if (it == areas.end()) { - return MatrixAreaNull; - } - - return it->second; + return areas[dir]; } - std::map> areas; + std::array, Direction::DIRECTION_LAST + 1> areas {}; bool hasExtArea = false; }; From edbf957ca7b1c1b0557854147ff27536108b97be Mon Sep 17 00:00:00 2001 From: SRNT-GG <95472530+SRNT-GG@users.noreply.github.com> Date: Thu, 5 Oct 2023 06:16:45 +0200 Subject: [PATCH 02/10] docs: fix url of tools in readme (#1675) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 16b6d73cb14..d3d83eecc48 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ source of the Canary, so that it will be the first repository to use this engine To connect to the server and to take a stable experience, you can use [mehah's otclient](https://github.com/mehah/otclient) or [tibia client](https://github.com/dudantas/tibia-client/releases/latest) and if you want to edit something, check -our [customized tools](https://docs.opentibiabr.com/others/downloads/tools). +our [customized tools](https://docs.opentibiabr.com/opentibiabr/downloads/tools). If you want edit the map, use the [own remere's map editor](https://github.com/opentibiabr/remeres-map-editor/). From 04ba21a9639c7bd085d85a3f0550423b4b32793c Mon Sep 17 00:00:00 2001 From: Karin Date: Thu, 5 Oct 2023 01:18:18 -0300 Subject: [PATCH 03/10] fix: fungus wrong id reference (#1676) --- .../scripts/globalevents/worldchanges/their_masters_voice.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-otservbr-global/scripts/globalevents/worldchanges/their_masters_voice.lua b/data-otservbr-global/scripts/globalevents/worldchanges/their_masters_voice.lua index fa1b9adbe33..2061c254cf9 100644 --- a/data-otservbr-global/scripts/globalevents/worldchanges/their_masters_voice.lua +++ b/data-otservbr-global/scripts/globalevents/worldchanges/their_masters_voice.lua @@ -12,7 +12,7 @@ function theirmastersvoice.onStartup() if item then local slimeChance = math.random(100) if slimeChance <= 30 then - item:transform(math.random(13585, 13589)) + item:transform(math.random(12059, 12063)) position:sendMagicEffect(CONST_ME_YELLOW_RINGS) end end From 72adc6e4bd02db4e8bef76af09367419ad9f7d8d Mon Sep 17 00:00:00 2001 From: Beats Date: Thu, 5 Oct 2023 01:51:37 -0300 Subject: [PATCH 04/10] improve: migrate Weapon and derivatives to shared_ptr (#1659) --- src/config/configmanager.cpp | 1 - src/creatures/combat/combat.cpp | 4 +- src/creatures/monsters/monster.cpp | 1 - src/creatures/npcs/npcs.cpp | 1 - src/creatures/players/player.cpp | 2 +- src/game/game.cpp | 6 -- src/game/scheduling/events_scheduler.cpp | 1 - src/game/zones/zone.cpp | 1 - src/io/iologindata.cpp | 1 - src/io/ioprey.cpp | 1 - src/items/item.cpp | 1 - src/items/items.cpp | 1 - src/items/tile.cpp | 1 - src/items/weapons/weapons.cpp | 6 +- src/items/weapons/weapons.hpp | 9 +- src/kv/kv.cpp | 1 - src/lua/callbacks/event_callback.cpp | 2 - .../functions/core/game/game_functions.cpp | 1 - src/lua/functions/core/game/lua_enums.cpp | 3 - .../functions/items/imbuement_functions.cpp | 1 - src/lua/functions/items/weapon_functions.cpp | 92 +++++++++---------- src/lua/functions/items/weapon_functions.hpp | 2 +- src/lua/functions/lua_functions_loader.cpp | 2 - src/lua/scripts/lua_environment.hpp | 6 +- src/lua/scripts/luascript.hpp | 2 +- src/lua/scripts/script_environment.cpp | 1 - src/map/utils/qtreenode.cpp | 1 - src/server/network/connection/connection.cpp | 1 - src/server/network/message/networkmessage.cpp | 1 - 29 files changed, 62 insertions(+), 91 deletions(-) diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index 940ea29eb51..3eacd9e9ade 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -12,7 +12,6 @@ #include "config/configmanager.hpp" #include "declarations.hpp" #include "game/game.hpp" -#include "lua/scripts/luajit_sync.hpp" #include "server/network/webhook/webhook.hpp" #if LUA_VERSION_NUM >= 502 diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 606a1c132fb..8b650253567 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -73,7 +73,7 @@ CombatDamage Combat::getCombatDamage(std::shared_ptr creature, std::sh ); } else if (formulaType == COMBAT_FORMULA_SKILL) { std::shared_ptr tool = player->getWeapon(); - const Weapon* weapon = g_weapons().getWeapon(tool); + const WeaponShared_ptr weapon = g_weapons().getWeapon(tool); if (weapon) { damage.primary.value = normal_random( static_cast(minb), @@ -1514,7 +1514,7 @@ void ValueCallback::getMinMaxValues(std::shared_ptr player, CombatDamage case COMBAT_FORMULA_SKILL: { // onGetPlayerMinMaxValues(player, attackSkill, attackValue, attackFactor) std::shared_ptr tool = player->getWeapon(); - const Weapon* weapon = g_weapons().getWeapon(tool); + const WeaponShared_ptr weapon = g_weapons().getWeapon(tool); std::shared_ptr item = nullptr; if (weapon) { diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index 4ef06634933..eef9aaa3830 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -14,7 +14,6 @@ #include "creatures/players/wheel/player_wheel.hpp" #include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" -#include "lua/creature/events.hpp" #include "lua/callbacks/event_callback.hpp" #include "lua/callbacks/events_callbacks.hpp" #include "map/spectators.hpp" diff --git a/src/creatures/npcs/npcs.cpp b/src/creatures/npcs/npcs.cpp index 432b0adbe15..c18897d1c4c 100644 --- a/src/creatures/npcs/npcs.cpp +++ b/src/creatures/npcs/npcs.cpp @@ -11,7 +11,6 @@ #include "declarations.hpp" #include "creatures/combat/combat.hpp" -#include "creatures/creature.hpp" #include "lua/scripts/lua_environment.hpp" #include "creatures/combat/spells.hpp" #include "creatures/npcs/npcs.hpp" diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 59b9292662d..0ec4bedf8a4 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -4222,7 +4222,7 @@ void Player::doAttacking(uint32_t) { bool result = false; std::shared_ptr tool = getWeapon(); - const Weapon* weapon = g_weapons().getWeapon(tool); + const WeaponShared_ptr weapon = g_weapons().getWeapon(tool); uint32_t delay = getAttackSpeed(); bool classicSpeed = g_configManager().getBoolean(CLASSIC_ATTACK_SPEED); diff --git a/src/game/game.cpp b/src/game/game.cpp index 88264725b41..9f853ab6b22 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -12,13 +12,11 @@ #include "lua/creature/actions.hpp" #include "items/bed.hpp" #include "creatures/creature.hpp" -#include "lua/creature/creatureevent.hpp" #include "database/databasetasks.hpp" #include "lua/creature/events.hpp" #include "lua/callbacks/event_callback.hpp" #include "lua/callbacks/events_callbacks.hpp" #include "game/game.hpp" -#include "game/functions/game_reload.hpp" #include "game/zones/zone.hpp" #include "lua/global/globalevent.hpp" #include "io/iologindata.hpp" @@ -34,13 +32,9 @@ #include "creatures/combat/spells.hpp" #include "lua/creature/talkaction.hpp" #include "items/weapons/weapons.hpp" -#include "lua/scripts/scripts.hpp" -#include "lua/modules/modules.hpp" #include "creatures/players/imbuements/imbuements.hpp" -#include "account/account.hpp" #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/npcs/npc.hpp" -#include "creatures/npcs/npcs.hpp" #include "server/network/webhook/webhook.hpp" #include "protobuf/appearances.pb.h" #include "server/network/protocol/protocollogin.hpp" diff --git a/src/game/scheduling/events_scheduler.cpp b/src/game/scheduling/events_scheduler.cpp index f7bd197979e..d5c71378f4a 100644 --- a/src/game/scheduling/events_scheduler.cpp +++ b/src/game/scheduling/events_scheduler.cpp @@ -12,7 +12,6 @@ #include "config/configmanager.hpp" #include "game/scheduling/events_scheduler.hpp" #include "lua/scripts/scripts.hpp" -#include "utils/pugicast.hpp" bool EventsScheduler::loadScheduleEventFromXml() { pugi::xml_document doc; diff --git a/src/game/zones/zone.cpp b/src/game/zones/zone.cpp index e06975ff7a6..f1aa300efee 100644 --- a/src/game/zones/zone.cpp +++ b/src/game/zones/zone.cpp @@ -14,7 +14,6 @@ #include "creatures/monsters/monster.hpp" #include "creatures/npcs/npc.hpp" #include "creatures/players/player.hpp" -#include "game/scheduling/dispatcher.hpp" phmap::parallel_flat_hash_map> Zone::zones = {}; const static std::shared_ptr nullZone = nullptr; diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp index 307d41a9cb3..f32b90568a4 100644 --- a/src/io/iologindata.cpp +++ b/src/io/iologindata.cpp @@ -15,7 +15,6 @@ #include "game/game.hpp" #include "creatures/monsters/monster.hpp" #include "creatures/players/wheel/player_wheel.hpp" -#include "io/ioprey.hpp" bool IOLoginData::gameWorldAuthentication(const std::string &accountDescriptor, const std::string &password, std::string &characterName, uint32_t &accountId, bool oldProtocol) { account::Account account(accountDescriptor); diff --git a/src/io/ioprey.cpp b/src/io/ioprey.cpp index b81dad1fdf7..50c3070b4a0 100644 --- a/src/io/ioprey.cpp +++ b/src/io/ioprey.cpp @@ -9,7 +9,6 @@ #include "pch.hpp" -#include "declarations.hpp" #include "creatures/monsters/monster.hpp" #include "creatures/players/player.hpp" #include "config/configmanager.hpp" diff --git a/src/items/item.cpp b/src/items/item.cpp index ea04be89385..b6902575bb2 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -10,7 +10,6 @@ #include "pch.hpp" #include "items/item.hpp" -#include "items/functions/item/item_parse.hpp" #include "items/containers/container.hpp" #include "items/decay/decay.hpp" #include "game/movement/teleport.hpp" diff --git a/src/items/items.cpp b/src/items/items.cpp index ca8e20e5b30..690e01a5b92 100644 --- a/src/items/items.cpp +++ b/src/items/items.cpp @@ -11,7 +11,6 @@ #include "items/functions/item/item_parse.hpp" #include "items/items.hpp" -#include "items/weapons/weapons.hpp" #include "game/game.hpp" #include "utils/pugicast.hpp" diff --git a/src/items/tile.cpp b/src/items/tile.cpp index 25f7f865688..0d9a5db0ead 100644 --- a/src/items/tile.cpp +++ b/src/items/tile.cpp @@ -19,7 +19,6 @@ #include "lua/creature/movement.hpp" #include "game/movement/teleport.hpp" #include "items/trashholder.hpp" -#include "map/house/housetile.hpp" #include "io/iomap.hpp" #include "map/spectators.hpp" diff --git a/src/items/weapons/weapons.cpp b/src/items/weapons/weapons.cpp index 995b71518c0..08f1d75c032 100644 --- a/src/items/weapons/weapons.cpp +++ b/src/items/weapons/weapons.cpp @@ -17,7 +17,7 @@ Weapons::Weapons() = default; Weapons::~Weapons() = default; -const Weapon* Weapons::getWeapon(std::shared_ptr item) const { +const WeaponShared_ptr Weapons::getWeapon(std::shared_ptr item) const { if (!item) { return nullptr; } @@ -33,7 +33,7 @@ void Weapons::clear() { weapons.clear(); } -bool Weapons::registerLuaEvent(Weapon* event) { +bool Weapons::registerLuaEvent(WeaponShared_ptr event) { weapons[event->getID()] = event; return true; } @@ -524,7 +524,7 @@ bool WeaponDistance::useWeapon(std::shared_ptr player, std::shared_ptr mainWeaponItem = player->getWeapon(true); - const Weapon* mainWeapon = g_weapons().getWeapon(mainWeaponItem); + const WeaponShared_ptr mainWeapon = g_weapons().getWeapon(mainWeaponItem); if (mainWeapon) { damageModifier = mainWeapon->playerWeaponCheck(player, target, mainWeaponItem->getShootRange()); } else { diff --git a/src/items/weapons/weapons.hpp b/src/items/weapons/weapons.hpp index 90785983e17..78a7195fb3c 100644 --- a/src/items/weapons/weapons.hpp +++ b/src/items/weapons/weapons.hpp @@ -21,7 +21,8 @@ class WeaponMelee; class WeaponDistance; class WeaponWand; -using Weapon_ptr = std::unique_ptr; +using WeaponUnique_ptr = std::unique_ptr; +using WeaponShared_ptr = std::shared_ptr; class Weapons final : public Scripts { public: @@ -36,16 +37,16 @@ class Weapons final : public Scripts { return inject(); } - const Weapon* getWeapon(std::shared_ptr item) const; + const WeaponShared_ptr getWeapon(std::shared_ptr item) const; static int32_t getMaxMeleeDamage(int32_t attackSkill, int32_t attackValue); static int32_t getMaxWeaponDamage(uint32_t level, int32_t attackSkill, int32_t attackValue, float attackFactor, bool isMelee); - bool registerLuaEvent(Weapon* event); + bool registerLuaEvent(WeaponShared_ptr event); void clear(); private: - std::map weapons; + std::map weapons; }; constexpr auto g_weapons = Weapons::getInstance; diff --git a/src/kv/kv.cpp b/src/kv/kv.cpp index cd2a8b974b5..e5dbc974b76 100644 --- a/src/kv/kv.cpp +++ b/src/kv/kv.cpp @@ -11,7 +11,6 @@ #include "kv/kv.hpp" #include "lib/di/container.hpp" -#include "utils/tools.hpp" KVStore &KVStore::getInstance() { return inject(); diff --git a/src/lua/callbacks/event_callback.cpp b/src/lua/callbacks/event_callback.cpp index 6444446bde9..8510502b226 100644 --- a/src/lua/callbacks/event_callback.cpp +++ b/src/lua/callbacks/event_callback.cpp @@ -11,8 +11,6 @@ #include "lua/callbacks/event_callback.hpp" -#include "lua/callbacks/event_callback.hpp" -#include "lua/callbacks/events_callbacks.hpp" #include "utils/tools.hpp" #include "items/item.hpp" #include "creatures/players/player.hpp" diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index 86358f1daf7..ec59b8b1c34 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -23,7 +23,6 @@ #include "lua/creature/talkaction.hpp" #include "lua/functions/creatures/npc/npc_type_functions.hpp" #include "lua/scripts/lua_environment.hpp" -#include "lua/scripts/scripts.hpp" #include "lua/creature/events.hpp" #include "lua/callbacks/event_callback.hpp" #include "lua/callbacks/events_callbacks.hpp" diff --git a/src/lua/functions/core/game/lua_enums.cpp b/src/lua/functions/core/game/lua_enums.cpp index 8d86185290f..fc1586f3363 100644 --- a/src/lua/functions/core/game/lua_enums.cpp +++ b/src/lua/functions/core/game/lua_enums.cpp @@ -11,15 +11,12 @@ #include "lua/functions/core/game/lua_enums.hpp" -#include "account/account.hpp" #include "creatures/players/wheel/wheel_definitions.hpp" #include "io/io_bosstiary.hpp" #include "config/configmanager.hpp" #include "creatures/creature.hpp" -#include "lua/creature/creatureevent.hpp" #include "declarations.hpp" #include "game/functions/game_reload.hpp" -#include "game/game.hpp" #define registerEnumClass(luaState, enumClassType) \ { \ diff --git a/src/lua/functions/items/imbuement_functions.cpp b/src/lua/functions/items/imbuement_functions.cpp index f512e56a62b..2a4e85d7d88 100644 --- a/src/lua/functions/items/imbuement_functions.cpp +++ b/src/lua/functions/items/imbuement_functions.cpp @@ -9,7 +9,6 @@ #include "pch.hpp" -#include "items/item.hpp" #include "items/weapons/weapons.hpp" #include "creatures/players/imbuements/imbuements.hpp" #include "lua/functions/items/imbuement_functions.hpp" diff --git a/src/lua/functions/items/weapon_functions.cpp b/src/lua/functions/items/weapon_functions.cpp index 2e5fde7ff15..3a41d0ac75d 100644 --- a/src/lua/functions/items/weapon_functions.cpp +++ b/src/lua/functions/items/weapon_functions.cpp @@ -25,7 +25,7 @@ int WeaponFunctions::luaCreateWeapon(lua_State* L) { case WEAPON_AXE: case WEAPON_CLUB: { if (auto weaponPtr = g_luaEnvironment().createWeaponObject(getScriptEnv()->getScriptInterface())) { - pushUserdata(L, weaponPtr.get()); + pushUserdata(L, weaponPtr); setMetatable(L, -1, "Weapon"); weaponPtr->weaponType = type; } else { @@ -37,7 +37,7 @@ int WeaponFunctions::luaCreateWeapon(lua_State* L) { case WEAPON_DISTANCE: case WEAPON_AMMO: { if (auto weaponPtr = g_luaEnvironment().createWeaponObject(getScriptEnv()->getScriptInterface())) { - pushUserdata(L, weaponPtr.get()); + pushUserdata(L, weaponPtr); setMetatable(L, -1, "Weapon"); weaponPtr->weaponType = type; } else { @@ -47,7 +47,7 @@ int WeaponFunctions::luaCreateWeapon(lua_State* L) { } case WEAPON_WAND: { if (auto weaponPtr = g_luaEnvironment().createWeaponObject(getScriptEnv()->getScriptInterface())) { - pushUserdata(L, weaponPtr.get()); + pushUserdata(L, weaponPtr); setMetatable(L, -1, "Weapon"); weaponPtr->weaponType = type; } else { @@ -65,7 +65,7 @@ int WeaponFunctions::luaCreateWeapon(lua_State* L) { int WeaponFunctions::luaWeaponAction(lua_State* L) { // weapon:action(callback) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { std::string typeName = getString(L, 2); std::string tmpStr = asLowerCaseString(typeName); @@ -90,15 +90,15 @@ int WeaponFunctions::luaWeaponAction(lua_State* L) { int WeaponFunctions::luaWeaponRegister(lua_State* L) { // weapon:register() - Weapon** weaponPtr = getRawUserdata(L, 1); + WeaponShared_ptr* weaponPtr = getRawUserDataShared(L, 1); if (weaponPtr && *weaponPtr) { - Weapon* weapon = *weaponPtr; + WeaponShared_ptr weapon = *weaponPtr; if (weapon->weaponType == WEAPON_DISTANCE || weapon->weaponType == WEAPON_AMMO || weapon->weaponType == WEAPON_MISSILE) { - weapon = getUserdata(L, 1); + weapon = getUserdataShared(L, 1); } else if (weapon->weaponType == WEAPON_WAND) { - weapon = getUserdata(L, 1); + weapon = getUserdataShared(L, 1); } else { - weapon = getUserdata(L, 1); + weapon = getUserdataShared(L, 1); } uint16_t id = weapon->getID(); @@ -123,7 +123,7 @@ int WeaponFunctions::luaWeaponRegister(lua_State* L) { int WeaponFunctions::luaWeaponOnUseWeapon(lua_State* L) { // weapon:onUseWeapon(callback) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { if (!weapon->loadCallback()) { pushBoolean(L, false); @@ -139,7 +139,7 @@ int WeaponFunctions::luaWeaponOnUseWeapon(lua_State* L) { int WeaponFunctions::luaWeaponUnproperly(lua_State* L) { // weapon:wieldedUnproperly(bool) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { weapon->setWieldUnproperly(getBoolean(L, 2)); pushBoolean(L, true); @@ -151,7 +151,7 @@ int WeaponFunctions::luaWeaponUnproperly(lua_State* L) { int WeaponFunctions::luaWeaponLevel(lua_State* L) { // weapon:level(lvl) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { weapon->setRequiredLevel(getNumber(L, 2)); weapon->setWieldInfo(WIELDINFO_LEVEL); @@ -164,7 +164,7 @@ int WeaponFunctions::luaWeaponLevel(lua_State* L) { int WeaponFunctions::luaWeaponMagicLevel(lua_State* L) { // weapon:magicLevel(lvl) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { weapon->setRequiredMagLevel(getNumber(L, 2)); weapon->setWieldInfo(WIELDINFO_MAGLV); @@ -177,7 +177,7 @@ int WeaponFunctions::luaWeaponMagicLevel(lua_State* L) { int WeaponFunctions::luaWeaponMana(lua_State* L) { // weapon:mana(mana) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { weapon->setMana(getNumber(L, 2)); pushBoolean(L, true); @@ -189,7 +189,7 @@ int WeaponFunctions::luaWeaponMana(lua_State* L) { int WeaponFunctions::luaWeaponManaPercent(lua_State* L) { // weapon:manaPercent(percent) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { weapon->setManaPercent(getNumber(L, 2)); pushBoolean(L, true); @@ -201,7 +201,7 @@ int WeaponFunctions::luaWeaponManaPercent(lua_State* L) { int WeaponFunctions::luaWeaponHealth(lua_State* L) { // weapon:health(health) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { weapon->setHealth(getNumber(L, 2)); pushBoolean(L, true); @@ -213,7 +213,7 @@ int WeaponFunctions::luaWeaponHealth(lua_State* L) { int WeaponFunctions::luaWeaponHealthPercent(lua_State* L) { // weapon:healthPercent(percent) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { weapon->setHealthPercent(getNumber(L, 2)); pushBoolean(L, true); @@ -225,7 +225,7 @@ int WeaponFunctions::luaWeaponHealthPercent(lua_State* L) { int WeaponFunctions::luaWeaponSoul(lua_State* L) { // weapon:soul(soul) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { weapon->setSoul(getNumber(L, 2)); pushBoolean(L, true); @@ -237,7 +237,7 @@ int WeaponFunctions::luaWeaponSoul(lua_State* L) { int WeaponFunctions::luaWeaponBreakChance(lua_State* L) { // weapon:breakChance(percent) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { weapon->setBreakChance(getNumber(L, 2)); pushBoolean(L, true); @@ -249,7 +249,7 @@ int WeaponFunctions::luaWeaponBreakChance(lua_State* L) { int WeaponFunctions::luaWeaponWandDamage(lua_State* L) { // weapon:damage(damage[min, max]) only use this if the weapon is a wand! - WeaponWand* weapon = getUserdata(L, 1); + const auto &weapon = getUserdataShared(L, 1); if (weapon) { weapon->setMinChange(getNumber(L, 2)); if (lua_gettop(L) > 2) { @@ -266,7 +266,7 @@ int WeaponFunctions::luaWeaponWandDamage(lua_State* L) { int WeaponFunctions::luaWeaponElement(lua_State* L) { // weapon:element(combatType) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { if (!getNumber(L, 2)) { std::string element = getString(L, 2); @@ -300,7 +300,7 @@ int WeaponFunctions::luaWeaponElement(lua_State* L) { int WeaponFunctions::luaWeaponPremium(lua_State* L) { // weapon:premium(bool) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { weapon->setNeedPremium(getBoolean(L, 2)); weapon->setWieldInfo(WIELDINFO_PREMIUM); @@ -313,7 +313,7 @@ int WeaponFunctions::luaWeaponPremium(lua_State* L) { int WeaponFunctions::luaWeaponVocation(lua_State* L) { // weapon:vocation(vocName[, showInDescription = false, lastVoc = false]) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { weapon->addVocWeaponMap(getString(L, 2)); weapon->setWieldInfo(WIELDINFO_VOCREQ); @@ -352,7 +352,7 @@ int WeaponFunctions::luaWeaponVocation(lua_State* L) { int WeaponFunctions::luaWeaponId(lua_State* L) { // weapon:id(id) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { weapon->setID(getNumber(L, 2)); pushBoolean(L, true); @@ -364,7 +364,7 @@ int WeaponFunctions::luaWeaponId(lua_State* L) { int WeaponFunctions::luaWeaponAttack(lua_State* L) { // weapon:attack(atk) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { uint16_t id = weapon->getID(); ItemType &it = Item::items.getItemType(id); @@ -378,7 +378,7 @@ int WeaponFunctions::luaWeaponAttack(lua_State* L) { int WeaponFunctions::luaWeaponDefense(lua_State* L) { // weapon:defense(defense[, extraDefense]) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { uint16_t id = weapon->getID(); ItemType &it = Item::items.getItemType(id); @@ -395,7 +395,7 @@ int WeaponFunctions::luaWeaponDefense(lua_State* L) { int WeaponFunctions::luaWeaponRange(lua_State* L) { // weapon:range(range) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { uint16_t id = weapon->getID(); ItemType &it = Item::items.getItemType(id); @@ -409,7 +409,7 @@ int WeaponFunctions::luaWeaponRange(lua_State* L) { int WeaponFunctions::luaWeaponCharges(lua_State* L) { // weapon:charges(charges[, showCharges = true]) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { bool showCharges = true; if (lua_gettop(L) > 2) { @@ -428,7 +428,7 @@ int WeaponFunctions::luaWeaponCharges(lua_State* L) { int WeaponFunctions::luaWeaponDuration(lua_State* L) { // weapon:duration(duration[, showDuration = true]) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { bool showDuration = true; if (lua_gettop(L) > 2) { @@ -447,7 +447,7 @@ int WeaponFunctions::luaWeaponDuration(lua_State* L) { int WeaponFunctions::luaWeaponDecayTo(lua_State* L) { // weapon:decayTo([itemid = 0] - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { uint16_t itemid = 0; if (lua_gettop(L) > 1) { @@ -465,7 +465,7 @@ int WeaponFunctions::luaWeaponDecayTo(lua_State* L) { int WeaponFunctions::luaWeaponTransformEquipTo(lua_State* L) { // weapon:transformEquipTo(itemid) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { uint16_t id = weapon->getID(); ItemType &it = Item::items.getItemType(id); @@ -479,7 +479,7 @@ int WeaponFunctions::luaWeaponTransformEquipTo(lua_State* L) { int WeaponFunctions::luaWeaponTransformDeEquipTo(lua_State* L) { // weapon:transformDeEquipTo(itemid) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { uint16_t id = weapon->getID(); ItemType &it = Item::items.getItemType(id); @@ -493,7 +493,7 @@ int WeaponFunctions::luaWeaponTransformDeEquipTo(lua_State* L) { int WeaponFunctions::luaWeaponShootType(lua_State* L) { // weapon:shootType(type) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { uint16_t id = weapon->getID(); ItemType &it = Item::items.getItemType(id); @@ -507,7 +507,7 @@ int WeaponFunctions::luaWeaponShootType(lua_State* L) { int WeaponFunctions::luaWeaponSlotType(lua_State* L) { // weapon:slotType(slot) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { uint16_t id = weapon->getID(); ItemType &it = Item::items.getItemType(id); @@ -527,7 +527,7 @@ int WeaponFunctions::luaWeaponSlotType(lua_State* L) { int WeaponFunctions::luaWeaponAmmoType(lua_State* L) { // weapon:ammoType(type) - WeaponDistance* weapon = getUserdata(L, 1); + const auto &weapon = getUserdataShared(L, 1); if (weapon) { uint16_t id = weapon->getID(); ItemType &it = Item::items.getItemType(id); @@ -553,7 +553,7 @@ int WeaponFunctions::luaWeaponAmmoType(lua_State* L) { int WeaponFunctions::luaWeaponHitChance(lua_State* L) { // weapon:hitChance(chance) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { uint16_t id = weapon->getID(); ItemType &it = Item::items.getItemType(id); @@ -567,7 +567,7 @@ int WeaponFunctions::luaWeaponHitChance(lua_State* L) { int WeaponFunctions::luaWeaponMaxHitChance(lua_State* L) { // weapon:maxHitChance(max) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { uint16_t id = weapon->getID(); ItemType &it = Item::items.getItemType(id); @@ -581,34 +581,34 @@ int WeaponFunctions::luaWeaponMaxHitChance(lua_State* L) { int WeaponFunctions::luaWeaponExtraElement(lua_State* L) { // weapon:extraElement(atk, combatType) - Weapon* weapon = getUserdata(L, 1); + const WeaponShared_ptr &weapon = getUserdataShared(L, 1); if (weapon) { uint16_t id = weapon->getID(); ItemType &it = Item::items.getItemType(id); - it.abilities.get()->elementDamage = getNumber(L, 2); + it.abilities->elementDamage = getNumber(L, 2); if (!getNumber(L, 3)) { std::string element = getString(L, 3); std::string tmpStrValue = asLowerCaseString(element); if (tmpStrValue == "earth") { - it.abilities.get()->elementType = COMBAT_EARTHDAMAGE; + it.abilities->elementType = COMBAT_EARTHDAMAGE; } else if (tmpStrValue == "ice") { - it.abilities.get()->elementType = COMBAT_ICEDAMAGE; + it.abilities->elementType = COMBAT_ICEDAMAGE; } else if (tmpStrValue == "energy") { - it.abilities.get()->elementType = COMBAT_ENERGYDAMAGE; + it.abilities->elementType = COMBAT_ENERGYDAMAGE; } else if (tmpStrValue == "fire") { - it.abilities.get()->elementType = COMBAT_FIREDAMAGE; + it.abilities->elementType = COMBAT_FIREDAMAGE; } else if (tmpStrValue == "death") { - it.abilities.get()->elementType = COMBAT_DEATHDAMAGE; + it.abilities->elementType = COMBAT_DEATHDAMAGE; } else if (tmpStrValue == "holy") { - it.abilities.get()->elementType = COMBAT_HOLYDAMAGE; + it.abilities->elementType = COMBAT_HOLYDAMAGE; } else { g_logger().warn("[WeaponFunctions:luaWeaponExtraElement] - " "Type {} does not exist", element); } } else { - it.abilities.get()->elementType = getNumber(L, 3); + it.abilities->elementType = getNumber(L, 3); } pushBoolean(L, true); } else { diff --git a/src/lua/functions/items/weapon_functions.hpp b/src/lua/functions/items/weapon_functions.hpp index 2f0d2e8ccb6..01ba6966317 100644 --- a/src/lua/functions/items/weapon_functions.hpp +++ b/src/lua/functions/items/weapon_functions.hpp @@ -14,7 +14,7 @@ class WeaponFunctions final : LuaScriptInterface { public: static void init(lua_State* L) { - registerClass(L, "Weapon", "", WeaponFunctions::luaCreateWeapon); + registerSharedClass(L, "Weapon", "", WeaponFunctions::luaCreateWeapon); registerMethod(L, "Weapon", "action", WeaponFunctions::luaWeaponAction); registerMethod(L, "Weapon", "register", WeaponFunctions::luaWeaponRegister); registerMethod(L, "Weapon", "id", WeaponFunctions::luaWeaponId); diff --git a/src/lua/functions/lua_functions_loader.cpp b/src/lua/functions/lua_functions_loader.cpp index 944849b1e6f..cf377592988 100644 --- a/src/lua/functions/lua_functions_loader.cpp +++ b/src/lua/functions/lua_functions_loader.cpp @@ -12,13 +12,11 @@ #include "creatures/combat/spells.hpp" #include "creatures/monsters/monster.hpp" #include "creatures/npcs/npc.hpp" -#include "creatures/players/imbuements/imbuements.hpp" #include "creatures/players/player.hpp" #include "creatures/players/grouping/guild.hpp" #include "game/zones/zone.hpp" #include "game/game.hpp" #include "game/movement/teleport.hpp" -#include "items/weapons/weapons.hpp" #include "lua/functions/core/core_functions.hpp" #include "lua/functions/creatures/creature_functions.hpp" #include "lua/functions/events/events_functions.hpp" diff --git a/src/lua/scripts/lua_environment.hpp b/src/lua/scripts/lua_environment.hpp index 216abb8ec78..042a21fe1d8 100644 --- a/src/lua/scripts/lua_environment.hpp +++ b/src/lua/scripts/lua_environment.hpp @@ -25,7 +25,7 @@ class LuaEnvironment : public LuaScriptInterface { static bool shuttingDown; LuaEnvironment(); - ~LuaEnvironment(); + ~LuaEnvironment() override; lua_State* getLuaState() override; @@ -38,7 +38,7 @@ class LuaEnvironment : public LuaScriptInterface { } bool initState() override; - bool reInitState(); + bool reInitState() override; bool closeState() override; LuaScriptInterface* getTestInterface(); @@ -50,7 +50,7 @@ class LuaEnvironment : public LuaScriptInterface { template std::shared_ptr createWeaponObject(LuaScriptInterface* interface) { auto weapon = std::make_shared(interface); - int weaponId = ++lastWeaponId; + auto weaponId = ++lastWeaponId; weaponMap[weaponId] = weapon; weaponIdMap[interface].push_back(weaponId); return weapon; diff --git a/src/lua/scripts/luascript.hpp b/src/lua/scripts/luascript.hpp index 80541e86d18..8f0b3b36c19 100644 --- a/src/lua/scripts/luascript.hpp +++ b/src/lua/scripts/luascript.hpp @@ -23,7 +23,7 @@ class LuaScriptInterface : public LuaFunctionsLoader { LuaScriptInterface &operator=(const LuaScriptInterface &) = delete; virtual bool initState(); - bool reInitState(); + virtual bool reInitState(); int32_t loadFile(const std::string &file, const std::string &scriptName); diff --git a/src/lua/scripts/script_environment.cpp b/src/lua/scripts/script_environment.cpp index 8899a954f69..ccebdb1a8dd 100644 --- a/src/lua/scripts/script_environment.cpp +++ b/src/lua/scripts/script_environment.cpp @@ -9,7 +9,6 @@ #include "pch.hpp" -#include "declarations.hpp" #include "game/game.hpp" #include "lua/scripts/luascript.hpp" #include "lua/scripts/script_environment.hpp" diff --git a/src/map/utils/qtreenode.cpp b/src/map/utils/qtreenode.cpp index 92d0a0423ee..ace469e14aa 100644 --- a/src/map/utils/qtreenode.cpp +++ b/src/map/utils/qtreenode.cpp @@ -10,7 +10,6 @@ #include "pch.hpp" #include "creatures/creature.hpp" -#include "map/mapcache.hpp" #include "qtreenode.hpp" bool QTreeLeafNode::newLeaf = false; diff --git a/src/server/network/connection/connection.cpp b/src/server/network/connection/connection.cpp index 04e7a82a64f..c2df08b2824 100644 --- a/src/server/network/connection/connection.cpp +++ b/src/server/network/connection/connection.cpp @@ -12,7 +12,6 @@ #include "server/network/connection/connection.hpp" #include "server/network/message/outputmessage.hpp" #include "server/network/protocol/protocol.hpp" -#include "server/network/protocol/protocolgame.hpp" #include "game/scheduling/scheduler.hpp" #include "game/scheduling/dispatcher.hpp" #include "server/server.hpp" diff --git a/src/server/network/message/networkmessage.cpp b/src/server/network/message/networkmessage.cpp index 9c7b5a333cb..457e3b32be0 100644 --- a/src/server/network/message/networkmessage.cpp +++ b/src/server/network/message/networkmessage.cpp @@ -11,7 +11,6 @@ #include "server/network/message/networkmessage.hpp" #include "items/containers/container.hpp" -#include "creatures/creature.hpp" int32_t NetworkMessage::decodeHeader() { int32_t newSize = buffer[0] | buffer[1] << 8; From 5eac8f5c02b865f2df4183c9506f3f2cf937586b Mon Sep 17 00:00:00 2001 From: Luan Santos Date: Thu, 5 Oct 2023 07:04:03 -0700 Subject: [PATCH 05/10] fix: allow players to send in bug reports (#1666) --- data/events/scripts/player.lua | 1 + data/libs/functions/fs.lua | 32 ++++++++++++++++++++ data/libs/functions/load.lua | 1 + src/server/network/protocol/protocolgame.cpp | 6 +--- 4 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 data/libs/functions/fs.lua diff --git a/data/events/scripts/player.lua b/data/events/scripts/player.lua index f176e5000c5..816aa523b70 100644 --- a/data/events/scripts/player.lua +++ b/data/events/scripts/player.lua @@ -445,6 +445,7 @@ end function Player:onReportBug(message, position, category) local name = self:getName() + FS.mkdir_p(string.format("%s/reports/bugs/%s", CORE_DIRECTORY, name)) local file = io.open(string.format("%s/reports/bugs/%s/report.txt", CORE_DIRECTORY, name), "a") if not file then diff --git a/data/libs/functions/fs.lua b/data/libs/functions/fs.lua new file mode 100644 index 00000000000..f95fba51b05 --- /dev/null +++ b/data/libs/functions/fs.lua @@ -0,0 +1,32 @@ +FS = {} + +function FS.exists(path) + local file = io.open(path, "r") + if file then + file:close() + return true + end + return false +end + +function FS.mkdir(path) + if FS.exists(path) then + return true + end + local success, err = os.execute("mkdir " .. path) + if not success then + return false, err + end + return true +end + +function FS.mkdir_p(path) + if path == "" then + return true + end + if FS.exists(path) then + return true + end + FS.mkdir_p(path:match("(.*[/\\])")) + return FS.mkdir(path) +end diff --git a/data/libs/functions/load.lua b/data/libs/functions/load.lua index 705fdf712d6..0ab33f5de11 100644 --- a/data/libs/functions/load.lua +++ b/data/libs/functions/load.lua @@ -5,6 +5,7 @@ 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/fs.lua") dofile(CORE_DIRECTORY .. "/libs/functions/game.lua") dofile(CORE_DIRECTORY .. "/libs/functions/item.lua") dofile(CORE_DIRECTORY .. "/libs/functions/itemtype.lua") diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 2848013701c..59c2867e519 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -6039,11 +6039,7 @@ void ProtocolGame::sendAllowBugReport() { NetworkMessage msg; msg.addByte(0x1A); - if (player->getAccountType() >= account::ACCOUNT_TYPE_NORMAL) { - msg.addByte(0x01); - } else { - msg.addByte(0x00); - } + msg.addByte(0x00); // 0x01 = DISABLE bug report writeToOutputBuffer(msg); } From 2b4c0c76ca798ef8edb1915985fcbb18ea9b2ca6 Mon Sep 17 00:00:00 2001 From: Luan Santos Date: Thu, 5 Oct 2023 07:11:28 -0700 Subject: [PATCH 06/10] fix: fury gates maps/teleport (#1669) --- .../world_changes/fury_gates/abdendriel.otbm | Bin 500 -> 2150 bytes .../world_changes/fury_gates/ankrahmun.otbm | Bin 573 -> 2408 bytes .../world/world_changes/fury_gates/carlin.otbm | Bin 472 -> 2030 bytes .../world_changes/fury_gates/darashia.otbm | Bin 676 -> 2557 bytes .../world/world_changes/fury_gates/edron.otbm | Bin 595 -> 2358 bytes .../world_changes/fury_gates/kazordoon.otbm | Bin 508 -> 2685 bytes .../world_changes/fury_gates/libertybay.otbm | Bin 580 -> 2488 bytes .../world_changes/fury_gates/porthope.otbm | Bin 456 -> 2187 bytes .../world/world_changes/fury_gates/thais.otbm | Bin 675 -> 3533 bytes .../world/world_changes/fury_gates/venore.otbm | Bin 587 -> 3646 bytes data/items/items.xml | 1 - 11 files changed, 1 deletion(-) diff --git a/data-otservbr-global/world/world_changes/fury_gates/abdendriel.otbm b/data-otservbr-global/world/world_changes/fury_gates/abdendriel.otbm index 9e6b2698eee239b2260d59cc2a06a80616390eed..c41c56415e9b25caa04bf53c4cbb98fd775a2710 100644 GIT binary patch literal 2150 zcmYjS$&M3O6fK4rUIq{i3Q0O7CIV#@3keoku>+KWEE44d+_)qxb+=pY4vEBqzW|A4 z%@Z^sQlyL@knix^o;Dd5<=lHt*{)5M?tS;1d+t!LEX%TT>$p6#NArg#YOu=nq^n|lY% z)^4@_px)fBZ*AMP*WR@1TMc`+(Qew?AMLbu_YdrL?Yo`riyP&G-MxG2eB(mddB63| zzOC;3r1i*pcq)Iz=@|E1UitK`#X^iOj8ACv$|rpjOW2hS4Rbr@xft)z=o)`*^vvPf z=p(F(Ft20&J11DLYyQN$Xa2>zPuuU9ztr}NbH8UYVY&pc--ncUHx_y2`KCM$zg8VV z$NZz1fU+eP$LgW>%PT;nC>9y?O;F1#zX-DOQr%K+Uis{_GKPhUu#0lvR{XPd!mzi7 z#_Ill6R5s&qCAMX`MSh>j$9GRepMYHqr9kZo(Ngz;>qb% zIXS&1jXA3K(2|&>AE-iVk6AP1N*YlSv(g_l1C=Jl6p}9HM5?3*u^eYY3{;5+ zG$!qGOli;jt*uY{d}p2F-8I3g@T_NEit#=KXT_kM0cOdqaE6?|GXhNdEu78~B3Oev zU_y7u{c%S)WTbn94P)cHBGMaDinoef?u&PXsu}JN*0cOgJB01ZFi?NE+&7WqlSjs< z2DRNjqeeiApA`K<*MWl#hG70t-LiKYDCNn@Ej_DEJc1k9B{4QpqQe2!iJKG-#Zc$S zJe9S$%QYnsi9=+F%9v;_S$H#U(N{8Bw)n97@ifa*O>0a=pAA4B!BOkYvzCV*7_Ok%_tP!m~# ziFvVN2^dbb0r_c!hSnFa>K+829yn%>#?54?X_*fdJZ2rk;}cqIAo)Ned~K0 z|8PIJiFt`-lT65XMYhDNw9eW}$yN$#wV|Z-xK$prXK8iT;dow6GnhW@G#C3o+JCMb zT)3XURDRgJ`_xLj>Gc#8=B6Lg>SCl}8L+Ag2RQ_wDonuaT^G$UUR&5sn4!$LCwK4^oR3Sd|yz=tX|8<|PYz=zLJnB>B=&4A%lEHPP0c(_fa)Gyyp{WAan diff --git a/data-otservbr-global/world/world_changes/fury_gates/ankrahmun.otbm b/data-otservbr-global/world/world_changes/fury_gates/ankrahmun.otbm index 04b378fa5780ac1edb1ad17d486954f75fa3d97b..d426bf442236303c41f258d6abccc85914252aed 100644 GIT binary patch literal 2408 zcmY*a*^c8x6!k0}C}5WE*3d{Tq&$FlB0}O}c$$}Nj_u?Dz939T>WEI#O7{#r&in+M z#E#e(!B_Aj2+6S>JKk-{p>CZU+PvhdZk>DX@u{jCP1CeWyDoo|r{*B?OodSCxZp*Db z+u&%516r?ic4WZOP-Z~c%9IQeYcyt;HpY$ zmEkEM8!f}S2IzNJE#=Alrzq0;!Ewf;Z8s`1;gvKVAP z9Lsw488*m_RJb<9J!#}GYGGR<-q*w7)bM7@K^yy0*^#`uIsU&?(`#;44mS?nL@GsfqP&pDqnf5H3( Z=L;UiCG(X0U8Neu+e-eg-`Q@r{{m@?u+{(o literal 573 zcmYk2yH3ME5JfjH-iRUv719U?DS27i^7bmh6nyQ0tAy6HPE zztXO?%gPCRakvFlm)?hK_I=Z_xflwwC*FHf+ix;qf4ai32KI&57b9EyvU-RARl^y0 zGE4uec2I!S_gSAsvsJGwqg_tYW(7s zh=wjy8y=rMlpXV@l7dGfXev<9DIO{^qfJo=v$BM+sK*!3RCP(2*C6GKC|U$fb!_NI zG5MAhV+B(GT~VMl#f^|6u`Ivmg~Gv8uq8Ra8`E!-^fxDrW&7V2z|}Yb4up$qulf_>54j zA-wGv63B}$;5)L;F3-g%-F@zrMK->5`}R3~y8DbA1VPXQ5&tdk4)4T#UXVx>o(*nP zH*LRltKJ{9uG?)pv7hg?)~ems#eTg%p0vJfpKqTFKMlSbx3)Rjw|l)wy}MtJcUslW zs@|%)Tee*;1-)vrRquSUJ>J>dx0Ckm?XA-bKDe{nlfa3SJ}?-6y=NuVB*ER_PS&L1 zIz2cwZPG~2#$5Yo%9q#$llfrkf(F4$xnySR^i<%vd!%*xmT9U5c`RJ8bZ6Mn8dtSO zSkVDzUQwE`OOJ#m>}kfCrD=}DKKNU3B#GwsaPOLB1Z3q%Ut8oEVFH91;h6vg8_6#G z3JE}@6|PQEX_GDpS-dSj{$gHRi5aY@rm~_#rp9K=`MH%ndMqu3eVx)LOEQbl>SEkw zLtj?#vPpkSdeo;^ObFLTmL6&lnQS7-*kpq?2C%pf|HPK7y*J!=gC4aTbGXc1{K_Q$ zL)g^tj}lQ+K3Nvhr2oHcbd*>Pb53L4oYF+lzLJQNVB^UqeY5O0o`USD z!1LSbWA_foCp@Lm}}=4>oXlD$x>#1v$|#HL;&B)P@HQ4WG#Mr z1Z`d81I{?c>k&Jq0~_jsTNVM8|tPKxW}$P3vWwiypK>|8rS)dcA8Bn{(8 zfPM~B0;4uXcgbjuB?bCOb5j7dBCpyvor2;AiC}IiC|WuOGobtIDEP;K;<2cCt==$0 zdd}0w$c!6FHc+cO&%KZFZDd?_2Fym7X%4;cNDko7B8hhs}+)$T0AOXnwxh?%y sLqH>qhS&u;fd$g>Skzcx;TBhnu)xwSwseax-Qr7qpEm59#p!hVAJ7JCP5=M^ literal 472 zcmY+9y-ve06os9{xk(ccNDNF!9zaY)LP(6=_*r0p2LLxQ#8v*3gqES|1M(cqJc7eb zj9szV*WWol$0mdjMO<9t?Q-McJ|BteoRVk05*b^|K4(v&6s7P7kN8osWQ&$8IJD6c;{F(O6DmTUs$ z!;6C3a2S;Da0U0%jPw31@z7L#*_e6oJ>?;uZ3vQMUlPciGODL zX7cE}$pf_LSAkUl)yTt+ucN6FkKf1i=L|`>!!z{j!kJBraMEe9sn#E$xiQnFvG~$F T2T=X%+D)jA@q@zO9^n29BNfX> diff --git a/data-otservbr-global/world/world_changes/fury_gates/darashia.otbm b/data-otservbr-global/world/world_changes/fury_gates/darashia.otbm index 5f96244586bcd8d2568b5ba2987225a75afc987e..4e25939682431fd70573c8bb712067300098ec16 100644 GIT binary patch literal 2557 zcmZWq$*vn!5bfk84VS_0d2kzKArNdZNGyO&b}SHt1$=?Yv-B*z_R!t7+nX%%5r~AD zpfH34i9te$9SHFwh#fn|MVqQR)qXA5`0Ca@=Tz0br`ncfS&em`e$#99)gxLjape5D zN3GW;o9XdrJF8AcuTJx6F@3xoU7jpQFC5RRMKOA2{M`8Ia}QasEJis+kEi98Vz#Vi zi}`4>naoZnub)oG_XpPTq?nW^v&obBVqR9$VqE8^v2zxlFRyUuLGLVdvRIc>4mKC8 zYu43!nv1>M{A%rKo$I=wwXt8mcTd4SA2#-jiEJ3r*q0uV4Z{fG00FoI1mKP$TutEO z5J(qUpyfg1Yj@wK@__x;Jk47;U9xw(0OMSy+J>0gqJ5wKKus~>~#u+$LG&0Tarx-qcA z#yupt`HPqrler`TCt1jbx~7vfH~%mqotxif#UYS`9KpdHpuxiY$)GGZf6J=zY=Jbx z9OuKi`Ge1gitjD)w7r$Cg{~#7@dr-e>sXggK3kcj$wNCVN$f(_72td3n3u zMmBib-c~khHs(-;eTMVONM0F4Yp>RIr*Yo5FVNSZgJM^b%hBE()Txf;I!Vq2Qk{yn zU@sSNJhppit8LT+5o#WYYVpGSaMV`hmFA}-%HuPfS0L5Xh51nqOLJ@7*dMUl?`Gm) zs3Uox4FtviU?9H3#4m*yF3&KiEnG?DEfsH{Foal_TACXfl^8+dLnc5Xg2cNV`Bc(G zg?U4;%85}ghByXX1PI>VSj?QpzK=S#fA;AI*A znYlYsNP;%5b7`M(E>dPHF6RCJgwthrz#!Hc>9iw;R#ND^$2wGQ+sa@mUl5(em52^x zY6l$&`BrHdI@n}O9o7|wF)MT}bvTz8>fuUkt|0iaxV0Fai-RB?gZ(xq6GI?Lypa7;EjhoTufMIHTz7rDF%I3E zSoR4xd23u|XBFI-t?^fqZS*f@t1i{^;aXj~(Z8H6a1K7uFoB~VXcs|L?(R^hS5pm? z(c2-jU&H_Y4$48T>kjd-HXh?c-A6S!i}ukD6~wxacc>)MeX>L4X^lLo>JLjte9gASx^&B+7JD5DNH$1D_O2acpcSag`!p$B)qR31`Mx z--$GLtC`u^*=39|$=Z0meYxx4pVdgab)DK@My_DmLj+qvXEyyUqt*aPx*Egl=1TNAGk=?xu`+e zWqY0Kl09=J`?En^S+T-wV8q(c5$kBy0X9GMj-oxSBOn z#hRINF>^J;C=_dKR03nGShG}^4VNY#Txgn2F3NQHrP_v&kk|o6)kwz}10w?$6|Lo> zO>Eq(iG6G^g_ySWw1I^alBtcVHmKyFk|zE?ayT(w diff --git a/data-otservbr-global/world/world_changes/fury_gates/edron.otbm b/data-otservbr-global/world/world_changes/fury_gates/edron.otbm index 72d3239b4bb021cddd6cd94880c61fa98a30a22c..3ad2c77ca4a26f8a09a777beb730c07f68fd840b 100644 GIT binary patch literal 2358 zcmZWq%Z?jW5bY#0PA(9l35yVhhXp~bVj)1{A(R~p0$~B-1Ds4tu{0hJo*}TvCO^Sz z3nX^C+mE*0&0FqwH{ZdU9nVFZsyeshR@k^zbo;F+z1Dj1#8bw5y=IrPd*;!N ze&_JG(>rLkZ?!x7?d$ty>){>4-0SxaE_ZteN5^Kr^=Wti!daa=IK08}*>gI++57m& z*4V-k|g^oH?weN9tQj zzjKB$!9cxQ!*FJ(zNXBZr)AlaVDg~^BMJx+4kUT?aZZMSzbk5?rEEbkvTOhm4b*58 z01m;(Qg>+u+z6a2k5ISQL@h=g9SoI4*!H7bG`3_5(2qfU(j62VwT_Y8uG`kUc_ zEeIMn2adFhNe5$fw^cUggkrU_G2W#g4V=gt>6VSbE>lBWG9IIU;ve`bbT;Yl#H1l) zeZ~w~@v1m1oTx8%D^>)t9~q;~107;ftq2mrMwtj~G#7&{U1)27O%3Q>=rnX(?n1k3 zqCTT&JF#p3jRb7blbR@#jMZ0yov6?Gn_`k=iLwD-r;a#YGDKtybE;jYT&${`+3HVf zZ##47T*`(c@C+w*ktHo;FnBo>%2eaYZ5)2?=Z|& z%2HG|Me`GG6?;r;>=CCis0`;J{y+m6&OYN=yS+t1f_TT}+3p#;mCcLGoT zaJbED2{Bnw&5!LqtNsLKR9!+VpU7^as}F%rNNlP)o$)WhgfAEV0_ w$|VpOnVxIr=_|*_Q;;t(uzaa65l`p!da*!UEcLCS`GwCq>yln~xnqo%Pq&I_sNTt?tqnYpWL*D*E+}tE_wW zLPfXI`E0Yr+H$UTqjvplIp5eZD|6Fw-c{+T^i>8bLzNNB((&h}1d4aeHw&fn@xrup z-g=QW$&UG+G|lsTNO#OlGR-%3aYtiM<-W=uq^ovU?XKEgwR>v!)b6R>Q@gKrU+uoy zeYV?J19b}2DPX74i6xPsXC6}6Elwml%dv;*9h=#iOZyn9l|?GM=!0|>X6F|6#FZI;QdxjQRzvZu;@lBIL`9K#bP zE}i5VKBUafa?gBAN(ysk*_Y@Mc-oWJU6Ix`4k!!4G!9iBsXT^s)$Xd@RlBQpPwk%C zJ+*sk_t|b+#L{Fo~09@KL@rzYjwjsasVTL2&$8v^)_ zR3$tVmSqO6^9S__k1ToVtO;Lu{G=uVkFA}pb988KE|t!2=;o0e9xf8|&rJWW+!`H= z`VZFJUX}_52lh^TLjsCB?E8y^k{qcoV{D!& zfc7S$N&%Ehs2297RMDy4*yvMv$-Oa9IjoesF{&I~^>o?OJ_2_r)s$C*N8pak1L~mc z4OHA?papn?Cjs8@PiQC_u|~}Wg<9t!jcZ84%>V;s8U38Pop8KbR>)Gl05Xr zxH346N#)>J;LrK6RotS@Enc=6} zmLbgbw_B*p@TY(=+cWnFeuZbYBkr00DY70}h%48>SdQPxCOG$HBrCAIN985=Lfs4f zM;7>50aoB2saIo{{LWX)-od10hcZf7e3`D1OxH+$AoU=3j;!jLt7opB1s0@4_<+X; z*%es)cUgnKbb-}P8jc=|R$>w#jY%I#Mh}a2wd`RUZvtc(Q0kWLUxrynL-M`=WTLTx zb5Q)sS<6@pR5mA4&W8e#?XM&w^S6{1WIt7*95WcQh{lz{lX@fsEolgG;h@1U( literal 508 zcmYjN%SyvQ6rDbd#Ro3Ba3#z_ab-XRL6atF!Ijp9e!*x48cZ@FiLFSX|L1r3epLGd zQ%^FT=w|NW+;h)8cL*Uwk~-erWHxS~It5d&ZIeqjx!NW->|Uh45b<7?uu*K@MdA8?b|7vu zUa+!BCS<%Umz-y5^jq^e(2F#jwF0?M8dI4!%@o91kA(CIPzT^eVlqyvc(cfc)7 zAy!@ILmRltN=uHv^_T^u64;-5%m$~dI#aE4&|PUD!9%+Y{-u$N#^Yvlx`STpPvBO9 xqaW>N0jXd%&|>RMZ}6(SJ%m-dZiRX{MIGu@nYvi5a(1z;{GVcMF7Z2_~Vilx~!GX!!;?)(4{ z;e>(`vJSD^Znt}}&-@M~Zrp&tp@(|)mB+sHQ`P&bUe#AiTb5;2)=~O~H;Y@x=)7Vy zM~}Q=ec@l%QT^-Q-nIJYdPDE%%e(cf{&xMtsJFMZQ-8PdLF4Tsm#ojW>Kmku^lrG* z+urMKZPxwkesA3m)^+2BHEX>W=$*YAfq&zzjjheyJ-yTTW@G()ZTVtzJ2cAEtINu3 zTVL&JqppryH?3RGRVSK#^=##U&Vl;ZoUOjPvr^fYY6nIbuzF6>6YaeczZ|Ic3mJUp=N*U*(6Kfx1V+%f{-l z!NAn*tG`Ty?m+!#&Te1*&8I}(j3twyS|!sH&5?SsR@oGo5fT=DVWAbPKdj0gnAk3x zSAb~^)deyjOl2d9)`)Za$zb|0P1Fw;EBjMUrXBP3k8)kviShXSG5Y*=mbn?K?+7iz zNPSNNl4&P&LveLtM*M#M_@8f**~x`%XW6zhVi?Hkh^-TIb(q};9Gx@dfN~SY(*<+R zq4oTEC*fF-r5mdUhvd6Mb~-1YLUcg_v&Cw9tFq6YGYPs0KR8#DrW>n&xGF>Hr^;Sq zSa*cdbQ6@WIbNzpGhG%Yszx<D(~|CCBqRcQSH7*N%91Qikn=bJ*NAHWNeqLt}_4 zI~g|`ce^z4m`xXB!DOPHT)5>sazYQf@LjI3ka1=9zcfO3%mech4@@^zPl#*q)15F3 ziFGsekZ$0=nPbG81!!%SB2O_$tsGTq6>^psq}G%QL~pipA-RyVRHs=#r7Y>blLOZ& zM~ z{C9KJC)mflt-6J}PlB*hs=M5)Q=Fb`6i+sc-$$bU0E7g6^mZ@+fuYEu+(%*`iL7O} zYQcLk2=|8Y>y6;YGhi7xHAcj#e6nP{;CBTO<^x;r~9O1E0 zW-pTaNbDoAj}rLN;$II%mf6(_Nbtt$R|*!Z+Do|&X$4WF9y)K8)9}!(Ale^uf>;KA z%EdwJ`mjOk`q@&Yg0YaEa+L`_@8iJO(0c)%z+i;}57VV(7Q#DkNJJ8595CLC#3o9K zs`;*pCj9q7ei0&~{YLNUF&Nf>qg;9ddUZyZ^BQ_Nj~ycO(BGNa4ey;{qR`(3x3q^| zMSJ==b>e(`_%P&cE(F04`@o-4riO-`Q^H;d3iXH_Albmogh7}m=nR*qCoH%Os8(3= z1`$EvQO-(U?pXIx!Q`?A*r?>{paCMJ0<4y8qh*`uQaz=}P<9O@Vs6gBYilNZW=0cF z;dbP7%*h*IzB1gI4DL+*Y!dTkoD1gNhwwgA9n6+mDx6V51dj}YPR-{*b?TrZ@WKD- CG literal 580 zcmY+8yH3ME5JfjH6G0FKNK{B$fT*y9kSNnpK`5Z}BhK1hDVFRwvQ0voydczZ*@#CEK*qB~~ zQITf!%se+wYX{^yr3ttp&x0%)7g0K*Y{H_L`7t-Q_DCH0JS%2Co1NriHXWJwNxc2f z9;SCWFKy8zbF$bFE&D+jmu)FU%UKGehYGxIr}SJ<=vhySvtER8^~c$d!q{v`VSMeD zunkD1p4FNI$oGEp=Lo3w8g(@409ti{hg~ju`U!=2qV=kj{idc4co^WktHWGSRn&)4 zec#2PJKwm6kqoSx+4G&xd9~OeQd3C Rhp#(=>H;Y`QtU`6e*x^=8f*Xn diff --git a/data-otservbr-global/world/world_changes/fury_gates/porthope.otbm b/data-otservbr-global/world/world_changes/fury_gates/porthope.otbm index f0b1b943c76bc6c716f9a7ad5a15fd05307007cd..2232ecbc988b575df858bd4b4a47c0bbe8f2b697 100644 GIT binary patch literal 2187 zcmZWp$!-&A5N=2?G%(P6_umP%IAmADa?E>+d{)&93-SypN7rLS4rT$>_!l`VVs%vygnR`dDn zUbDN9-OiWtPJV7ByV+=G*XEnuRwuh$zf!-n=a_Y?l`WBVK3|#ZG~3-~YdLGIHkyl# zzZdiRp}=alI^BgR3x|E9#kO5c7`FKcI}$$gSB&u8d!aEd-dpV3}V02dU% z7En&IMbKa3>91!Ls zAv~!lQP2L#{YBg#$yoKcZ~x1B(zAbv>_eKK{f%{__*CbU_U(Vz?hK^QKe>=+GAF0_ z%`z0rIqsnf97)A8zE|8DYIg_rgPEN%x`im}8H$2_-H`-6%|YKy2ICFvN4so~*;J&* zM3y7_F-spsXUu)i+?$wnp>BoyBgqG60FJC?RyR(eg2H}C`E)DQ0s|B%n|mvh2?x?} z#%$UpvBQG4QPy5S!I);bD%#wx8Do-Fznl~s4RnzO77-0q2i(ZE;{moB7uaU3ptuT3 z28fUpdICe7MgkCy$Y>-}z-bg@M!1hu%Skm;Iz-TPgtDA<>HV~n<$Mt31e6Wb=4^_Q zUK|a^U7wr`-3D90K>&9{uyjo90g>*A6c5A`dKPT?@JpzjWqSxg$FytN6d*lWV)^T` zd+gOm2nqU>SAY}rH+CX30c^StQUX*V2ZrSa2tf;EH;A-pR#1NX2*pLZ5N!InkUIqN zI1~`ia(DXDnN6?=963)!i9jSQySZDINcQds8)Is`RFSFnlPbvhSUCviQ&ssair(55 zPTXZUC3G3d5+FUUtqQ~YSlO|Mz^gJn4;+lg1BaeAMf1VIKEbm-HzLyCaG-#!%cbehA(ezYbqzFvjvi)uXXQ!ac>T UaN#PA$`qhE%CKG1TR%$je|dcVzyJUM literal 456 zcmYk0OHRWu5Qg1XsGta9r?g!lc>!WYB!tAWJ62E@Z~{Xt#ZppRNod*Sc09_Xmtf5i zY$kOa*d*g`zJF#ALP(=Fd?)3)?7)9ok#_3{-LVCa=u#9hz2zy-_(@J@Owr3o6f&dd z;YE1XIz%^;ra&W}KV(7`Le42$Fp;qPgoisADVY_qRQxnoY&j2~(q#7^jOAm_E2z7u zMCF$5`7her?ilT@h%vf{;rTu4{u~48|6%g1y{mpD5D>06RRC-{s8|5ZcK6?1<-fX- z{#(OVF5!b`r{KmTP|fTP=-MiPY`v|?4x29ibRPcNOmWW&rx~jU%wg#iI09gV&7|dcLVc@`l#T-CM}9P^EBYv+p0{lLuEw?Evg zi)Fr4xKX%XoFBEC^jG+R$Ceq}^smf|P5MX27X6{4vD?OOx9Cr?+ie=H&aIQt1D;S5 zMrzUD;v_gvsMBde*=bQf%oq+3KQh`n74gsQW&XKji<;Y1;6uuGTXdgUig^oeP=65W zbXf>qlLm}={kTqAG~&rc??BJX#bUzTdQ)}$Cf#J<8^_rKWgi)`g+=|gW^qdhaF--6 z_LKlavZXeSp~9X3nZH~VfuKdJ?9n_CF=*2hZ23tt=lmE`ZqoS-^M0t77Cp|EZv@kT zrA=qp^4-D`Ht7Ynd?A*riV0h^zQlTA8)MX@3;#z%1`!!V+|)QZjaU$K>%x+x5gS;g zNzbs5?$efrsgKx0>d}tFP`yq^0h>Co3s-a*bN->f~!lo>}7VWfqzAvxzn&TWqj% z5o}0szls~|$)ZVYNZc2TbA*3>i3Lc|0gE7^81w=4=o4lfr9OSOoN*j*v{sB|x6=j8 z=`l>^eazhsk6B;42ILw|*MPhZeJUi+_&xDwXU^->=i>AcQwx7jWBLZ}8#x~f{7OoA zM-iot9sxZmsCfwsRFxSpC=^fT)>@H&aL7s!)80BkExX8^Zjf;l5yR&E!av4+=Oz~;EZ`QJ_I4wZ#%cXJZV z;>x&Vy@#=f9tx-33xWqWupZbyOB)#i5-^uD*t!f&2|MOPLRqN2&pKRt^qoJ}{J2#( zn-V0xlnA_iVi|Iy%81z7ExJXf8Nt@8_j_TP{u}6(r5&)XIsrP#T=NjqOp#;&9ZeDv z)`TK%siW2ukE=B^k8Wxn-3cY)FHV6g8hcXtjI|zko??%I0xTc2?ewtfa2}CV85-v^Yj_x#wP$_|<4e@LsSqDr!Xr5ro z=UR-jyfM`m20_amebaKcOu0+Xvw*hDz80d~!wX#Q>!bLhzItH?#;|Kf0}At?$bcdP ziVP^qgJJ`U4JbCCI1j2AP{n{M22=s)CA`D}I(W4Oc`y<9+%mAfu368+2XZKum7R3x zDPFkNa#I(cboElsEZKvf^t3{mnd_VM=`2%qw{{kaWgIho3N}#6Aw9{!{O1y`NVH8E z_XV0U94J=P1Unqk(^(j7UkJ7#@F8V4JkdbEa`ZL?I~vkmc5~}y&uWB=2F)CexwCmK z@j$=va>N*S0|%K~uj;o6;web60mZ3a9B@{EG_^orGr>tU6A{iH uf~s6p9IXoYWsOpu@pCW6RG%pr6H#YlB1jo$xH*!PQt3ke8^Kq_gZ}_qDm@hd literal 675 zcmYjO%TB^T6dhiD(3S!&T$#*5UM%Mod^;H2PS(f=>i9|vJ}k#;A-K_&034YcFjGMYM7|Iz3Q%f2YRZE&PE(NZAXQuvCil*Hd2Dy4n_qa!+K diff --git a/data-otservbr-global/world/world_changes/fury_gates/venore.otbm b/data-otservbr-global/world/world_changes/fury_gates/venore.otbm index 1ca21d586d1592ce89e867863c75802fb41a100a..1922f21ab60638f031a404dcba69b5ac3bbeece1 100644 GIT binary patch literal 3646 zcmZWs$&Oo95OpTgH)J5mB9S5_FF>qlAt50oPjB)Y0(hS70G)0pF-Z^VZUUP~+3*WU ztbxoE0mOpeVTt$zH&y4{*M5P`dve`dr>0X?&$(PKx5yopXLN6R_n5pZDmi-Oom^+_ z&TOOb&3yl6p*`E2?atoYD}1)LQ>bms_qTToAFh79`oWR6bM@`Qrbsttd+WRNo&EXt zR$=YV+Whv~wcE4RH_zwp%(k|7XYX%rZ|&{Rc2~dMynXRZD%{#xSHbDGQo+sbulHsu zS{%>a%iVoraiUl^@%aOJH~22q&5!36`I{okUq5poCnY@*34WA$yrIX*B|R=TmI`0t zPK9?i`J-VT3*MKiz~ibOi(<85?x!a=aJSerEwOa=g5nliz`cGRKicMj++9ZucvOMTinTQWn@`obQYNctKa`Qx`B_8UV5OD6%Wwlx9nD0gD zM3re+hox!@mLv@@qQ_Lcrjl1`gn?A8cCl+dRs3Slv?YR*7vu(^ihW71o(vFwCY(|i zMo@ItxkPQ7)V}#d6-xs^k``s^AaN3_9G81bl&9+0E#|7vfXjnSX)0ZbHFdp0ML@k& z#Ta#}^#6mmbww|T4Uvv71kN(FID%xZ z5~ia#1~Yd^(}{U5tRpi^L)$l_3}H%RgnF7RoGjB#fb!6M$I@ulMo212Uml~^Y=$R< zt_&G#WyH!>#`ZmdCNZl+bQ4O*&Z~~3YH2G}$8dr^S0}hDi4gn6DNaBqLX{{1udTzB z2vQW;cx`ZG=m#Rn$tiBBd2pn+`~~Kpl)N?$#mc2G0MVxc$_qVATj;A zNt6XXM+u_~>WB^XwM^@v}au6WTuMS zGC#2UN$P4hxI zrG3G|M|1#pX)bEB-J=d>0k6eP^OGpz8b?xW_ek94l!!a*g`_F(bmS-YQqtxOP8bRs z#nIU*0XTMSI(^iZOm;XdA1rI@a?~>auIku87@|vUbOyW09iR~b!L90XXCQp3x}^ti zdt3sD5NB*JuWz17;27-a z-WWkVAaufLGZ{^&$eoce`v{AHCYfbyhZ^^A=1b8TA!L5cIb*O1PLmqW1XV=#2;mMf zvbpZv5ftDOV{*sjf;%x!QciJ$d9%|}vT{C&7}P7{@=_{vlt PSLOGqgT>i{gM)tox9nEF literal 587 zcmX|-O;5r=5QY~Dwecf*^JKCIjVGHJ6E8}EvZY=`0l(!^?Itv#l(e;YA>qe*_9twe zI?P_$+2@^^ecxq_F~#ce_1BY?0e`0=_1ZBT2ZapzGRmiXBx9M%(~RE+3+{zco}~Q3 zzO>J4M{JPr7-%T7&oo-(Q8MR25k#}#VPNAW;KV;-m& zv;(#`CcrGRE->tKbrhk43LRAFc%Gi^gNj@F$Y{O$N`m`FX?riE_CUoe#XG4!R2xGR MtV#t>vtO3wAJ8=dQvd(} diff --git a/data/items/items.xml b/data/items/items.xml index 8c826d3fd64..d23468cabf3 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -15098,7 +15098,6 @@ - From 1ac6d8ab436e05d3397195ad6c07da65c9b298a5 Mon Sep 17 00:00:00 2001 From: Elson Costa Date: Thu, 5 Oct 2023 14:41:06 -0300 Subject: [PATCH 07/10] feat: reward durable exercise weapon (#1566) Created a command to enable players to receive a durable exercise weapon at just one time. --- config.lua.dist | 2 + data-otservbr-global/lib/core/storages.lua | 2 +- .../customs/reward_exercise.lua | 13 ++++ data/scripts/talkactions/player/reward.lua | 65 +++++++++++++++++++ src/config/config_definitions.hpp | 2 + src/config/configmanager.cpp | 2 + .../functions/core/game/config_functions.cpp | 2 + 7 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 data-otservbr-global/scripts/creaturescripts/customs/reward_exercise.lua create mode 100644 data/scripts/talkactions/player/reward.lua diff --git a/config.lua.dist b/config.lua.dist index 76508f6d7d7..a00bb061f81 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -186,6 +186,7 @@ onlyPremiumAccount = false -- NOTE: buyAolCommandFee will add fee when player buy aol by command (!aol), active changing value more than 0 (fee value. ex: 1 = 1gp aol will be 50001) -- NOTE: buyBlessCommandFee will add fee when player buy bless by command (!bless), active changing value between 1 and 100 (fee percent. ex: 3 = 3%, 30 = 30%) -- NOTE: teleportPlayerToVocationRoom will enable oressa to teleport player to his/her room vocation +-- NOTE: toggleReceiveReward = true, will enable players to choose one of reward exercise weapon by command !reward weatherRain = false thunderEffect = false allConsoleLog = false @@ -200,6 +201,7 @@ toggleTravelsFree = false buyAolCommandFee = 0 buyBlessCommandFee = 0 teleportPlayerToVocationRoom = true +toggleReceiveReward = false -- Teleport summon -- Set to true will never remove the summon diff --git a/data-otservbr-global/lib/core/storages.lua b/data-otservbr-global/lib/core/storages.lua index d5f434274b0..b1795ce1dde 100644 --- a/data-otservbr-global/lib/core/storages.lua +++ b/data-otservbr-global/lib/core/storages.lua @@ -140,7 +140,7 @@ Storage = { PremiumAccount = 30058, BattleAxeQuest = 30059, ShrineEntrance = 30060, - + PlayerWeaponReward = 30061, --[[ Old storages Over time, this will be dropped and replaced by the table above diff --git a/data-otservbr-global/scripts/creaturescripts/customs/reward_exercise.lua b/data-otservbr-global/scripts/creaturescripts/customs/reward_exercise.lua new file mode 100644 index 00000000000..f17304cec00 --- /dev/null +++ b/data-otservbr-global/scripts/creaturescripts/customs/reward_exercise.lua @@ -0,0 +1,13 @@ +local winReward = CreatureEvent("WinReward") + +function winReward.onLogin(player) + if configManager.getBoolean(configKeys.TOGGLE_RECEIVE_REWARD) and player:getTown():getId() >= TOWNS_LIST.AB_DENDRIEL then + -- check user won exercise weapon and send message + if player:getStorageValue(tonumber(Storage.PlayerWeaponReward)) ~= 1 then + player:sendTextMessage(MESSAGE_LOGIN, "You can receive an exercise weapon using command !reward") + end + end + return true +end + +winReward:register() diff --git a/data/scripts/talkactions/player/reward.lua b/data/scripts/talkactions/player/reward.lua new file mode 100644 index 00000000000..7c2999a587e --- /dev/null +++ b/data/scripts/talkactions/player/reward.lua @@ -0,0 +1,65 @@ +local config = { + items = { + { id = 35284, charges = 64000 }, + { id = 35279, charges = 64000 }, + { id = 35281, charges = 64000 }, + { id = 35283, charges = 64000 }, + { id = 35282, charges = 64000 }, + { id = 35280, charges = 64000 }, + }, + storage = tonumber(Storage.PlayerWeaponReward), -- storage key, player can only win once +} + +local function sendExerciseRewardModal(player) + local window = ModalWindow({ + title = "Exercise Reward", + message = "choose a item", + }) + for _, it in pairs(config.items) do + local iType = ItemType(it.id) + if iType then + window:addChoice(iType:getName(), function(player, button, choice) + if button.name ~= "Select" then + return true + end + + local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX) + if inbox and inbox:getEmptySlots() > 0 then + local item = inbox:addItem(it.id, it.charges) + if item then + item:setAttribute(ITEM_ATTRIBUTE_STORE, systemTime()) + else + player:sendTextMessage(MESSAGE_LOOK, "You need to have capacity and empty slots to receive.") + return + end + player:sendTextMessage(MESSAGE_LOOK, string.format("Congratulations, you received a %s with %i charges in your store inbox.", iType:getName(), it.charges)) + player:setStorageValue(config.storage, 1) + else + player:sendTextMessage(MESSAGE_LOOK, "You need to have capacity and empty slots to receive.") + end + end) + end + end + window:addButton("Select") + window:addButton("Close") + window:setDefaultEnterButton(0) + window:setDefaultEscapeButton(1) + window:sendToPlayer(player) +end + +local exerciseRewardModal = TalkAction("!reward") +function exerciseRewardModal.onSay(player, words, param) + if not configManager.getBoolean(configKeys.TOGGLE_RECEIVE_REWARD) or player:getTown():getId() < TOWNS_LIST.AB_DENDRIEL then + return true + end + if player:getStorageValue(config.storage) > 0 then + player:sendTextMessage(MESSAGE_LOOK, "You already received your exercise weapon reward!") + return true + end + sendExerciseRewardModal(player) + return true +end + +exerciseRewardModal:separator(" ") +exerciseRewardModal:groupType("normal") +exerciseRewardModal:register() diff --git a/src/config/config_definitions.hpp b/src/config/config_definitions.hpp index 6a785280e56..d3bf0b87eec 100644 --- a/src/config/config_definitions.hpp +++ b/src/config/config_definitions.hpp @@ -91,6 +91,8 @@ enum booleanConfig_t { TOGGLE_MOUNT_IN_PZ, TOGGLE_HOUSE_TRANSFER_ON_SERVER_RESTART, + TOGGLE_RECEIVE_REWARD, + LAST_BOOLEAN_CONFIG }; diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index 3eacd9e9ade..d0abcc9352b 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -392,6 +392,8 @@ bool ConfigManager::load() { boolean[TOGGLE_HOUSE_TRANSFER_ON_SERVER_RESTART] = getGlobalBoolean(L, "togglehouseTransferOnRestart", false); + boolean[TOGGLE_RECEIVE_REWARD] = getGlobalBoolean(L, "toggleReceiveReward", false); + loaded = true; lua_close(L); return true; diff --git a/src/lua/functions/core/game/config_functions.cpp b/src/lua/functions/core/game/config_functions.cpp index a64f1d91b66..bdeaa226fc1 100644 --- a/src/lua/functions/core/game/config_functions.cpp +++ b/src/lua/functions/core/game/config_functions.cpp @@ -251,6 +251,8 @@ void ConfigFunctions::init(lua_State* L) { registerEnumIn(L, "configKeys", VIP_FAMILIAR_TIME_COOLDOWN_REDUCTION); registerEnumIn(L, "configKeys", TOGGLE_HOUSE_TRANSFER_ON_SERVER_RESTART); + + registerEnumIn(L, "configKeys", TOGGLE_RECEIVE_REWARD); #undef registerEnumIn } From dbe353c22878b7e6fae08aebbed50794bc1e96b4 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Fri, 6 Oct 2023 11:48:57 -0300 Subject: [PATCH 08/10] fix: suppress compilation warning (#1684) Suppress false positive warning for type comparison by leveraging compile-time checks --- src/kv/value_wrapper.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/kv/value_wrapper.hpp b/src/kv/value_wrapper.hpp index 9317eb6b8fd..a4e899adcb0 100644 --- a/src/kv/value_wrapper.hpp +++ b/src/kv/value_wrapper.hpp @@ -179,7 +179,10 @@ inline bool operator==(const ValueVariant &lhs, const ValueVariant &rhs) { return b.contains(key) && value.get() == b.at(key).get(); }); } - return a == b; + // Compares a and b if types A and B are the same, at compile-time + if constexpr (std::is_same_v) { + return a == b; + } }, lhs, rhs ); From e133007fbbfe489b10a71dffd342db48862d4b2e Mon Sep 17 00:00:00 2001 From: Luan Santos Date: Fri, 6 Oct 2023 15:07:07 -0700 Subject: [PATCH 09/10] feat: monster critical chance and multipliers refactor (#1679) --- .../monster/bosses/doctor_marrow.lua | 110 ++++++++++++++ .../scripts/lib/register_monster_type.lua | 3 + src/creatures/combat/combat.cpp | 139 ++++++++---------- src/creatures/combat/combat.hpp | 2 + src/creatures/monsters/monster.cpp | 33 +---- src/creatures/monsters/monster.hpp | 29 ++-- src/creatures/monsters/monsters.hpp | 1 + .../monster/monster_type_functions.cpp | 14 ++ .../monster/monster_type_functions.hpp | 9 +- 9 files changed, 218 insertions(+), 122 deletions(-) create mode 100644 data-otservbr-global/monster/bosses/doctor_marrow.lua diff --git a/data-otservbr-global/monster/bosses/doctor_marrow.lua b/data-otservbr-global/monster/bosses/doctor_marrow.lua new file mode 100644 index 00000000000..3de9bc41825 --- /dev/null +++ b/data-otservbr-global/monster/bosses/doctor_marrow.lua @@ -0,0 +1,110 @@ +local mType = Game.createMonsterType("Doctor Marrow") +local monster = {} + +monster.description = "Doctor Marrow" +monster.experience = 0 +monster.outfit = { + lookType = 1611, + lookHead = 57, + lookBody = 0, + lookLegs = 0, + lookFeet = 95, + lookAddons = 0, + lookMount = 0, +} + +monster.health = 1800 +monster.maxHealth = 1800 +monster.race = "blood" +monster.corpse = 18074 +monster.speed = 125 +monster.manaCost = 0 + +monster.changeTarget = { + interval = 4000, + chance = 10, +} + +monster.strategiesTarget = { + nearest = 80, + health = 10, + damage = 10, +} + +monster.flags = { + summonable = false, + attackable = true, + hostile = true, + convinceable = false, + pushable = false, + rewardBoss = true, + illusionable = false, + canPushItems = true, + canPushCreatures = true, + critChance = 10, + staticAttackChance = 90, + targetDistance = 1, + runHealth = 0, + healthHidden = false, + isBlockable = false, + canWalkOnEnergy = true, + canWalkOnFire = true, + canWalkOnPoison = true, +} + +monster.light = { + level = 0, + color = 0, +} + +monster.voices = { + interval = 5000, + chance = 10, + { text = "You can't stop the future!", yell = false }, +} + +monster.attacks = { + { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -2800 }, +} + +monster.defenses = { + defense = 54, + armor = 59, + mitigation = 3.7, +} + +monster.elements = { + { type = COMBAT_PHYSICALDAMAGE, percent = 0 }, + { type = COMBAT_ENERGYDAMAGE, percent = 0 }, + { type = COMBAT_EARTHDAMAGE, percent = 0 }, + { type = COMBAT_FIREDAMAGE, percent = 0 }, + { type = COMBAT_LIFEDRAIN, percent = 0 }, + { type = COMBAT_MANADRAIN, percent = 0 }, + { type = COMBAT_DROWNDAMAGE, percent = 0 }, + { type = COMBAT_ICEDAMAGE, percent = 0 }, + { type = COMBAT_HOLYDAMAGE, percent = 0 }, + { type = COMBAT_DEATHDAMAGE, percent = 0 }, +} + +monster.immunities = { + { type = "paralyze", condition = true }, + { type = "outfit", condition = false }, + { type = "invisible", condition = true }, + { type = "bleed", condition = false }, +} + +mType.onThink = function(monster, interval) end + +mType.onAppear = function(monster, creature) + if monster:getType():isRewardBoss() then + monster:setReward(true) + end +end + +mType.onDisappear = function(monster, creature) end + +mType.onMove = function(monster, creature, fromPosition, toPosition) end + +mType.onSay = function(monster, creature, type, message) end + +mType:register(monster) diff --git a/data-otservbr-global/scripts/lib/register_monster_type.lua b/data-otservbr-global/scripts/lib/register_monster_type.lua index 68983418159..21a10d94aac 100644 --- a/data-otservbr-global/scripts/lib/register_monster_type.lua +++ b/data-otservbr-global/scripts/lib/register_monster_type.lua @@ -199,6 +199,9 @@ registerMonsterType.flags = function(mtype, mask) if mask.flags.canPushCreatures ~= nil then mtype:canPushCreatures(mask.flags.canPushCreatures) end + if mask.flags.critChance ~= nil then + mtype:critChance(mask.flags.critChance) + end if mask.flags.targetDistance then mtype:targetDistance(math.max(1, mask.flags.targetDistance)) end diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 8b650253567..75cec14c30f 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -1139,41 +1139,7 @@ void Combat::doCombatHealth(std::shared_ptr caster, std::shared_ptrgetPlayer()) { - // Critical damage - uint16_t chance = caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_CHANCE) + (uint16_t)damage.criticalChance; - // Charm low blow rune) - if (target && target->getMonster() && damage.primary.type != COMBAT_HEALING) { - uint16_t playerCharmRaceid = caster->getPlayer()->parseRacebyCharm(CHARM_LOW, false, 0); - if (playerCharmRaceid != 0) { - const auto mType = g_monsters().getMonsterType(target->getName()); - if (mType && playerCharmRaceid == mType->info.raceid) { - const auto charm = g_iobestiary().getBestiaryCharm(CHARM_LOW); - if (charm) { - chance += charm->percent; - g_game().sendDoubleSoundEffect(target->getPosition(), charm->soundCastEffect, charm->soundImpactEffect, caster); - } - } - } - } - if (chance != 0 && uniform_random(1, 100) <= chance) { - damage.critical = true; - damage.primary.value += (damage.primary.value * (caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE) + damage.criticalDamage)) / 100; - damage.secondary.value += (damage.secondary.value * (caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE) + damage.criticalDamage)) / 100; - } - - // Fatal hit (onslaught) - if (auto playerWeapon = caster->getPlayer()->getInventoryItem(CONST_SLOT_LEFT); - playerWeapon != nullptr && playerWeapon->getTier()) { - double_t fatalChance = playerWeapon->getFatalChance(); - double_t randomChance = uniform_random(0, 10000) / 100; - if (damage.primary.type != COMBAT_HEALING && fatalChance > 0 && randomChance < fatalChance) { - damage.fatal = true; - damage.primary.value += static_cast(std::round(damage.primary.value * 0.6)); - damage.secondary.value += static_cast(std::round(damage.secondary.value * 0.6)); - } - } - } + applyExtensions(caster, target, damage, params); if (canCombat) { if (target && caster && params.distanceEffect != CONST_ANI_NONE) { @@ -1194,27 +1160,7 @@ void Combat::doCombatHealth(std::shared_ptr caster, std::shared_ptr caster, const Position &position, const std::unique_ptr &area, CombatDamage &damage, const CombatParams ¶ms) { - if (caster && caster->getPlayer()) { - // Critical damage - uint16_t chance = caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_CHANCE) + (uint16_t)damage.criticalChance; - if (damage.primary.type != COMBAT_HEALING && chance != 0 && uniform_random(1, 100) <= chance) { - damage.critical = true; - damage.primary.value += (damage.primary.value * (caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE) + damage.criticalDamage)) / 100; - damage.secondary.value += (damage.secondary.value * (caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE) + damage.criticalDamage)) / 100; - } - - // Fatal hit (onslaught) - if (auto playerWeapon = caster->getPlayer()->getInventoryItem(CONST_SLOT_LEFT); - playerWeapon != nullptr && playerWeapon->getTier() > 0) { - double_t fatalChance = playerWeapon->getFatalChance(); - double_t randomChance = uniform_random(0, 10000) / 100; - if (damage.primary.type != COMBAT_HEALING && fatalChance > 0 && randomChance < fatalChance) { - damage.fatal = true; - damage.primary.value += static_cast(std::round(damage.primary.value * 0.6)); - damage.secondary.value += static_cast(std::round(damage.secondary.value * 0.6)); - } - } - } + applyExtensions(caster, nullptr, damage, params); const auto origin = caster ? caster->getPosition() : Position(); CombatFunc(caster, origin, position, area, params, CombatHealthFunc, &damage); } @@ -1231,15 +1177,7 @@ void Combat::doCombatMana(std::shared_ptr caster, std::shared_ptrgetPosition(), params.impactEffect); } - if (caster && caster->getPlayer()) { - // Critical damage - uint16_t chance = caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_CHANCE) + (uint16_t)damage.criticalChance; - if (chance != 0 && uniform_random(1, 100) <= chance) { - damage.critical = true; - damage.primary.value += (damage.primary.value * (caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE) + damage.criticalDamage)) / 100; - damage.secondary.value += (damage.secondary.value * (caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE) + damage.criticalDamage)) / 100; - } - } + applyExtensions(caster, target, damage, params); if (canCombat) { if (caster && target && params.distanceEffect != CONST_ANI_NONE) { @@ -1260,15 +1198,7 @@ void Combat::doCombatMana(std::shared_ptr caster, std::shared_ptr caster, const Position &position, const std::unique_ptr &area, CombatDamage &damage, const CombatParams ¶ms) { - if (caster && caster->getPlayer()) { - // Critical damage - uint16_t chance = caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_CHANCE) + (uint16_t)damage.criticalChance; - if (chance != 0 && uniform_random(1, 100) <= chance) { - damage.critical = true; - damage.primary.value += (damage.primary.value * (caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE) + damage.criticalDamage)) / 100; - damage.secondary.value += (damage.secondary.value * (caster->getPlayer()->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE) + damage.criticalDamage)) / 100; - } - } + applyExtensions(caster, nullptr, damage, params); const auto origin = caster ? caster->getPosition() : Position(); CombatFunc(caster, origin, position, area, params, CombatManaFunc, &damage); } @@ -2080,3 +2010,64 @@ void MagicField::onStepInField(const std::shared_ptr &creature) { creature->addCondition(conditionCopy); } } + +void Combat::applyExtensions(std::shared_ptr caster, std::shared_ptr target, CombatDamage &damage, const CombatParams ¶ms) { + if (damage.extension || !caster || damage.primary.type == COMBAT_HEALING) { + return; + } + + g_logger().debug("[Combat::applyExtensions] - Applying extensions for {} on {}. Initial damage: {}", caster->getName(), target ? target->getName() : "null", damage.primary.value); + + // Critical hit + uint16_t chance = 0; + int32_t multiplier = 50; + auto player = caster->getPlayer(); + auto monster = caster->getMonster(); + if (player) { + chance = player->getSkillLevel(SKILL_CRITICAL_HIT_CHANCE); + multiplier = player->getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE); + + if (target) { + uint16_t playerCharmRaceid = player->parseRacebyCharm(CHARM_LOW, false, 0); + if (playerCharmRaceid != 0) { + const auto mType = g_monsters().getMonsterType(target->getName()); + if (mType && playerCharmRaceid == mType->info.raceid) { + const auto charm = g_iobestiary().getBestiaryCharm(CHARM_LOW); + if (charm) { + chance += charm->percent; + g_game().sendDoubleSoundEffect(target->getPosition(), charm->soundCastEffect, charm->soundImpactEffect, caster); + } + } + } + } + } else if (monster) { + chance = monster->critChance(); + } + + multiplier += damage.criticalDamage; + multiplier = 1 + multiplier / 100; + chance += (uint16_t)damage.criticalChance; + + if (chance != 0 && uniform_random(1, 100) <= chance) { + damage.critical = true; + damage.primary.value *= multiplier; + damage.secondary.value *= multiplier; + } + + if (player) { + // Fatal hit (onslaught) + if (auto playerWeapon = player->getInventoryItem(CONST_SLOT_LEFT); + playerWeapon != nullptr && playerWeapon->getTier() > 0) { + double_t fatalChance = playerWeapon->getFatalChance(); + double_t randomChance = uniform_random(0, 10000) / 100; + if (fatalChance > 0 && randomChance < fatalChance) { + damage.fatal = true; + damage.primary.value += static_cast(std::round(damage.primary.value * 0.6)); + damage.secondary.value += static_cast(std::round(damage.secondary.value * 0.6)); + } + } + } else if (monster) { + damage.primary.value *= monster->getAttackMultiplier(); + damage.secondary.value *= monster->getAttackMultiplier(); + } +} diff --git a/src/creatures/combat/combat.hpp b/src/creatures/combat/combat.hpp index 16062cb685d..2bb2d0a3589 100644 --- a/src/creatures/combat/combat.hpp +++ b/src/creatures/combat/combat.hpp @@ -260,6 +260,8 @@ class Combat { Combat(const Combat &) = delete; Combat &operator=(const Combat &) = delete; + static void applyExtensions(std::shared_ptr caster, std::shared_ptr target, CombatDamage &damage, const CombatParams ¶ms); + static void doCombatHealth(std::shared_ptr caster, std::shared_ptr target, CombatDamage &damage, const CombatParams ¶ms); static void doCombatHealth(std::shared_ptr caster, const Position &position, const std::unique_ptr &area, CombatDamage &damage, const CombatParams ¶ms); diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index eef9aaa3830..0ce9432ea68 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -811,12 +811,6 @@ void Monster::doAttacking(uint32_t interval) { bool resetTicks = interval != 0; attackTicks += interval; - float forgeAttackBonus = 0; - if (monsterForgeClassification > ForgeClassifications_t::FORGE_NORMAL_MONSTER) { - uint16_t damageBase = 3; - forgeAttackBonus = static_cast(damageBase + 100) / 100.f; - } - const Position &myPos = getPosition(); const Position &targetPos = attackedCreature->getPosition(); @@ -834,20 +828,8 @@ void Monster::doAttacking(uint32_t interval) { updateLook = false; } - float multiplier; - if (maxCombatValue > 0) { // Defense - multiplier = getDefenseMultiplier(); - } else { // Attack - multiplier = getAttackMultiplier(); - } - - minCombatValue = spellBlock.minCombatValue * multiplier; - maxCombatValue = spellBlock.maxCombatValue * multiplier; - - if (maxCombatValue <= 0 && forgeAttackBonus > 0) { - minCombatValue *= static_cast(forgeAttackBonus); - maxCombatValue *= static_cast(forgeAttackBonus); - } + minCombatValue = spellBlock.minCombatValue; + maxCombatValue = spellBlock.maxCombatValue; if (spellBlock.spell == nullptr) { continue; @@ -1912,15 +1894,8 @@ bool Monster::getCombatValues(int32_t &min, int32_t &max) { return false; } - float multiplier; - if (maxCombatValue > 0) { // Defense - multiplier = getDefenseMultiplier(); - } else { // Attack - multiplier = getAttackMultiplier(); - } - - min = minCombatValue * multiplier; - max = maxCombatValue * multiplier; + min = minCombatValue; + max = maxCombatValue; return true; } diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index 5f589af0e28..858b3ac8dd8 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -77,13 +77,13 @@ class Monster final : public Creature { return mType->info.race; } float getMitigation() const override { - return mType->info.mitigation; + return mType->info.mitigation * getDefenseMultiplier(); } int32_t getArmor() const override { - return mType->info.armor; + return mType->info.armor * getDefenseMultiplier(); } int32_t getDefense() const override { - return mType->info.defense; + return mType->info.defense * getDefenseMultiplier(); } Faction_t getFaction() const override { @@ -126,6 +126,9 @@ class Monster final : public Creature { bool canSeeInvisibility() const override { return isImmune(CONDITION_INVISIBLE); } + uint16_t critChance() const { + return mType->info.critChance; + } uint32_t getManaCost() const { return mType->info.manaCost; } @@ -325,6 +328,16 @@ class Monster final : public Creature { bool isImmune(ConditionType_t conditionType) const override; bool isImmune(CombatType_t combatType) const override; + float getAttackMultiplier() const { + float multiplier = mType->getAttackMultiplier(); + return multiplier * std::pow(1.03f, getForgeStack()); + } + + float getDefenseMultiplier() const { + float multiplier = mType->getDefenseMultiplier(); + return multiplier * std::pow(1.01f, getForgeStack()); + } + private: CreatureWeakHashMap friendList; CreatureIDList targetIDList; @@ -436,14 +449,4 @@ class Monster final : public Creature { void doRandomStep(Direction &nextDirection, bool &result); void onConditionStatusChange(const ConditionType_t &type); - - float getAttackMultiplier() const { - float multiplier = mType->getAttackMultiplier(); - return multiplier * std::pow(1.03f, getForgeStack()); - } - - float getDefenseMultiplier() const { - float multiplier = mType->getAttackMultiplier(); - return multiplier * std::pow(1.01f, getForgeStack()); - } }; diff --git a/src/creatures/monsters/monsters.hpp b/src/creatures/monsters/monsters.hpp index a13792fd297..b214b478e3b 100644 --- a/src/creatures/monsters/monsters.hpp +++ b/src/creatures/monsters/monsters.hpp @@ -128,6 +128,7 @@ class MonsterType { int32_t changeTargetChance = 0; int32_t defense = 0; int32_t armor = 0; + uint16_t critChance = 0; int32_t strategiesTargetNearest = 0; int32_t strategiesTargetHealth = 0; int32_t strategiesTargetDamage = 0; diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp index 1beea8de66b..ba77c49a90a 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp @@ -280,6 +280,20 @@ int MonsterTypeFunctions::luaMonsterTypeCanPushCreatures(lua_State* L) { return 1; } +int MonsterTypeFunctions::luaMonsterTypeCritChance(lua_State* L) { + // get: monsterType:critChance() set: monsterType:critChance(int) + const auto monsterType = getUserdataShared(L, 1); + if (monsterType) { + if (lua_gettop(L) == 2) { + monsterType->info.critChance = getNumber(L, 2); + } + lua_pushnumber(L, monsterType->info.critChance); + } else { + lua_pushnil(L); + } + return 1; +} + int32_t MonsterTypeFunctions::luaMonsterTypeName(lua_State* L) { // get: monsterType:name() set: monsterType:name(name) const auto monsterType = getUserdataShared(L, 1); diff --git a/src/lua/functions/creatures/monster/monster_type_functions.hpp b/src/lua/functions/creatures/monster/monster_type_functions.hpp index d9c513d2bce..9d892219250 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.hpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.hpp @@ -35,6 +35,8 @@ class MonsterTypeFunctions final : LuaScriptInterface { registerMethod(L, "MonsterType", "canPushItems", MonsterTypeFunctions::luaMonsterTypeCanPushItems); registerMethod(L, "MonsterType", "canPushCreatures", MonsterTypeFunctions::luaMonsterTypeCanPushCreatures); + registerMethod(L, "MonsterType", "critChance", MonsterTypeFunctions::luaMonsterTypeCritChance); + registerMethod(L, "MonsterType", "name", MonsterTypeFunctions::luaMonsterTypeName); registerMethod(L, "MonsterType", "nameDescription", MonsterTypeFunctions::luaMonsterTypeNameDescription); @@ -260,10 +262,5 @@ class MonsterTypeFunctions final : LuaScriptInterface { static int luaMonsterTypeAddSound(lua_State* L); static int luaMonsterTypeGetSounds(lua_State* L); static int luaMonsterTypedeathSound(lua_State* L); - - // Hazard system - static int luaMonsterTypeHazardSystemCrit(lua_State* L); - static int luaMonsterTypeHazardSystemDodge(lua_State* L); - static int luaMonsterTypeHazardSystemSpawnPod(lua_State* L); - static int luaMonsterTypeHazardSystemDamageBoost(lua_State* L); + static int luaMonsterTypeCritChance(lua_State* L); }; From c8f7233c1351edf2585c44507c4411edbb2b313f Mon Sep 17 00:00:00 2001 From: un000000 <101636937+un000000@users.noreply.github.com> Date: Tue, 10 Oct 2023 02:17:17 +0200 Subject: [PATCH 10/10] fix: fluid type must be within enum (#1678) Whenever auction is ended, resulting fluid type should be between 0 and the size of fluid type enum. --- src/items/item.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/items/item.cpp b/src/items/item.cpp index b6902575bb2..4677041e777 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -185,7 +185,7 @@ Item::Item(const uint16_t itemId, uint16_t itemCount /*= 0*/) : const ItemType &it = items[id]; auto itemCharges = it.charges; if (it.isFluidContainer() || it.isSplash()) { - setAttribute(ItemAttribute_t::FLUIDTYPE, itemCount); + setAttribute(ItemAttribute_t::FLUIDTYPE, itemCount * (itemCount < sizeof(Fluids_t))); } else if (it.stackable) { if (itemCount != 0) { setItemCount(static_cast(itemCount));