From cdd68ad8d2a015dbc13a57e4f099243840d33074 Mon Sep 17 00:00:00 2001 From: Beats Date: Wed, 30 Oct 2024 19:13:33 -0300 Subject: [PATCH 1/3] refactor: remove circular dependencies from header files (#3025) Co-authored-by: Eduardo Dantas --- .../normalize_cpp_own_includes.py.py | 72 + src/account/account.cpp | 87 +- src/account/account.hpp | 6 +- src/account/account_info.hpp | 2 + src/account/account_repository.hpp | 8 +- src/account/account_repository_db.cpp | 62 +- src/account/account_repository_db.hpp | 14 +- src/canary_server.cpp | 11 +- src/config/configmanager.cpp | 2 + src/creatures/appearance/mounts/mounts.cpp | 2 + src/creatures/appearance/outfit/outfit.cpp | 22 +- src/creatures/appearance/outfit/outfit.hpp | 21 +- src/creatures/combat/combat.cpp | 191 +- src/creatures/combat/combat.hpp | 187 +- src/creatures/combat/condition.cpp | 128 + src/creatures/combat/condition.hpp | 124 +- src/creatures/combat/spells.cpp | 349 +- src/creatures/combat/spells.hpp | 354 +- src/creatures/creature.cpp | 54 +- src/creatures/creature.hpp | 68 +- src/creatures/creatures_definitions.hpp | 2 - src/creatures/interactions/chat.cpp | 57 +- src/creatures/interactions/chat.hpp | 59 +- src/creatures/monsters/monster.cpp | 330 +- src/creatures/monsters/monster.hpp | 298 +- src/creatures/monsters/monsters.cpp | 37 +- src/creatures/monsters/monsters.hpp | 40 +- .../monsters/spawns/spawn_monster.cpp | 52 +- .../monsters/spawns/spawn_monster.hpp | 41 +- src/creatures/npcs/npc.cpp | 112 +- src/creatures/npcs/npc.hpp | 128 +- src/creatures/npcs/npcs.cpp | 14 +- src/creatures/npcs/npcs.hpp | 10 +- src/creatures/npcs/spawns/spawn_npc.cpp | 35 +- src/creatures/npcs/spawns/spawn_npc.hpp | 35 +- .../achievement/player_achievement.cpp | 2 +- .../players/cyclopedia/player_badge.cpp | 9 +- .../players/cyclopedia/player_cyclopedia.cpp | 8 +- .../players/cyclopedia/player_title.cpp | 7 +- .../players/cyclopedia/player_title.hpp | 3 +- src/creatures/players/grouping/familiars.cpp | 10 +- src/creatures/players/grouping/familiars.hpp | 9 +- src/creatures/players/grouping/groups.cpp | 7 +- src/creatures/players/grouping/groups.hpp | 6 +- src/creatures/players/grouping/guild.cpp | 1 + src/creatures/players/grouping/party.cpp | 69 +- src/creatures/players/grouping/party.hpp | 72 +- .../players/imbuements/imbuements.cpp | 51 +- .../players/imbuements/imbuements.hpp | 56 +- src/creatures/players/management/ban.cpp | 1 + src/creatures/players/management/waitlist.cpp | 5 + src/creatures/players/player.cpp | 6852 +++++++++++------ src/creatures/players/player.hpp | 2196 +----- src/creatures/players/storages/storages.cpp | 2 +- src/creatures/players/vip/player_vip.cpp | 6 +- src/creatures/players/vocations/vocation.cpp | 81 +- src/creatures/players/vocations/vocation.hpp | 64 +- src/creatures/players/wheel/player_wheel.cpp | 18 +- src/creatures/players/wheel/player_wheel.hpp | 6 + .../players/wheel/wheel_definitions.hpp | 2 - src/creatures/players/wheel/wheel_gems.cpp | 1 + src/database/database.cpp | 3 +- src/database/databasemanager.cpp | 3 +- src/database/databasetasks.cpp | 1 + src/game/bank/bank.cpp | 7 +- src/game/functions/game_reload.cpp | 18 +- src/game/functions/game_reload.hpp | 10 +- src/game/game.cpp | 429 +- src/game/game.hpp | 88 +- src/game/game_definitions.hpp | 2 +- src/game/movement/position.cpp | 1 + src/game/movement/teleport.cpp | 4 +- src/game/movement/teleport.hpp | 5 +- src/game/scheduling/dispatcher.cpp | 1 + src/game/scheduling/events_scheduler.cpp | 3 +- src/game/scheduling/save_manager.cpp | 4 + src/game/scheduling/task.cpp | 2 +- src/game/zones/zone.cpp | 3 +- src/io/functions/iologindata_load_player.cpp | 23 +- src/io/functions/iologindata_save_player.cpp | 8 + src/io/functions/iologindata_save_player.hpp | 2 + src/io/io_bosstiary.cpp | 6 +- src/io/io_bosstiary.hpp | 9 +- src/io/iobestiary.cpp | 10 +- src/io/iobestiary.hpp | 16 +- src/io/ioguild.cpp | 3 +- src/io/iologindata.cpp | 2 + src/io/iomap.cpp | 1 + src/io/iomapserialize.cpp | 2 + src/io/iomarket.cpp | 5 +- src/io/ioprey.cpp | 7 +- src/items/bed.cpp | 6 +- src/items/containers/container.cpp | 5 +- src/items/containers/depot/depotchest.cpp | 1 + src/items/containers/inbox/inbox.cpp | 1 + src/items/containers/mailbox/mailbox.cpp | 4 +- src/items/cylinder.hpp | 2 +- src/items/decay/decay.cpp | 3 +- src/items/functions/item/attribute.cpp | 2 + src/items/functions/item/attribute.hpp | 1 - src/items/functions/item/item_parse.cpp | 3 + src/items/item.cpp | 136 +- src/items/item.hpp | 121 +- src/items/items.cpp | 32 +- src/items/items.hpp | 35 +- src/items/items_definitions.hpp | 2 +- src/items/thing.cpp | 1 + src/items/tile.cpp | 19 +- src/items/tile.hpp | 10 +- src/items/trashholder.cpp | 1 + src/items/weapons/weapons.cpp | 4 +- src/kv/value_wrapper.cpp | 1 + src/lib/di/soft_singleton.cpp | 1 + src/lib/di/soft_singleton.hpp | 3 +- src/lib/metrics/metrics.cpp | 3 +- src/lua/callbacks/creaturecallback.cpp | 33 + src/lua/callbacks/creaturecallback.hpp | 35 +- src/lua/callbacks/event_callback.cpp | 5 +- src/lua/creature/actions.cpp | 11 +- src/lua/creature/creatureevent.cpp | 7 +- src/lua/creature/creatureevent.hpp | 8 +- src/lua/creature/events.cpp | 15 +- src/lua/creature/events.hpp | 27 +- src/lua/creature/movement.cpp | 8 +- src/lua/creature/raids.cpp | 6 +- src/lua/creature/raids.hpp | 7 +- src/lua/creature/talkaction.cpp | 5 +- .../functions/core/game/bank_functions.cpp | 2 + .../functions/core/game/game_functions.cpp | 24 +- .../functions/core/game/global_functions.cpp | 53 +- .../functions/core/game/global_functions.hpp | 40 +- src/lua/functions/core/game/lua_enums.cpp | 11 +- src/lua/functions/core/game/lua_enums.hpp | 2 - .../core/game/modal_window_functions.cpp | 3 +- .../functions/core/game/zone_functions.cpp | 1 + src/lua/functions/core/libs/db_functions.cpp | 3 +- src/lua/functions/core/libs/kv_functions.cpp | 3 +- .../functions/core/libs/metrics_functions.cpp | 1 + .../network/network_message_functions.cpp | 2 + .../core/network/webhook_functions.cpp | 1 + .../creatures/combat/combat_functions.cpp | 6 +- .../creatures/combat/condition_functions.cpp | 4 +- .../creatures/combat/spell_functions.cpp | 5 +- .../creatures/combat/variant_functions.cpp | 3 +- .../creatures/creature_functions.cpp | 9 +- .../creatures/monster/charm_functions.cpp | 3 +- .../creatures/monster/loot_functions.cpp | 5 +- .../creatures/monster/monster_functions.cpp | 9 +- .../monster/monster_spell_functions.cpp | 1 + .../monster/monster_type_functions.cpp | 11 +- .../functions/creatures/npc/npc_functions.cpp | 6 +- .../creatures/npc/npc_type_functions.cpp | 6 +- .../creatures/npc/shop_functions.cpp | 6 +- .../creatures/player/group_functions.cpp | 3 +- .../creatures/player/guild_functions.cpp | 3 +- .../creatures/player/mount_functions.cpp | 9 +- .../creatures/player/party_functions.cpp | 3 +- .../creatures/player/player_functions.cpp | 28 +- .../creatures/player/player_functions.hpp | 3 + .../creatures/player/vocation_functions.cpp | 3 +- src/lua/functions/events/action_functions.cpp | 3 +- .../events/creature_event_functions.cpp | 3 +- .../events/events_scheduler_functions.cpp | 3 +- .../events/global_event_functions.cpp | 1 + .../functions/events/move_event_functions.cpp | 4 +- .../events/talk_action_functions.cpp | 3 +- .../functions/items/container_functions.cpp | 4 +- .../functions/items/imbuement_functions.cpp | 3 +- .../items/item_classification_functions.cpp | 4 +- src/lua/functions/items/item_functions.cpp | 9 +- .../functions/items/item_type_functions.cpp | 3 +- src/lua/functions/lua_functions_loader.cpp | 3 +- src/lua/functions/map/house_functions.cpp | 4 +- src/lua/functions/map/position_functions.cpp | 7 +- src/lua/functions/map/teleport_functions.cpp | 3 +- src/lua/functions/map/tile_functions.cpp | 4 +- src/lua/functions/map/town_functions.cpp | 3 +- src/lua/global/baseevents.cpp | 3 + src/lua/global/baseevents.hpp | 3 +- src/lua/global/globalevent.cpp | 1 + src/lua/lua_definitions.hpp | 2 +- src/lua/modules/modules.cpp | 1 + src/lua/modules/modules.hpp | 1 + src/lua/scripts/lua_environment.cpp | 3 +- src/lua/scripts/luascript.cpp | 1 + src/lua/scripts/script_environment.cpp | 4 +- src/lua/scripts/scripts.cpp | 12 +- src/map/house/house.cpp | 10 +- src/map/house/housetile.cpp | 10 +- src/map/map.cpp | 11 +- src/map/mapcache.cpp | 13 +- src/map/spectators.cpp | 21 +- src/map/spectators.hpp | 40 +- src/map/utils/astarnodes.cpp | 6 +- src/map/utils/mapsector.cpp | 3 +- src/pch.hpp | 3 + src/security/argon.cpp | 3 +- src/security/rsa.cpp | 3 +- src/server/network/message/networkmessage.cpp | 1 + src/server/network/message/outputmessage.cpp | 2 +- src/server/network/protocol/protocolgame.cpp | 68 +- src/server/network/protocol/protocolgame.hpp | 48 +- src/server/network/protocol/protocollogin.cpp | 2 + .../network/protocol/protocolstatus.cpp | 4 +- src/server/network/webhook/webhook.cpp | 1 + src/server/server.cpp | 3 +- src/server/signals.cpp | 10 +- src/utils/tools.cpp | 15 +- src/utils/tools.hpp | 2 + .../account/in_memory_account_repository.hpp | 14 +- tests/integration/main.cpp | 60 +- tests/unit/account/account_test.cpp | 53 +- 212 files changed, 8271 insertions(+), 6302 deletions(-) create mode 100644 docs/python-scripts/normalize_cpp_own_includes.py.py diff --git a/docs/python-scripts/normalize_cpp_own_includes.py.py b/docs/python-scripts/normalize_cpp_own_includes.py.py new file mode 100644 index 00000000000..d5e0ca0e858 --- /dev/null +++ b/docs/python-scripts/normalize_cpp_own_includes.py.py @@ -0,0 +1,72 @@ +import os + +# Path to the directory containing C++ files (.cpp) +directory_path = "path/to/src" + +def normalize_include_line(line): + # Normalize the include line by removing extra spaces and using '/' as separator + return ' '.join(line.strip().split()).replace('\\', '/') + +def correct_include_path(line, correct_include): + # Check if the include line matches the correct include, regardless of the path + if line.strip().startswith('#include') and ('"' + correct_include.split('/')[-1] + '"') in line: + return f'#include "{correct_include}"\n' + return line + +# Function to modify files and correct their own includes +def modify_includes(directory): + for root, dirs, files in os.walk(directory): + for filename in files: + if filename.endswith('.cpp'): + file_path = os.path.join(root, filename) + correct_include = f"{os.path.relpath(root, directory).replace('\\', '/')}/{filename.replace('.cpp', '.hpp')}" + + # Remove './' from the beginning of the path, if present + if correct_include.startswith('./'): + correct_include = correct_include[2:] + + include_statement = f'#include "{correct_include}"\n' + + with open(file_path, 'r', encoding='utf8') as file: + lines = file.readlines() + + corrected_lines = [] + include_found = False + include_renamed = False + include_at_correct_position = False + + # Normalize lines and correct includes as necessary + for i, line in enumerate(lines): + normalized_line = normalize_include_line(line) + if correct_include in normalized_line: + # If the correct include was found and is in the correct position + include_found = True + if i == next((idx for idx, l in enumerate(lines) if l.strip().startswith('#include')), i): + include_at_correct_position = True + if include_at_correct_position: + corrected_lines.append(line) # Keep the original include if it is correct and in the right position + elif filename.replace('.cpp', '.hpp') in normalized_line: + # Replace any old version of the include with the corrected version + corrected_lines.append(correct_include_path(line, correct_include)) + include_renamed = True + else: + corrected_lines.append(line) + + # If the include was found but not in the correct position, or was renamed, move it to the first include position + if (include_found and not include_at_correct_position) or include_renamed: + # Remove any occurrence of the correct include that is out of place + corrected_lines = [line for line in corrected_lines if line.strip() != include_statement.strip()] + # Find the first include position and insert the correct include + first_include_index = next((i for i, line in enumerate(corrected_lines) if line.strip().startswith('#include')), len(corrected_lines)) + corrected_lines.insert(first_include_index, include_statement) + # Add a blank line immediately after the first include, if necessary + if first_include_index + 1 < len(corrected_lines) and not corrected_lines[first_include_index + 1].isspace(): + corrected_lines.insert(first_include_index + 1, '\n') + + # Write the changes back to the file only if modifications were made + if corrected_lines != lines: + with open(file_path, 'w', encoding='utf8') as file: + file.writelines(corrected_lines) + +# Call the function to modify the files +modify_includes(directory_path) diff --git a/src/account/account.cpp b/src/account/account.cpp index 2cd411ef3e7..22d69d8833a 100644 --- a/src/account/account.cpp +++ b/src/account/account.cpp @@ -10,33 +10,32 @@ #include "account/account.hpp" #include "account/account_repository_db.hpp" -#include "config/configmanager.hpp" -#include "utils/definitions.hpp" #include "security/argon.hpp" #include "utils/tools.hpp" - -#include "enums/account_type.hpp" #include "enums/account_coins.hpp" #include "enums/account_errors.hpp" +#include "enums/account_type.hpp" Account::Account(const uint32_t &id) { m_descriptor.clear(); - m_account.id = id; - m_account.premiumRemainingDays = 0; - m_account.premiumLastDay = 0; - m_account.accountType = ACCOUNT_TYPE_NORMAL; + m_account = std::make_unique(); + m_account->id = id; + m_account->premiumRemainingDays = 0; + m_account->premiumLastDay = 0; + m_account->accountType = ACCOUNT_TYPE_NORMAL; } Account::Account(std::string descriptor) : m_descriptor(std::move(descriptor)) { - m_account.id = 0; - m_account.premiumRemainingDays = 0; - m_account.premiumLastDay = 0; - m_account.accountType = ACCOUNT_TYPE_NORMAL; + m_account = std::make_unique(); + m_account->id = 0; + m_account->premiumRemainingDays = 0; + m_account->premiumLastDay = 0; + m_account->accountType = ACCOUNT_TYPE_NORMAL; } uint8_t Account::load() { - if (m_account.id != 0 && g_accountRepository().loadByID(m_account.id, m_account)) { + if (m_account->id != 0 && g_accountRepository().loadByID(m_account->id, m_account)) { m_accLoaded = true; return enumToValue(AccountErrors_t::Ok); } @@ -81,7 +80,7 @@ std::tuple Account::getCoins(const uint8_t &type) const { } uint32_t coins = 0; - if (!g_accountRepository().getCoins(m_account.id, type, coins)) { + if (!g_accountRepository().getCoins(m_account->id, type, coins)) { return { 0, enumToValue(AccountErrors_t::Storage) }; } @@ -103,7 +102,7 @@ uint8_t Account::addCoins(const uint8_t &type, const uint32_t &amount, const std return result; } - if (!g_accountRepository().setCoins(m_account.id, type, coins + amount)) { + if (!g_accountRepository().setCoins(m_account->id, type, coins + amount)) { return enumToValue(AccountErrors_t::Storage); } @@ -132,7 +131,7 @@ uint8_t Account::removeCoins(const uint8_t &type, const uint32_t &amount, const return enumToValue(AccountErrors_t::RemoveCoins); } - if (!g_accountRepository().setCoins(m_account.id, type, coins - amount)) { + if (!g_accountRepository().setCoins(m_account->id, type, coins - amount)) { return enumToValue(AccountErrors_t::Storage); } @@ -150,17 +149,17 @@ void Account::registerCoinTransaction(const uint8_t &transactionType, const uint return; } - if (!g_accountRepository().registerCoinsTransaction(m_account.id, transactionType, amount, type, detail)) { + if (!g_accountRepository().registerCoinsTransaction(m_account->id, transactionType, amount, type, detail)) { g_logger().error( "Failed to register transaction: 'account:[{}], transaction " "type:[{}], coins:[{}], coin type:[{}], description:[{}]", - m_account.id, transactionType, amount, type, detail + m_account->id, transactionType, amount, type, detail ); } } [[nodiscard]] uint32_t Account::getID() const { - return m_account.id; + return m_account->id; }; std::string Account::getDescriptor() const { @@ -173,61 +172,61 @@ std::string Account::getPassword() { } std::string password; - if (!g_accountRepository().getPassword(m_account.id, password)) { + if (!g_accountRepository().getPassword(m_account->id, password)) { password.clear(); - g_logger().error("Failed to get password for account[{}]!", m_account.id); + g_logger().error("Failed to get password for account[{}]!", m_account->id); } return password; } void Account::addPremiumDays(const int32_t &days) { - auto timeLeft = std::max(0, static_cast((m_account.premiumLastDay - getTimeNow()) % 86400)); - setPremiumDays(m_account.premiumRemainingDays + days); - m_account.premiumDaysPurchased += days; + auto timeLeft = std::max(0, static_cast((m_account->premiumLastDay - getTimeNow()) % 86400)); + setPremiumDays(m_account->premiumRemainingDays + days); + m_account->premiumDaysPurchased += days; if (timeLeft > 0) { - m_account.premiumLastDay += timeLeft; + m_account->premiumLastDay += timeLeft; } } void Account::setPremiumDays(const int32_t &days) { - m_account.premiumRemainingDays = days; - m_account.premiumLastDay = getTimeNow() + (days * 86400); + m_account->premiumRemainingDays = days; + m_account->premiumLastDay = getTimeNow() + (days * 86400); if (days <= 0) { - m_account.premiumLastDay = 0; - m_account.premiumRemainingDays = 0; + m_account->premiumLastDay = 0; + m_account->premiumRemainingDays = 0; } } [[nodiscard]] uint32_t Account::getPremiumRemainingDays() const { - return m_account.premiumLastDay > getTimeNow() ? static_cast((m_account.premiumLastDay - getTimeNow()) / 86400) : 0; + return m_account->premiumLastDay > getTimeNow() ? static_cast((m_account->premiumLastDay - getTimeNow()) / 86400) : 0; } [[nodiscard]] uint32_t Account::getPremiumDaysPurchased() const { - return m_account.premiumDaysPurchased; + return m_account->premiumDaysPurchased; } uint8_t Account::setAccountType(const uint8_t &accountType) { - m_account.accountType = accountType; + m_account->accountType = accountType; return enumToValue(AccountErrors_t::Ok); } [[nodiscard]] uint8_t Account::getAccountType() const { - return m_account.accountType; + return m_account->accountType; } void Account::updatePremiumTime() { - time_t lastDay = m_account.premiumLastDay; - uint32_t remainingDays = m_account.premiumRemainingDays; + time_t lastDay = m_account->premiumLastDay; + uint32_t remainingDays = m_account->premiumRemainingDays; time_t currentTime = getTimeNow(); auto daysLeft = static_cast((lastDay - currentTime) / 86400); auto timeLeft = static_cast((lastDay - currentTime) % 86400); - m_account.premiumRemainingDays = daysLeft > 0 ? daysLeft : 0; + m_account->premiumRemainingDays = daysLeft > 0 ? daysLeft : 0; if (daysLeft == 0 && timeLeft == 0) { setPremiumDays(0); @@ -237,7 +236,7 @@ void Account::updatePremiumTime() { setPremiumDays(0); } - if (remainingDays == m_account.premiumRemainingDays) { + if (remainingDays == m_account->premiumRemainingDays) { return; } @@ -249,14 +248,14 @@ void Account::updatePremiumTime() { std::tuple, uint8_t> Account::getAccountPlayers() const { auto valueToReturn = enumToValue(m_accLoaded ? AccountErrors_t::Ok : AccountErrors_t::NotInitialized); - return { m_account.players, valueToReturn }; + return { m_account->players, valueToReturn }; } void Account::setProtocolCompat(bool toggle) { - m_account.oldProtocol = toggle; + m_account->oldProtocol = toggle; } bool Account::getProtocolCompat() const { - return m_account.oldProtocol; + return m_account->oldProtocol; } bool Account::authenticate() { @@ -269,8 +268,8 @@ bool Account::authenticate(const std::string &secret) { } bool Account::authenticateSession() { - if (m_account.sessionExpires < getTimeNow()) { - g_logger().error("Session expired for account[{}] expired at [{}] current time [{}]!", m_account.id, m_account.sessionExpires, getTimeNow()); + if (m_account->sessionExpires < getTimeNow()) { + g_logger().error("Session expired for account[{}] expired at [{}] current time [{}]!", m_account->id, m_account->sessionExpires, getTimeNow()); return false; } return true; @@ -290,9 +289,9 @@ bool Account::authenticatePassword(const std::string &password) { } uint32_t Account::getAccountAgeInDays() const { - return static_cast(std::ceil((getTimeNow() - m_account.creationTime) / 86400)); + return static_cast(std::ceil((getTimeNow() - m_account->creationTime) / 86400)); } [[nodiscard]] time_t Account::getPremiumLastDay() const { - return m_account.premiumLastDay; + return m_account->premiumLastDay; } diff --git a/src/account/account.hpp b/src/account/account.hpp index d968ba8ddfb..16bc72b2de6 100644 --- a/src/account/account.hpp +++ b/src/account/account.hpp @@ -9,13 +9,15 @@ #pragma once -#include "account/account_info.hpp" +struct AccountInfo; class Account { public: explicit Account(const uint32_t &id); explicit Account(std::string descriptor); + ~Account() = default; + /** Coins * @brief Get the amount of coins that the account has from database. * @@ -126,6 +128,6 @@ class Account { private: std::string m_descriptor; - AccountInfo m_account; + std::unique_ptr m_account; bool m_accLoaded = false; }; diff --git a/src/account/account_info.hpp b/src/account/account_info.hpp index 698c3b96c1c..8723bcd8b71 100644 --- a/src/account/account_info.hpp +++ b/src/account/account_info.hpp @@ -15,6 +15,8 @@ #endif struct AccountInfo { + ~AccountInfo() = default; + uint32_t id = 0; uint32_t premiumRemainingDays = 0; time_t premiumLastDay = 0; diff --git a/src/account/account_repository.hpp b/src/account/account_repository.hpp index 6dfe2c65668..e6817151382 100644 --- a/src/account/account_repository.hpp +++ b/src/account/account_repository.hpp @@ -22,10 +22,10 @@ class AccountRepository { static AccountRepository &getInstance(); - virtual bool loadByID(const uint32_t &id, AccountInfo &acc) = 0; - virtual bool loadByEmailOrName(bool oldProtocol, const std::string &emailOrName, AccountInfo &acc) = 0; - virtual bool loadBySession(const std::string &email, AccountInfo &acc) = 0; - virtual bool save(const AccountInfo &accInfo) = 0; + virtual bool loadByID(const uint32_t &id, std::unique_ptr &acc) = 0; + virtual bool loadByEmailOrName(bool oldProtocol, const std::string &emailOrName, std::unique_ptr &acc) = 0; + virtual bool loadBySession(const std::string &email, std::unique_ptr &acc) = 0; + virtual bool save(const std::unique_ptr &accInfo) = 0; virtual bool getCharacterByAccountIdAndName(const uint32_t &id, const std::string &name) = 0; diff --git a/src/account/account_repository_db.cpp b/src/account/account_repository_db.cpp index 10f3d6f3a41..81d44e270b0 100644 --- a/src/account/account_repository_db.cpp +++ b/src/account/account_repository_db.cpp @@ -10,27 +10,25 @@ #include "account/account_repository_db.hpp" #include "database/database.hpp" +#include "enums/account_coins.hpp" #include "utils/definitions.hpp" #include "utils/tools.hpp" -#include "enums/account_type.hpp" -#include "enums/account_coins.hpp" -#include "account/account_info.hpp" AccountRepositoryDB::AccountRepositoryDB() : coinTypeToColumn({ { enumToValue(CoinType::Normal), "coins" }, { enumToValue(CoinType::Tournament), "tournament_coins" }, { enumToValue(CoinType::Transferable), "coins_transferable" } }) { } -bool AccountRepositoryDB::loadByID(const uint32_t &id, AccountInfo &acc) { +bool AccountRepositoryDB::loadByID(const uint32_t &id, std::unique_ptr &acc) { auto query = fmt::format("SELECT `id`, `type`, `premdays`, `lastday`, `creation`, `premdays_purchased`, 0 AS `expires` FROM `accounts` WHERE `id` = {}", id); return load(query, acc); }; -bool AccountRepositoryDB::loadByEmailOrName(bool oldProtocol, const std::string &emailOrName, AccountInfo &acc) { +bool AccountRepositoryDB::loadByEmailOrName(bool oldProtocol, const std::string &emailOrName, std::unique_ptr &acc) { auto identifier = oldProtocol ? "name" : "email"; auto query = fmt::format("SELECT `id`, `type`, `premdays`, `lastday`, `creation`, `premdays_purchased`, 0 AS `expires` FROM `accounts` WHERE `{}` = {}", identifier, g_database().escapeString(emailOrName)); return load(query, acc); }; -bool AccountRepositoryDB::loadBySession(const std::string &sessionKey, AccountInfo &acc) { +bool AccountRepositoryDB::loadBySession(const std::string &sessionKey, std::unique_ptr &acc) { auto query = fmt::format( "SELECT `accounts`.`id`, `type`, `premdays`, `lastday`, `creation`, `premdays_purchased`, `account_sessions`.`expires` " "FROM `accounts` " @@ -41,21 +39,21 @@ bool AccountRepositoryDB::loadBySession(const std::string &sessionKey, AccountIn return load(query, acc); }; -bool AccountRepositoryDB::save(const AccountInfo &accInfo) { +bool AccountRepositoryDB::save(const std::unique_ptr &accInfo) { bool successful = g_database().executeQuery( fmt::format( "UPDATE `accounts` SET `type` = {}, `premdays` = {}, `lastday` = {}, `creation` = {}, `premdays_purchased` = {} WHERE `id` = {}", - accInfo.accountType, - accInfo.premiumRemainingDays, - accInfo.premiumLastDay, - accInfo.creationTime, - accInfo.premiumDaysPurchased, - accInfo.id + accInfo->accountType, + accInfo->premiumRemainingDays, + accInfo->premiumLastDay, + accInfo->creationTime, + accInfo->premiumDaysPurchased, + accInfo->id ) ); if (!successful) { - g_logger().error("Failed to save account:[{}]", accInfo.id); + g_logger().error("Failed to save account:[{}]", accInfo->id); } return successful; @@ -155,13 +153,13 @@ bool AccountRepositoryDB::registerCoinsTransaction( return successful; }; -bool AccountRepositoryDB::loadAccountPlayers(AccountInfo &acc) { +bool AccountRepositoryDB::loadAccountPlayers(std::unique_ptr &acc) { auto result = g_database().storeQuery( - fmt::format("SELECT `name`, `deletion` FROM `players` WHERE `account_id` = {} ORDER BY `name` ASC", acc.id) + fmt::format("SELECT `name`, `deletion` FROM `players` WHERE `account_id` = {} ORDER BY `name` ASC", acc->id) ); if (!result) { - g_logger().error("Failed to load account[{}] players!", acc.id); + g_logger().error("Failed to load account[{}] players!", acc->id); return false; } @@ -170,43 +168,43 @@ bool AccountRepositoryDB::loadAccountPlayers(AccountInfo &acc) { continue; } - acc.players.try_emplace({ result->getString("name"), result->getNumber("deletion") }); + acc->players.try_emplace({ result->getString("name"), result->getNumber("deletion") }); } while (result->next()); return true; } -bool AccountRepositoryDB::load(const std::string &query, AccountInfo &acc) { +bool AccountRepositoryDB::load(const std::string &query, std::unique_ptr &acc) { auto result = g_database().storeQuery(query); if (result == nullptr) { return false; } - acc.id = result->getNumber("id"); - acc.accountType = result->getNumber("type"); - acc.premiumLastDay = result->getNumber("lastday"); - acc.sessionExpires = result->getNumber("expires"); - acc.premiumDaysPurchased = result->getNumber("premdays_purchased"); - acc.creationTime = result->getNumber("creation"); - acc.premiumRemainingDays = acc.premiumLastDay > getTimeNow() ? (acc.premiumLastDay - getTimeNow()) / 86400 : 0; + acc->id = result->getNumber("id"); + acc->accountType = result->getNumber("type"); + acc->premiumLastDay = result->getNumber("lastday"); + acc->sessionExpires = result->getNumber("expires"); + acc->premiumDaysPurchased = result->getNumber("premdays_purchased"); + acc->creationTime = result->getNumber("creation"); + acc->premiumRemainingDays = acc->premiumLastDay > getTimeNow() ? (acc->premiumLastDay - getTimeNow()) / 86400 : 0; setupLoyaltyInfo(acc); return loadAccountPlayers(acc); } -void AccountRepositoryDB::setupLoyaltyInfo(AccountInfo &acc) { - if (acc.premiumDaysPurchased >= acc.premiumRemainingDays && acc.creationTime != 0) { +void AccountRepositoryDB::setupLoyaltyInfo(std::unique_ptr &acc) { + if (acc->premiumDaysPurchased >= acc->premiumRemainingDays && acc->creationTime != 0) { return; } - if (acc.premiumDaysPurchased < acc.premiumRemainingDays) { - acc.premiumDaysPurchased = acc.premiumRemainingDays; + if (acc->premiumDaysPurchased < acc->premiumRemainingDays) { + acc->premiumDaysPurchased = acc->premiumRemainingDays; } - if (acc.creationTime == 0) { - acc.creationTime = getTimeNow(); + if (acc->creationTime == 0) { + acc->creationTime = getTimeNow(); } save(acc); diff --git a/src/account/account_repository_db.hpp b/src/account/account_repository_db.hpp index e34d864a090..014d5af1c68 100644 --- a/src/account/account_repository_db.hpp +++ b/src/account/account_repository_db.hpp @@ -15,10 +15,10 @@ class AccountRepositoryDB final : public AccountRepository { public: AccountRepositoryDB(); - bool loadByID(const uint32_t &id, AccountInfo &acc) override; - bool loadByEmailOrName(bool oldProtocol, const std::string &emailOrName, AccountInfo &acc) override; - bool loadBySession(const std::string &esseionKey, AccountInfo &acc) override; - bool save(const AccountInfo &accInfo) override; + bool loadByID(const uint32_t &id, std::unique_ptr &acc) override; + bool loadByEmailOrName(bool oldProtocol, const std::string &emailOrName, std::unique_ptr &acc) override; + bool loadBySession(const std::string &esseionKey, std::unique_ptr &acc) override; + bool save(const std::unique_ptr &accInfo) override; bool getCharacterByAccountIdAndName(const uint32_t &id, const std::string &name) override; @@ -36,7 +36,7 @@ class AccountRepositoryDB final : public AccountRepository { private: const std::map coinTypeToColumn; - bool load(const std::string &query, AccountInfo &acc); - bool loadAccountPlayers(AccountInfo &acc); - void setupLoyaltyInfo(AccountInfo &acc); + bool load(const std::string &query, std::unique_ptr &acc); + bool loadAccountPlayers(std::unique_ptr &acc); + void setupLoyaltyInfo(std::unique_ptr &acc); }; diff --git a/src/canary_server.cpp b/src/canary_server.cpp index aaf1de80530..687fa51b6cd 100644 --- a/src/canary_server.cpp +++ b/src/canary_server.cpp @@ -9,15 +9,20 @@ #include "canary_server.hpp" -#include "declarations.hpp" +#include "config/configmanager.hpp" +#include "creatures/npcs/npcs.hpp" #include "creatures/players/grouping/familiars.hpp" +#include "creatures/players/imbuements/imbuements.hpp" #include "creatures/players/storages/storages.hpp" #include "database/databasemanager.hpp" +#include "declarations.hpp" #include "game/game.hpp" -#include "game/zones/zone.hpp" #include "game/scheduling/dispatcher.hpp" #include "game/scheduling/events_scheduler.hpp" +#include "game/zones/zone.hpp" +#include "io/io_bosstiary.hpp" #include "io/iomarket.hpp" +#include "io/ioprey.hpp" #include "lib/thread/thread_pool.hpp" #include "lua/creature/events.hpp" #include "lua/modules/modules.hpp" @@ -26,8 +31,6 @@ #include "server/network/protocol/protocollogin.hpp" #include "server/network/protocol/protocolstatus.hpp" #include "server/network/webhook/webhook.hpp" -#include "io/ioprey.hpp" -#include "io/io_bosstiary.hpp" #include "core.hpp" diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index aa028b52284..1c00df76c2f 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -8,9 +8,11 @@ */ #include "config/configmanager.hpp" + #include "lib/di/container.hpp" #include "game/game.hpp" #include "server/network/webhook/webhook.hpp" +#include "utils/tools.hpp" #if LUA_VERSION_NUM >= 502 #undef lua_strlen diff --git a/src/creatures/appearance/mounts/mounts.cpp b/src/creatures/appearance/mounts/mounts.cpp index e68610fc51e..4b40d383543 100644 --- a/src/creatures/appearance/mounts/mounts.cpp +++ b/src/creatures/appearance/mounts/mounts.cpp @@ -8,6 +8,8 @@ */ #include "creatures/appearance/mounts/mounts.hpp" + +#include "config/configmanager.hpp" #include "game/game.hpp" #include "utils/pugicast.hpp" #include "utils/tools.hpp" diff --git a/src/creatures/appearance/outfit/outfit.cpp b/src/creatures/appearance/outfit/outfit.cpp index 7826ff71e37..4910f937bc3 100644 --- a/src/creatures/appearance/outfit/outfit.cpp +++ b/src/creatures/appearance/outfit/outfit.cpp @@ -8,9 +8,15 @@ */ #include "creatures/appearance/outfit/outfit.hpp" + +#include "config/configmanager.hpp" +#include "creatures/players/player.hpp" +#include "game/game.hpp" +#include "lib/di/container.hpp" #include "utils/pugicast.hpp" #include "utils/tools.hpp" -#include "game/game.hpp" + +std::vector> outfits[PLAYERSEX_LAST + 1]; Outfits &Outfits::getInstance() { return inject(); @@ -101,3 +107,17 @@ std::shared_ptr Outfits::getOutfitByLookType(const std::shared_ptr> &Outfits::getOutfits(PlayerSex_t sex) const { + return outfits[sex]; +} + +std::shared_ptr Outfits::getOutfitByName(PlayerSex_t sex, const std::string &name) const { + for (const auto &outfit : outfits[sex]) { + if (outfit->name == name) { + return outfit; + } + } + + return nullptr; +} diff --git a/src/creatures/appearance/outfit/outfit.hpp b/src/creatures/appearance/outfit/outfit.hpp index c4d49a7f1a2..2d9488449aa 100644 --- a/src/creatures/appearance/outfit/outfit.hpp +++ b/src/creatures/appearance/outfit/outfit.hpp @@ -9,9 +9,7 @@ #pragma once -#include "declarations.hpp" -#include "lib/di/container.hpp" - +enum PlayerSex_t : uint8_t; class Player; struct OutfitEntry { @@ -50,20 +48,7 @@ class Outfits { bool loadFromXml(); [[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]; - } - - std::shared_ptr getOutfitByName(PlayerSex_t sex, const std::string &name) const { - for (const auto &outfit : outfits[sex]) { - if (outfit->name == name) { - return outfit; - } - } - - return nullptr; - } + [[nodiscard]] const std::vector> &getOutfits(PlayerSex_t sex) const; -private: - std::vector> outfits[PLAYERSEX_LAST + 1]; + std::shared_ptr getOutfitByName(PlayerSex_t sex, const std::string &name) const; }; diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index c65fdf7f053..a63f42093db 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -7,22 +7,26 @@ * Website: https://docs.opentibiabr.com/ */ -#include "declarations.hpp" #include "creatures/combat/combat.hpp" -#include "lua/creature/events.hpp" -#include "lua/callbacks/event_callback.hpp" -#include "lua/callbacks/events_callbacks.hpp" + +#include "config/configmanager.hpp" +#include "creatures/combat/condition.hpp" +#include "creatures/combat/spells.hpp" +#include "creatures/monsters/monster.hpp" +#include "creatures/monsters/monsters.hpp" +#include "creatures/players/grouping/party.hpp" +#include "creatures/players/imbuements/imbuements.hpp" #include "creatures/players/wheel/player_wheel.hpp" #include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" #include "io/iobestiary.hpp" -#include "creatures/monsters/monster.hpp" -#include "creatures/monsters/monsters.hpp" +#include "io/ioprey.hpp" #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" +#include "lua/creature/events.hpp" +#include "map/spectators.hpp" int32_t Combat::getLevelFormula(const std::shared_ptr &player, const std::shared_ptr &wheelSpell, const CombatDamage &damage) const { if (!player) { @@ -429,6 +433,14 @@ void Combat::setPlayerCombatValues(formulaType_t newFormulaType, double newMina, this->maxb = newMaxb; } +void Combat::postCombatEffects(const std::shared_ptr &caster, const Position &origin, const Position &pos) const { + postCombatEffects(caster, origin, pos, params); +} + +void Combat::setOrigin(CombatOrigin origin) { + params.origin = origin; +} + bool Combat::setParam(CombatParam_t param, uint32_t value) { switch (param) { case COMBAT_PARAM_TYPE: { @@ -499,6 +511,18 @@ bool Combat::setParam(CombatParam_t param, uint32_t value) { return false; } +void Combat::setArea(std::unique_ptr &newArea) { + this->area = std::move(newArea); +} + +bool Combat::hasArea() const { + return area != nullptr; +} + +void Combat::addCondition(const std::shared_ptr &condition) { + params.conditionList.emplace_back(condition); +} + bool Combat::setCallback(CallBackParam_t key) { switch (key) { case CALLBACK_PARAM_LEVELMAGICVALUE: { @@ -1530,6 +1554,9 @@ bool Combat::isValidChainTarget(const std::shared_ptr &caster, const s //**********************************************************// +ValueCallback::ValueCallback(formulaType_t initType) : + type(initType) { } + uint32_t ValueCallback::getMagicLevelSkill(const std::shared_ptr &player, const CombatDamage &damage) const { if (!player) { return 0; @@ -1722,6 +1749,9 @@ void TargetCallback::onTargetCombat(const std::shared_ptr &creature, c //**********************************************************// +ChainCallback::ChainCallback(const uint8_t &chainTargets, const uint8_t &chainDistance, const bool &backtracking) : + m_chainDistance(chainDistance), m_chainTargets(chainTargets), m_backtracking(backtracking) { } + void ChainCallback::getChainValues(const std::shared_ptr &creature, uint8_t &maxTargets, uint8_t &chainDistance, bool &backtracking) { if (m_fromLua) { onChainCombat(creature, maxTargets, chainDistance, backtracking); @@ -1734,6 +1764,11 @@ void ChainCallback::getChainValues(const std::shared_ptr &creature, ui backtracking = m_backtracking; } } + +void ChainCallback::setFromLua(bool fromLua) { + m_fromLua = fromLua; +} + void ChainCallback::onChainCombat(const std::shared_ptr &creature, uint8_t &maxTargets, uint8_t &chainDistance, bool &backtracking) const { // onChainCombat(creature) if (!LuaScriptInterface::reserveScriptEnv()) { @@ -1832,6 +1867,10 @@ void AreaCombat::clear() { std::ranges::fill(areas, nullptr); } +std::unique_ptr AreaCombat::clone() const { + return std::make_unique(*this); +} + AreaCombat::AreaCombat(const AreaCombat &rhs) { hasExtArea = rhs.hasExtArea; for (uint_fast8_t i = 0; i <= Direction::DIRECTION_LAST; ++i) { @@ -1841,6 +1880,10 @@ AreaCombat::AreaCombat(const AreaCombat &rhs) { } } +AreaCombat::~AreaCombat() { + clear(); +} + void AreaCombat::getList(const Position ¢erPos, const Position &targetPos, std::vector> &list) const { const std::unique_ptr &area = getArea(centerPos, targetPos); if (!area) { @@ -1948,6 +1991,36 @@ void AreaCombat::copyArea(const std::unique_ptr &input, const std::u } } +const std::unique_ptr &AreaCombat::getArea(const Position ¢erPos, const Position &targetPos) const { + int32_t dx = Position::getOffsetX(targetPos, centerPos); + int32_t dy = Position::getOffsetY(targetPos, centerPos); + + Direction dir; + if (dx < 0) { + dir = DIRECTION_WEST; + } else if (dx > 0) { + dir = DIRECTION_EAST; + } else if (dy < 0) { + dir = DIRECTION_NORTH; + } else { + dir = DIRECTION_SOUTH; + } + + if (hasExtArea) { + if (dx < 0 && dy < 0) { + dir = DIRECTION_NORTHWEST; + } else if (dx > 0 && dy < 0) { + dir = DIRECTION_NORTHEAST; + } else if (dx < 0 && dy > 0) { + dir = DIRECTION_SOUTHWEST; + } else if (dx > 0 && dy > 0) { + dir = DIRECTION_SOUTHEAST; + } + } + + return areas[dir]; +} + std::unique_ptr AreaCombat::createArea(const std::list &list, uint32_t rows) { uint32_t cols; if (rows == 0) { @@ -2209,3 +2282,107 @@ void Combat::applyExtensions(const std::shared_ptr &caster, const std: damage.secondary.value *= monster->getAttackMultiplier(); } } + +MagicField::MagicField(uint16_t type) : + Item(type), createTime(OTSYS_TIME()) { } + +std::shared_ptr MagicField::getMagicField() { + return static_self_cast(); +} + +bool MagicField::isReplaceable() const { + return Item::items[getID()].replaceable; +} + +CombatType_t MagicField::getCombatType() const { + const ItemType &it = items[getID()]; + return it.combatType; +} + +int32_t MagicField::getDamage() const { + const ItemType &it = items[getID()]; + if (it.conditionDamage) { + return it.conditionDamage->getTotalDamage(); + } + return 0; +} + +MatrixArea::MatrixArea(uint32_t initRows, uint32_t initCols) : + centerX(0), centerY(0), rows(initRows), cols(initCols) { + data_ = new bool*[rows]; + + for (uint32_t row = 0; row < rows; ++row) { + data_[row] = new bool[cols]; + + for (uint32_t col = 0; col < cols; ++col) { + data_[row][col] = false; + } + } +} + +MatrixArea::MatrixArea(const MatrixArea &rhs) { + centerX = rhs.centerX; + centerY = rhs.centerY; + rows = rhs.rows; + cols = rhs.cols; + + data_ = new bool*[rows]; + + for (uint32_t row = 0; row < rows; ++row) { + data_[row] = new bool[cols]; + + for (uint32_t col = 0; col < cols; ++col) { + data_[row][col] = rhs.data_[row][col]; + } + } +} + +MatrixArea::~MatrixArea() { + for (uint32_t row = 0; row < rows; ++row) { + delete[] data_[row]; + } + + delete[] data_; +} + +std::unique_ptr MatrixArea::clone() const { + return std::make_unique(*this); +} + +void MatrixArea::setValue(uint32_t row, uint32_t col, bool value) const { + if (row < rows && col < cols) { + data_[row][col] = value; + } else { + g_logger().error("[{}] Access exceeds the upper limit of memory block"); + throw std::out_of_range("Access exceeds the upper limit of memory block"); + } +} + +bool MatrixArea::getValue(uint32_t row, uint32_t col) const { + return data_[row][col]; +} + +void MatrixArea::setCenter(uint32_t y, uint32_t x) { + centerX = x; + centerY = y; +} + +void MatrixArea::getCenter(uint32_t &y, uint32_t &x) const { + x = centerX; + y = centerY; +} + +uint32_t MatrixArea::getRows() const { + return rows; +} + +uint32_t MatrixArea::getCols() const { + return cols; +} +const bool* MatrixArea::operator[](uint32_t i) const { + return data_[i]; +} + +bool* MatrixArea::operator[](uint32_t i) { + return data_[i]; +} diff --git a/src/creatures/combat/combat.hpp b/src/creatures/combat/combat.hpp index d842826769d..1b8af711235 100644 --- a/src/creatures/combat/combat.hpp +++ b/src/creatures/combat/combat.hpp @@ -9,23 +9,23 @@ #pragma once +#include "items/item.hpp" #include "lua/global/baseevents.hpp" -#include "creatures/combat/condition.hpp" -#include "map/map.hpp" class Condition; class Creature; -class Item; class Spell; class Player; class MatrixArea; class Weapon; +class Tile; + +using CreatureVector = std::vector>; // for luascript callback class ValueCallback final : public CallBack { public: - explicit ValueCallback(formulaType_t initType) : - type(initType) { } + explicit ValueCallback(formulaType_t initType); /** * @brief Get the magic level skill for the player. @@ -60,13 +60,10 @@ class TargetCallback final : public CallBack { class ChainCallback final : public CallBack { public: ChainCallback() = default; - ChainCallback(const uint8_t &chainTargets, const uint8_t &chainDistance, const bool &backtracking) : - m_chainDistance(chainDistance), m_chainTargets(chainTargets), m_backtracking(backtracking) { } + ChainCallback(const uint8_t &chainTargets, const uint8_t &chainDistance, const bool &backtracking); void getChainValues(const std::shared_ptr &creature, uint8_t &maxTargets, uint8_t &chainDistance, bool &backtracking); - void setFromLua(bool fromLua) { - m_fromLua = fromLua; - } + void setFromLua(bool fromLua); private: void onChainCombat(const std::shared_ptr &creature, uint8_t &chainTargets, uint8_t &chainDistance, bool &backtracking) const; @@ -116,84 +113,28 @@ using CombatFunction = std::function, std::shared class MatrixArea { public: - MatrixArea(uint32_t initRows, uint32_t initCols) : - centerX(0), centerY(0), rows(initRows), cols(initCols) { - data_ = new bool*[rows]; - - for (uint32_t row = 0; row < rows; ++row) { - data_[row] = new bool[cols]; - - for (uint32_t col = 0; col < cols; ++col) { - data_[row][col] = false; - } - } - } + MatrixArea(uint32_t initRows, uint32_t initCols); - MatrixArea(const MatrixArea &rhs) { - centerX = rhs.centerX; - centerY = rhs.centerY; - rows = rhs.rows; - cols = rhs.cols; + MatrixArea(const MatrixArea &rhs); - data_ = new bool*[rows]; + ~MatrixArea(); - for (uint32_t row = 0; row < rows; ++row) { - data_[row] = new bool[cols]; + std::unique_ptr clone() const; - for (uint32_t col = 0; col < cols; ++col) { - data_[row][col] = rhs.data_[row][col]; - } - } - } - - ~MatrixArea() { - for (uint32_t row = 0; row < rows; ++row) { - delete[] data_[row]; - } + // non-assignable + MatrixArea &operator=(const MatrixArea &) = delete; - delete[] data_; - } + void setValue(uint32_t row, uint32_t col, bool value) const; + bool getValue(uint32_t row, uint32_t col) const; - std::unique_ptr clone() const { - return std::make_unique(*this); - } + void setCenter(uint32_t y, uint32_t x); + void getCenter(uint32_t &y, uint32_t &x) const; - // non-assignable - MatrixArea &operator=(const MatrixArea &) = delete; + uint32_t getRows() const; + uint32_t getCols() const; - void setValue(uint32_t row, uint32_t col, bool value) const { - if (row < rows && col < cols) { - data_[row][col] = value; - } else { - g_logger().error("[{}] Access exceeds the upper limit of memory block"); - } - } - bool getValue(uint32_t row, uint32_t col) const { - return data_[row][col]; - } - - void setCenter(uint32_t y, uint32_t x) { - centerX = x; - centerY = y; - } - void getCenter(uint32_t &y, uint32_t &x) const { - x = centerX; - y = centerY; - } - - uint32_t getRows() const { - return rows; - } - uint32_t getCols() const { - return cols; - } - - const bool* operator[](uint32_t i) const { - return data_[i]; - } - bool* operator[](uint32_t i) { - return data_[i]; - } + const bool* operator[](uint32_t i) const; + bool* operator[](uint32_t i); private: uint32_t centerX; @@ -209,9 +150,7 @@ class AreaCombat { AreaCombat() = default; AreaCombat(const AreaCombat &rhs); - ~AreaCombat() { - clear(); - } + ~AreaCombat(); // non-assignable AreaCombat &operator=(const AreaCombat &) = delete; @@ -224,43 +163,13 @@ class AreaCombat { void setupExtArea(const std::list &list, uint32_t rows); void clear(); - std::unique_ptr clone() const { - return std::make_unique(*this); - } + std::unique_ptr clone() const; private: std::unique_ptr createArea(const std::list &list, uint32_t rows); void copyArea(const std::unique_ptr &input, const std::unique_ptr &output, MatrixOperation_t op) const; - const std::unique_ptr &getArea(const Position ¢erPos, const Position &targetPos) const { - int32_t dx = Position::getOffsetX(targetPos, centerPos); - int32_t dy = Position::getOffsetY(targetPos, centerPos); - - Direction dir; - if (dx < 0) { - dir = DIRECTION_WEST; - } else if (dx > 0) { - dir = DIRECTION_EAST; - } else if (dy < 0) { - dir = DIRECTION_NORTH; - } else { - dir = DIRECTION_SOUTH; - } - - if (hasExtArea) { - if (dx < 0 && dy < 0) { - dir = DIRECTION_NORTHWEST; - } else if (dx > 0 && dy < 0) { - dir = DIRECTION_NORTHEAST; - } else if (dx < 0 && dy > 0) { - dir = DIRECTION_SOUTHWEST; - } else if (dx > 0 && dy > 0) { - dir = DIRECTION_SOUTHEAST; - } - } - - return areas[dir]; - } + const std::unique_ptr &getArea(const Position ¢erPos, const Position &targetPos) const; std::array, Direction::DIRECTION_LAST + 1> areas {}; bool hasExtArea = false; @@ -311,23 +220,13 @@ class Combat { CallBack* getCallback(CallBackParam_t key) const; bool setParam(CombatParam_t param, uint32_t value); - void setArea(std::unique_ptr &newArea) { - this->area = std::move(newArea); - } - bool hasArea() const { - return area != nullptr; - } - void addCondition(const std::shared_ptr &condition) { - params.conditionList.emplace_back(condition); - } + void setArea(std::unique_ptr &newArea); + bool hasArea() const; + void addCondition(const std::shared_ptr &condition); void setPlayerCombatValues(formulaType_t formulaType, double mina, double minb, double maxa, double maxb); - void postCombatEffects(const std::shared_ptr &caster, const Position &origin, const Position &pos) const { - postCombatEffects(caster, origin, pos, params); - } + void postCombatEffects(const std::shared_ptr &caster, const Position &origin, const Position &pos) const; - void setOrigin(CombatOrigin origin) { - params.origin = origin; - } + void setOrigin(CombatOrigin origin); /** * @brief Sets the name of the instant spell. @@ -412,27 +311,13 @@ class Combat { class MagicField final : public Item { public: - explicit MagicField(uint16_t type) : - Item(type), createTime(OTSYS_TIME()) { } - - std::shared_ptr getMagicField() override { - return static_self_cast(); - } - - bool isReplaceable() const { - return Item::items[getID()].replaceable; - } - CombatType_t getCombatType() const { - const ItemType &it = items[getID()]; - return it.combatType; - } - int32_t getDamage() const { - const ItemType &it = items[getID()]; - if (it.conditionDamage) { - return it.conditionDamage->getTotalDamage(); - } - return 0; - } + explicit MagicField(uint16_t type); + + std::shared_ptr getMagicField() override; + + bool isReplaceable() const; + CombatType_t getCombatType() const; + int32_t getDamage() const; void onStepInField(const std::shared_ptr &creature); private: diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index a464f3db062..e49a6662991 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -8,10 +8,19 @@ */ #include "creatures/combat/condition.hpp" + +#include "config/configmanager.hpp" +#include "creatures/combat/combat.hpp" +#include "creatures/monsters/monsters.hpp" +#include "enums/player_icons.hpp" #include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" #include "io/fileloader.hpp" +#include "kv/kv.hpp" #include "map/spectators.hpp" +#include "creatures/creature.hpp" +#include "creatures/players/player.hpp" +#include "server/network/protocol/protocolgame.hpp" /** * Condition @@ -313,6 +322,10 @@ std::shared_ptr Condition::createCondition(PropStream &propStream) { return createCondition(static_cast(id), static_cast(type), ticks, 0, buff != 0, subId); } +Condition::Condition(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff, uint32_t initSubId, bool isPersistent) : + endTime(initTicks == -1 ? std::numeric_limits::max() : 0), + subId(initSubId), ticks(initTicks), conditionType(initType), id(initId), isBuff(initBuff), m_isPersistent(isPersistent) { } + bool Condition::startCondition(std::shared_ptr) { if (ticks > 0) { endTime = ticks + OTSYS_TIME(); @@ -368,6 +381,26 @@ std::unordered_set Condition::getIcons() const { return icons; } +ConditionId_t Condition::getId() const { + return id; +} + +uint32_t Condition::getSubId() const { + return subId; +} + +ConditionType_t Condition::getType() const { + return conditionType; +} + +int64_t Condition::getEndTime() const { + return endTime; +} + +int32_t Condition::getTicks() const { + return ticks; +} + bool Condition::updateCondition(const std::shared_ptr &addCondition) { if (conditionType != addCondition->getType()) { return false; @@ -388,6 +421,9 @@ bool Condition::updateCondition(const std::shared_ptr &addCondition) * ConditionGeneric */ +ConditionGeneric::ConditionGeneric(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff, uint32_t initSubId, bool isPersistent) : + Condition(initId, initType, initTicks, initBuff, initSubId, isPersistent) { } + bool ConditionGeneric::startCondition(std::shared_ptr creature) { return Condition::startCondition(creature); } @@ -452,6 +488,10 @@ std::unordered_set ConditionGeneric::getIcons() const { return icons; } +std::shared_ptr ConditionGeneric::clone() const { + return std::make_shared(*this); +} + /** * ConditionAttributes */ @@ -581,6 +621,9 @@ void ConditionAttributes::serialize(PropWriteStream &propWriteStream) { propWriteStream.write(charmChanceModifier); } +ConditionAttributes::ConditionAttributes(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff, uint32_t initSubId) : + ConditionGeneric(initId, initType, initTicks, initBuff, initSubId) { } + bool ConditionAttributes::startCondition(std::shared_ptr creature) { if (!Condition::startCondition(creature)) { return false; @@ -1078,6 +1121,10 @@ bool ConditionAttributes::setParam(ConditionParam_t param, int32_t value) { } } +std::shared_ptr ConditionAttributes::clone() const { + return std::make_shared(*this); +} + int32_t ConditionAttributes::getAbsorbByIndex(uint8_t index) const { try { return absorbs.at(index); @@ -1150,6 +1197,9 @@ void ConditionAttributes::setIncreasePercent(uint8_t index, int32_t value) { * ConditionRegeneration */ +ConditionRegeneration::ConditionRegeneration(ConditionId_t initId, ConditionType_t initType, int32_t iniTicks, bool initBuff, uint32_t initSubId) : + ConditionGeneric(initId, initType, iniTicks, initBuff, initSubId) { } + bool ConditionRegeneration::startCondition(std::shared_ptr creature) { if (!Condition::startCondition(creature)) { return false; @@ -1317,10 +1367,17 @@ uint32_t ConditionRegeneration::getManaTicks(const std::shared_ptr &cr return manaTicks; } +std::shared_ptr ConditionRegeneration::clone() const { + return std::make_shared(*this); +} + /** * ConditionManaShield */ +ConditionManaShield::ConditionManaShield(ConditionId_t initId, ConditionType_t initType, int32_t iniTicks, bool initBuff, uint32_t initSubId) : + Condition(initId, initType, iniTicks, initBuff, initSubId) { } + bool ConditionManaShield::startCondition(std::shared_ptr creature) { if (!Condition::startCondition(creature)) { return false; @@ -1385,6 +1442,10 @@ bool ConditionManaShield::setParam(ConditionParam_t param, int32_t value) { } } +std::shared_ptr ConditionManaShield::clone() const { + return std::make_shared(*this); +} + std::unordered_set ConditionManaShield::getIcons() const { auto icons = Condition::getIcons(); if (manaShield != 0) { @@ -1399,6 +1460,9 @@ std::unordered_set ConditionManaShield::getIcons() const { * ConditionSoul */ +ConditionSoul::ConditionSoul(ConditionId_t initId, ConditionType_t initType, int32_t iniTicks, bool initBuff, uint32_t initSubId) : + ConditionGeneric(initId, initType, iniTicks, initBuff, initSubId) { } + void ConditionSoul::addCondition(std::shared_ptr, const std::shared_ptr addCondition) { if (updateCondition(addCondition)) { setTicks(addCondition->getTicks()); @@ -1460,6 +1524,10 @@ bool ConditionSoul::setParam(ConditionParam_t param, int32_t value) { } } +std::shared_ptr ConditionSoul::clone() const { + return std::make_shared(*this); +} + /** * ConditionDamage */ @@ -1598,6 +1666,10 @@ bool ConditionDamage::addDamage(int32_t rounds, int32_t time, int32_t value) { return true; } +bool ConditionDamage::doForceUpdate() const { + return forceUpdate; +} + bool ConditionDamage::init() { if (periodDamage != 0) { return true; @@ -1833,6 +1905,13 @@ std::unordered_set ConditionDamage::getIcons() const { return icons; } +std::shared_ptr ConditionDamage::clone() const { + return std::make_shared(*this); +} + +ConditionDamage::ConditionDamage(ConditionId_t intiId, ConditionType_t initType, bool initBuff, uint32_t initSubId) : + Condition(intiId, initType, 0, initBuff, initSubId) { } + void ConditionDamage::generateDamageList(int32_t amount, int32_t start, std::list &list) { amount = std::abs(amount); int32_t sum = 0; @@ -2076,6 +2155,9 @@ bool ConditionFeared::setPositionParam(ConditionParam_t param, const Position &p return false; } +ConditionFeared::ConditionFeared(ConditionId_t intiId, ConditionType_t initType, int32_t initTicks, bool initBuff, uint32_t initSubId) : + Condition(intiId, initType, initTicks, initBuff, initSubId) { } + bool ConditionFeared::startCondition(std::shared_ptr creature) { g_logger().debug("[ConditionFeared::executeCondition] Condition started for {}", creature->getName()); getFleeDirection(creature); @@ -2133,6 +2215,10 @@ std::unordered_set ConditionFeared::getIcons() const { return icons; } +std::shared_ptr ConditionFeared::clone() const { + return std::make_shared(*this); +} + /** * ConditionSpeed */ @@ -2200,6 +2286,9 @@ void ConditionSpeed::serialize(PropWriteStream &propWriteStream) { propWriteStream.write(maxb); } +ConditionSpeed::ConditionSpeed(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff, uint32_t initSubId, int32_t initChangeSpeed) : + Condition(initId, initType, initTicks, initBuff, initSubId), speedDelta(initChangeSpeed) { } + bool ConditionSpeed::startCondition(std::shared_ptr creature) { if (!Condition::startCondition(creature)) { return false; @@ -2282,10 +2371,17 @@ std::unordered_set ConditionSpeed::getIcons() const { return icons; } +std::shared_ptr ConditionSpeed::clone() const { + return std::make_shared(*this); +} + /** * ConditionInvisible */ +ConditionInvisible::ConditionInvisible(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff, uint32_t initSubId) : + ConditionGeneric(initId, initType, initTicks, initBuff, initSubId) { } + bool ConditionInvisible::startCondition(std::shared_ptr creature) { if (!Condition::startCondition(creature)) { return false; @@ -2301,6 +2397,10 @@ void ConditionInvisible::endCondition(std::shared_ptr creature) { } } +std::shared_ptr ConditionInvisible::clone() const { + return std::make_shared(*this); +} + /** * ConditionOutfit */ @@ -2327,6 +2427,9 @@ void ConditionOutfit::serialize(PropWriteStream &propWriteStream) { propWriteStream.write(outfit); } +ConditionOutfit::ConditionOutfit(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff, uint32_t initSubId) : + Condition(initId, initType, initTicks, initBuff, initSubId) { } + bool ConditionOutfit::startCondition(std::shared_ptr creature) { if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS) && outfit.lookType != 0 && !g_game().isLookTypeRegistered(outfit.lookType)) { g_logger().warn("[ConditionOutfit::startCondition] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", outfit.lookType); @@ -2385,10 +2488,17 @@ void ConditionOutfit::addCondition(std::shared_ptr creature, const std } } +std::shared_ptr ConditionOutfit::clone() const { + return std::make_shared(*this); +} + /** * ConditionLight */ +ConditionLight::ConditionLight(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff, uint32_t initSubId, uint8_t initLightlevel, uint8_t initLightcolor) : + Condition(initId, initType, initTicks, initBuff, initSubId), lightInfo(initLightlevel, initLightcolor) { } + bool ConditionLight::startCondition(std::shared_ptr creature) { if (!Condition::startCondition(creature)) { return false; @@ -2437,6 +2547,10 @@ void ConditionLight::addCondition(std::shared_ptr creature, const std: } } +std::shared_ptr ConditionLight::clone() const { + return std::make_shared(*this); +} + bool ConditionLight::setParam(ConditionParam_t param, int32_t value) { bool ret = Condition::setParam(param, value); if (ret) { @@ -2518,6 +2632,13 @@ void ConditionSpellCooldown::addCondition(std::shared_ptr creature, co } } +std::shared_ptr ConditionSpellCooldown::clone() const { + return std::make_shared(*this); +} + +ConditionSpellCooldown::ConditionSpellCooldown(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff, uint32_t initSubId) : + ConditionGeneric(initId, initType, initTicks, initBuff, initSubId) { } + bool ConditionSpellCooldown::startCondition(std::shared_ptr creature) { if (!Condition::startCondition(creature)) { return false; @@ -2549,6 +2670,13 @@ void ConditionSpellGroupCooldown::addCondition(std::shared_ptr creatur } } +std::shared_ptr ConditionSpellGroupCooldown::clone() const { + return std::make_shared(*this); +} + +ConditionSpellGroupCooldown::ConditionSpellGroupCooldown(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff, uint32_t initSubId) : + ConditionGeneric(initId, initType, initTicks, initBuff, initSubId) { } + bool ConditionSpellGroupCooldown::startCondition(std::shared_ptr creature) { if (!Condition::startCondition(creature)) { return false; diff --git a/src/creatures/combat/condition.hpp b/src/creatures/combat/condition.hpp index 88949d5e3fb..1cc798624e3 100644 --- a/src/creatures/combat/condition.hpp +++ b/src/creatures/combat/condition.hpp @@ -9,9 +9,10 @@ #pragma once -#include "declarations.hpp" +#include "creatures/creatures_definitions.hpp" +#include "game/movement/position.hpp" -#include "enums/player_icons.hpp" +enum class PlayerIcon : uint8_t; class Creature; class Player; @@ -21,9 +22,7 @@ class PropWriteStream; class Condition : public SharedObject { public: Condition() = default; - Condition(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff = false, uint32_t initSubId = 0, bool isPersistent = false) : - endTime(initTicks == -1 ? std::numeric_limits::max() : 0), - subId(initSubId), ticks(initTicks), conditionType(initType), id(initId), isBuff(initBuff), m_isPersistent(isPersistent) { } + Condition(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff = false, uint32_t initSubId = 0, bool isPersistent = false); virtual ~Condition() = default; virtual bool startCondition(std::shared_ptr creature); @@ -31,24 +30,14 @@ class Condition : public SharedObject { virtual void endCondition(std::shared_ptr creature) = 0; virtual void addCondition(std::shared_ptr creature, std::shared_ptr condition) = 0; virtual std::unordered_set getIcons() const; - ConditionId_t getId() const { - return id; - } - uint32_t getSubId() const { - return subId; - } + ConditionId_t getId() const; + uint32_t getSubId() const; virtual std::shared_ptr clone() const = 0; - ConditionType_t getType() const { - return conditionType; - } - int64_t getEndTime() const { - return endTime; - } - int32_t getTicks() const { - return ticks; - } + ConditionType_t getType() const; + int64_t getEndTime() const; + int32_t getTicks() const; void setTicks(int32_t newTicks); static std::shared_ptr createCondition(ConditionId_t id, ConditionType_t type, int32_t ticks, int32_t param = 0, bool buff = false, uint32_t subId = 0, bool isPersistent = false); @@ -87,8 +76,7 @@ class Condition : public SharedObject { class ConditionGeneric : public Condition { public: - ConditionGeneric(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff = false, uint32_t initSubId = 0, bool isPersistent = false) : - Condition(initId, initType, initTicks, initBuff, initSubId, isPersistent) { } + ConditionGeneric(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff = false, uint32_t initSubId = 0, bool isPersistent = false); bool startCondition(std::shared_ptr creature) override; bool executeCondition(const std::shared_ptr &creature, int32_t interval) override; @@ -96,15 +84,12 @@ class ConditionGeneric : public Condition { void addCondition(std::shared_ptr creature, std::shared_ptr condition) override; std::unordered_set getIcons() const override; - std::shared_ptr clone() const override { - return std::make_shared(*this); - } + std::shared_ptr clone() const override; }; class ConditionAttributes final : public ConditionGeneric { public: - ConditionAttributes(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff = false, uint32_t initSubId = 0) : - ConditionGeneric(initId, initType, initTicks, initBuff, initSubId) { } + ConditionAttributes(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff = false, uint32_t initSubId = 0); bool startCondition(std::shared_ptr creature) final; bool executeCondition(const std::shared_ptr &creature, int32_t interval) final; @@ -113,9 +98,7 @@ class ConditionAttributes final : public ConditionGeneric { bool setParam(ConditionParam_t param, int32_t value) final; - std::shared_ptr clone() const final { - return std::make_shared(*this); - } + std::shared_ptr clone() const override; // serialization void serialize(PropWriteStream &propWriteStream) final; @@ -170,8 +153,7 @@ class ConditionAttributes final : public ConditionGeneric { class ConditionRegeneration final : public ConditionGeneric { public: - ConditionRegeneration(ConditionId_t initId, ConditionType_t initType, int32_t iniTicks, bool initBuff = false, uint32_t initSubId = 0) : - ConditionGeneric(initId, initType, iniTicks, initBuff, initSubId) { } + ConditionRegeneration(ConditionId_t initId, ConditionType_t initType, int32_t iniTicks, bool initBuff = false, uint32_t initSubId = 0); bool startCondition(std::shared_ptr creature) override; void endCondition(std::shared_ptr creature) override; @@ -183,9 +165,7 @@ class ConditionRegeneration final : public ConditionGeneric { uint32_t getHealthTicks(const std::shared_ptr &creature) const; uint32_t getManaTicks(const std::shared_ptr &creature) const; - std::shared_ptr clone() const override { - return std::make_shared(*this); - } + std::shared_ptr clone() const override; // serialization void serialize(PropWriteStream &propWriteStream) override; @@ -203,8 +183,7 @@ class ConditionRegeneration final : public ConditionGeneric { class ConditionManaShield final : public Condition { public: - ConditionManaShield(ConditionId_t initId, ConditionType_t initType, int32_t iniTicks, bool initBuff = false, uint32_t initSubId = 0) : - Condition(initId, initType, iniTicks, initBuff, initSubId) { } + ConditionManaShield(ConditionId_t initId, ConditionType_t initType, int32_t iniTicks, bool initBuff = false, uint32_t initSubId = 0); bool startCondition(std::shared_ptr creature) override; void endCondition(std::shared_ptr creature) override; @@ -213,9 +192,7 @@ class ConditionManaShield final : public Condition { bool setParam(ConditionParam_t param, int32_t value) override; - std::shared_ptr clone() const override { - return std::make_shared(*this); - } + std::shared_ptr clone() const override; // serialization void serialize(PropWriteStream &propWriteStream) override; @@ -227,17 +204,14 @@ class ConditionManaShield final : public Condition { class ConditionSoul final : public ConditionGeneric { public: - ConditionSoul(ConditionId_t initId, ConditionType_t initType, int32_t iniTicks, bool initBuff = false, uint32_t initSubId = 0) : - ConditionGeneric(initId, initType, iniTicks, initBuff, initSubId) { } + ConditionSoul(ConditionId_t initId, ConditionType_t initType, int32_t iniTicks, bool initBuff = false, uint32_t initSubId = 0); void addCondition(std::shared_ptr creature, std::shared_ptr addCondition) override; bool executeCondition(const std::shared_ptr &creature, int32_t interval) override; bool setParam(ConditionParam_t param, int32_t value) override; - std::shared_ptr clone() const override { - return std::make_shared(*this); - } + std::shared_ptr clone() const override; // serialization void serialize(PropWriteStream &propWriteStream) override; @@ -251,22 +225,18 @@ class ConditionSoul final : public ConditionGeneric { class ConditionInvisible final : public ConditionGeneric { public: - ConditionInvisible(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff = false, uint32_t initSubId = 0) : - ConditionGeneric(initId, initType, initTicks, initBuff, initSubId) { } + ConditionInvisible(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff = false, uint32_t initSubId = 0); bool startCondition(std::shared_ptr creature) override; void endCondition(std::shared_ptr creature) override; - std::shared_ptr clone() const override { - return std::make_shared(*this); - } + std::shared_ptr clone() const override; }; class ConditionDamage final : public Condition { public: ConditionDamage() = default; - ConditionDamage(ConditionId_t intiId, ConditionType_t initType, bool initBuff = false, uint32_t initSubId = 0) : - Condition(intiId, initType, 0, initBuff, initSubId) { } + ConditionDamage(ConditionId_t intiId, ConditionType_t initType, bool initBuff = false, uint32_t initSubId = 0); static void generateDamageList(int32_t amount, int32_t start, std::list &list); @@ -276,16 +246,12 @@ class ConditionDamage final : public Condition { void addCondition(std::shared_ptr creature, std::shared_ptr condition) override; std::unordered_set getIcons() const override; - std::shared_ptr clone() const override { - return std::make_shared(*this); - } + std::shared_ptr clone() const override; bool setParam(ConditionParam_t param, int32_t value) override; bool addDamage(int32_t rounds, int32_t time, int32_t value); - bool doForceUpdate() const { - return forceUpdate; - } + bool doForceUpdate() const; int32_t getTotalDamage() const; // serialization @@ -318,8 +284,7 @@ class ConditionDamage final : public Condition { class ConditionFeared final : public Condition { public: ConditionFeared() = default; - ConditionFeared(ConditionId_t intiId, ConditionType_t initType, int32_t initTicks, bool initBuff, uint32_t initSubId) : - Condition(intiId, initType, initTicks, initBuff, initSubId) { } + ConditionFeared(ConditionId_t intiId, ConditionType_t initType, int32_t initTicks, bool initBuff, uint32_t initSubId); bool startCondition(std::shared_ptr creature) override; bool executeCondition(const std::shared_ptr &creature, int32_t interval) override; @@ -327,9 +292,7 @@ class ConditionFeared final : public Condition { void addCondition(std::shared_ptr creature, std::shared_ptr condition) override; std::unordered_set getIcons() const override; - std::shared_ptr clone() const override { - return std::make_shared(*this); - } + std::shared_ptr clone() const override; bool setPositionParam(ConditionParam_t param, const Position &pos) override; @@ -357,8 +320,7 @@ class ConditionFeared final : public Condition { class ConditionSpeed final : public Condition { public: - ConditionSpeed(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff, uint32_t initSubId, int32_t initChangeSpeed) : - Condition(initId, initType, initTicks, initBuff, initSubId), speedDelta(initChangeSpeed) { } + ConditionSpeed(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff, uint32_t initSubId, int32_t initChangeSpeed); bool startCondition(std::shared_ptr creature) override; bool executeCondition(const std::shared_ptr &creature, int32_t interval) override; @@ -366,9 +328,7 @@ class ConditionSpeed final : public Condition { void addCondition(std::shared_ptr creature, std::shared_ptr condition) override; std::unordered_set getIcons() const override; - std::shared_ptr clone() const override { - return std::make_shared(*this); - } + std::shared_ptr clone() const override; bool setParam(ConditionParam_t param, int32_t value) override; @@ -392,17 +352,14 @@ class ConditionSpeed final : public Condition { class ConditionOutfit final : public Condition { public: - ConditionOutfit(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff = false, uint32_t initSubId = 0) : - Condition(initId, initType, initTicks, initBuff, initSubId) { } + ConditionOutfit(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff = false, uint32_t initSubId = 0); bool startCondition(std::shared_ptr creature) override; bool executeCondition(const std::shared_ptr &creature, int32_t interval) override; void endCondition(std::shared_ptr creature) override; void addCondition(std::shared_ptr creature, std::shared_ptr condition) override; - std::shared_ptr clone() const override { - return std::make_shared(*this); - } + std::shared_ptr clone() const override; void setOutfit(const Outfit_t &outfit); void setLazyMonsterOutfit(const std::string &monsterName); @@ -418,17 +375,14 @@ class ConditionOutfit final : public Condition { class ConditionLight final : public Condition { public: - ConditionLight(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff, uint32_t initSubId, uint8_t initLightlevel, uint8_t initLightcolor) : - Condition(initId, initType, initTicks, initBuff, initSubId), lightInfo(initLightlevel, initLightcolor) { } + ConditionLight(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff, uint32_t initSubId, uint8_t initLightlevel, uint8_t initLightcolor); bool startCondition(std::shared_ptr creature) override; bool executeCondition(const std::shared_ptr &creature, int32_t interval) override; void endCondition(std::shared_ptr creature) override; void addCondition(std::shared_ptr creature, std::shared_ptr addCondition) override; - std::shared_ptr clone() const override { - return std::make_shared(*this); - } + std::shared_ptr clone() const override; bool setParam(ConditionParam_t param, int32_t value) override; @@ -444,26 +398,20 @@ class ConditionLight final : public Condition { class ConditionSpellCooldown final : public ConditionGeneric { public: - ConditionSpellCooldown(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff = false, uint32_t initSubId = 0) : - ConditionGeneric(initId, initType, initTicks, initBuff, initSubId) { } + ConditionSpellCooldown(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff = false, uint32_t initSubId = 0); bool startCondition(std::shared_ptr creature) override; void addCondition(std::shared_ptr creature, std::shared_ptr condition) override; - std::shared_ptr clone() const override { - return std::make_shared(*this); - } + std::shared_ptr clone() const override; }; class ConditionSpellGroupCooldown final : public ConditionGeneric { public: - ConditionSpellGroupCooldown(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff = false, uint32_t initSubId = 0) : - ConditionGeneric(initId, initType, initTicks, initBuff, initSubId) { } + ConditionSpellGroupCooldown(ConditionId_t initId, ConditionType_t initType, int32_t initTicks, bool initBuff = false, uint32_t initSubId = 0); bool startCondition(std::shared_ptr creature) override; void addCondition(std::shared_ptr creature, std::shared_ptr condition) override; - std::shared_ptr clone() const override { - return std::make_shared(*this); - } + std::shared_ptr clone() const override; }; diff --git a/src/creatures/combat/spells.cpp b/src/creatures/combat/spells.cpp index 26635d84421..b6ae30bee87 100644 --- a/src/creatures/combat/spells.cpp +++ b/src/creatures/combat/spells.cpp @@ -7,17 +7,23 @@ * Website: https://docs.opentibiabr.com/ */ -#include "creatures/combat/combat.hpp" #include "creatures/combat/spells.hpp" -#include "creatures/monsters/monster.hpp" -#include "game/game.hpp" -#include "lua/scripts/lua_environment.hpp" -#include "creatures/players/wheel/player_wheel.hpp" +#include "config/configmanager.hpp" +#include "creatures/combat/combat.hpp" +#include "creatures/combat/condition.hpp" +#include "creatures/players/player.hpp" +#include "creatures/players/wheel/player_wheel.hpp" +#include "creatures/players/wheel/wheel_definitions.hpp" +#include "enums/account_group_type.hpp" +#include "enums/account_type.hpp" +#include "game/game.hpp" #include "lua/global/lua_variant.hpp" +#include "lua/scripts/lua_environment.hpp" +#include "lua/scripts/luascript.hpp" -#include "enums/account_type.hpp" -#include "enums/account_group_type.hpp" +std::array(WheelSpellBoost_t::TOTAL_COUNT)> wheelOfDestinyRegularBoost = { 0 }; +std::array(WheelSpellBoost_t::TOTAL_COUNT)> wheelOfDestinyUpgradedBoost = { 0 }; Spells::Spells() = default; Spells::~Spells() = default; @@ -112,6 +118,10 @@ bool Spells::hasInstantSpell(const std::string &word) const { return false; } +void Spells::setInstantSpell(const std::string &word, const std::shared_ptr &instant) { + instants.try_emplace(word, instant); +} + bool Spells::registerInstantLuaEvent(const std::shared_ptr &instant) { if (instant) { // If the spell not have the "spell:words()" return a error message @@ -173,6 +183,14 @@ std::list Spells::getSpellsByVocation(uint16_t vocationId) { return spellsList; } +const std::map> &Spells::getInstantSpells() const { + return instants; +} + +Spells &Spells::getInstance() { + return inject(); +} + std::shared_ptr Spells::getSpellByName(const std::string &name) { std::shared_ptr spell = getRuneSpellByName(name); if (!spell) { @@ -272,6 +290,14 @@ bool CombatSpell::loadScriptCombat() { return m_combat != nullptr; } +std::shared_ptr CombatSpell::getCombat() const { + return m_combat; +} + +std::string CombatSpell::getScriptTypeName() const { + return "onCastSpell"; +} + bool CombatSpell::castSpell(const std::shared_ptr &creature) { if (isLoadedCallback()) { LuaVariant var; @@ -631,6 +657,22 @@ void Spell::setWheelOfDestinyBoost(WheelSpellBoost_t boost, WheelSpellGrade_t gr } } +const std::string &Spell::getWords() const { + return m_words; +} + +void Spell::setWords(std::string_view newWord) { + m_words = newWord.data(); +} + +const std::string &Spell::getSeparator() const { + return m_separator; +} + +void Spell::setSeparator(std::string_view newSeparator) { + m_separator = newSeparator.data(); +} + void Spell::getCombatDataAugment(const std::shared_ptr &player, CombatDamage &damage) const { if (!(damage.instantSpellName).empty()) { const auto equippedAugmentItems = player->getEquippedAugmentItems(); @@ -715,6 +757,22 @@ void Spell::applyCooldownConditions(const std::shared_ptr &player) const } } +const std::string &Spell::getName() const { + return name; +} + +void Spell::setName(std::string n) { + name = std::move(n); +} + +uint16_t Spell::getSpellId() const { + return m_spellId; +} + +void Spell::setSpellId(uint16_t id) { + m_spellId = id; +} + void Spell::postCastSpell(const std::shared_ptr &player, bool finishedCast /*= true*/, bool payCost /*= true*/) const { if (finishedCast) { if (!player->hasFlag(PlayerFlags_t::HasNoExhaustion)) { @@ -749,6 +807,10 @@ void Spell::postCastSpell(const std::shared_ptr &player, uint32_t manaCo } } +bool Spell::isLearnable() const { + return learnable; +} + uint32_t Spell::getManaCost(const std::shared_ptr &player) const { WheelSpellGrade_t spellGrade = player->wheel()->getSpellUpgrade(getName()); uint32_t manaRedution = 0; @@ -776,6 +838,196 @@ uint32_t Spell::getManaCost(const std::shared_ptr &player) const { return 0; } +uint32_t Spell::getSoulCost() const { + return soul; +} + +void Spell::setSoulCost(uint32_t s) { + soul = s; +} + +uint32_t Spell::getLevel() const { + return level; +} + +void Spell::setLevel(uint32_t lvl) { + level = lvl; +} + +uint32_t Spell::getMagicLevel() const { + return magLevel; +} + +void Spell::setMagicLevel(uint32_t lvl) { + magLevel = lvl; +} + +uint32_t Spell::getMana() const { + return mana; +} + +void Spell::setMana(uint32_t m) { + mana = m; +} + +uint32_t Spell::getManaPercent() const { + return manaPercent; +} + +void Spell::setManaPercent(uint32_t m) { + manaPercent = m; +} + +bool Spell::isPremium() const { + return premium; +} + +void Spell::setPremium(bool p) { + premium = p; +} + +bool Spell::isEnabled() const { + return enabled; +} + +void Spell::setEnabled(bool e) { + enabled = e; +} + +const VocSpellMap &Spell::getVocMap() const { + return vocSpellMap; +} + +void Spell::addVocMap(uint16_t vocationId, bool b) { + if (vocationId == 0XFFFF) { + g_logger().error("Vocation overflow for spell: {}", getName()); + return; + } + + g_logger().trace("Adding spell: {} to voc id: {}", getName(), vocationId); + vocSpellMap[vocationId] = b; +} + +SpellGroup_t Spell::getGroup() { + return group; +} + +void Spell::setGroup(SpellGroup_t g) { + group = g; +} + +SpellGroup_t Spell::getSecondaryGroup() { + return secondaryGroup; +} + +void Spell::setSecondaryGroup(SpellGroup_t g) { + secondaryGroup = g; +} + +uint32_t Spell::getCooldown() const { + return cooldown; +} + +void Spell::setCooldown(uint32_t cd) { + cooldown = cd; +} + +uint32_t Spell::getSecondaryCooldown() const { + return secondaryGroupCooldown; +} + +void Spell::setSecondaryCooldown(uint32_t cd) { + secondaryGroupCooldown = cd; +} + +uint32_t Spell::getGroupCooldown() const { + return groupCooldown; +} + +void Spell::setGroupCooldown(uint32_t cd) { + groupCooldown = cd; +} + +int32_t Spell::getRange() const { + return range; +} + +void Spell::setRange(int32_t r) { + range = r; +} + +bool Spell::getNeedTarget() const { + return needTarget; +} + +void Spell::setNeedTarget(bool n) { + needTarget = n; +} + +bool Spell::getNeedWeapon() const { + return needWeapon; +} + +void Spell::setNeedWeapon(bool n) { + needWeapon = n; +} + +bool Spell::getNeedLearn() const { + return learnable; +} + +void Spell::setNeedLearn(bool n) { + learnable = n; +} + +bool Spell::getSelfTarget() const { + return selfTarget; +} + +void Spell::setSelfTarget(bool s) { + selfTarget = s; +} + +bool Spell::getBlockingSolid() const { + return blockingSolid; +} + +void Spell::setBlockingSolid(bool b) { + blockingSolid = b; +} + +bool Spell::getBlockingCreature() const { + return blockingCreature; +} + +void Spell::setBlockingCreature(bool b) { + blockingCreature = b; +} + +bool Spell::getAggressive() const { + return aggressive; +} + +void Spell::setAggressive(bool a) { + aggressive = a; +} + +bool Spell::getAllowOnSelf() const { + return allowOnSelf; +} + +void Spell::setAllowOnSelf(bool s) { + allowOnSelf = s; +} + +bool Spell::getLockedPZ() const { + return pzLocked; +} + +void Spell::setLockedPZ(bool b) { + pzLocked = b; +} + bool InstantSpell::playerCastInstant(const std::shared_ptr &player, std::string ¶m) const { if (!playerSpellCheck(player)) { return false; @@ -907,6 +1159,10 @@ bool InstantSpell::canThrowSpell(const std::shared_ptr &creature, cons return true; } +std::string InstantSpell::getScriptTypeName() const { + return "onCastSpell"; +} + bool InstantSpell::castSpell(const std::shared_ptr &creature) { LuaVariant var; var.instantName = getName(); @@ -970,6 +1226,50 @@ bool InstantSpell::executeCastSpell(const std::shared_ptr &creature, c return getScriptInterface()->callFunction(2); } +bool InstantSpell::isInstant() const { + return true; +} + +bool InstantSpell::getHasParam() const { + return hasParam; +} + +void InstantSpell::setHasParam(bool p) { + hasParam = p; +} + +bool InstantSpell::getHasPlayerNameParam() const { + return hasPlayerNameParam; +} + +void InstantSpell::setHasPlayerNameParam(bool p) { + hasPlayerNameParam = p; +} + +bool InstantSpell::getNeedDirection() const { + return needDirection; +} + +void InstantSpell::setNeedDirection(bool n) { + needDirection = n; +} + +bool InstantSpell::getNeedCasterTargetOrDirection() const { + return casterTargetOrDirection; +} + +void InstantSpell::setNeedCasterTargetOrDirection(bool d) { + casterTargetOrDirection = d; +} + +bool InstantSpell::getBlockWalls() const { + return checkLineOfSight; +} + +void InstantSpell::setBlockWalls(bool w) { + checkLineOfSight = w; +} + bool InstantSpell::canCast(const std::shared_ptr &player) const { if (player->hasFlag(PlayerFlags_t::CannotUseSpells)) { return false; @@ -1013,6 +1313,14 @@ ReturnValue RuneSpell::canExecuteAction(const std::shared_ptr &player, c return RETURNVALUE_NOERROR; } +bool RuneSpell::hasOwnErrorHandler() { + return true; +} + +std::shared_ptr RuneSpell::getTarget(const std::shared_ptr &, const std::shared_ptr &targetCreature, const Position &, uint8_t) const { + return targetCreature; +} + bool RuneSpell::executeUse(const std::shared_ptr &player, const std::shared_ptr &item, const Position &, const std::shared_ptr &target, const Position &toPosition, bool isHotkey) { if (!playerRuneSpellCheck(player, toPosition)) { return false; @@ -1081,6 +1389,10 @@ bool RuneSpell::castSpell(const std::shared_ptr &creature, const std:: return internalCastSpell(creature, var, false); } +std::string RuneSpell::getScriptTypeName() const { + return "onCastSpell"; +} + bool RuneSpell::internalCastSpell(const std::shared_ptr &creature, const LuaVariant &var, bool isHotkey) const { bool result; if (isLoadedCallback()) { @@ -1116,3 +1428,26 @@ bool RuneSpell::executeCastSpell(const std::shared_ptr &creature, cons return getScriptInterface()->callFunction(3); } + +bool RuneSpell::isInstant() const { + return false; +} + +uint16_t RuneSpell::getRuneItemId() const { + return runeId; +} + +void RuneSpell::setRuneItemId(uint16_t i) { + runeId = i; +} + +uint32_t RuneSpell::getCharges() const { + return charges; +} + +void RuneSpell::setCharges(uint32_t c) { + if (c > 0) { + hasCharges = true; + } + charges = c; +} diff --git a/src/creatures/combat/spells.hpp b/src/creatures/combat/spells.hpp index 4e929b35e89..c12c3af3686 100644 --- a/src/creatures/combat/spells.hpp +++ b/src/creatures/combat/spells.hpp @@ -9,13 +9,12 @@ #pragma once -#include "lua/scripts/luascript.hpp" -#include "creatures/players/player.hpp" -#include "creatures/players/wheel/wheel_definitions.hpp" #include "lua/creature/actions.hpp" -#include "lua/creature/talkaction.hpp" #include "lua/scripts/scripts.hpp" +enum class WheelSpellBoost_t : uint8_t; +enum class WheelSpellGrade_t : uint8_t; + class InstantSpell; class RuneSpell; class Spell; @@ -33,9 +32,7 @@ class Spells final : public Scripts { Spells(const Spells &) = delete; Spells &operator=(const Spells &) = delete; - static Spells &getInstance() { - return inject(); - } + static Spells &getInstance(); std::shared_ptr getSpellByName(const std::string &name); std::shared_ptr getRuneSpell(uint16_t id); @@ -52,15 +49,12 @@ class Spells final : public Scripts { std::list getSpellsByVocation(uint16_t vocationId); - [[nodiscard]] const std::map> &getInstantSpells() const { - return instants; - }; + [[nodiscard]] const std::map> &getInstantSpells() const; + ; [[nodiscard]] bool hasInstantSpell(const std::string &word) const; - void setInstantSpell(const std::string &word, const std::shared_ptr &instant) { - instants.try_emplace(word, instant); - } + void setInstantSpell(const std::string &word, const std::shared_ptr &instant); void clear(); bool registerInstantLuaEvent(const std::shared_ptr &instant); @@ -105,14 +99,10 @@ class CombatSpell final : public Script, public BaseSpell, public std::enable_sh bool executeCastSpell(const std::shared_ptr &creature, const LuaVariant &var) const; bool loadScriptCombat(); - std::shared_ptr getCombat() const { - return m_combat; - } + std::shared_ptr getCombat() const; private: - std::string getScriptTypeName() const override { - return "onCastSpell"; - } + std::string getScriptTypeName() const override; std::shared_ptr m_combat; @@ -124,176 +114,68 @@ class Spell : public BaseSpell { public: Spell() = default; - [[nodiscard]] const std::string &getName() const { - return name; - } - void setName(std::string n) { - name = std::move(n); - } - [[nodiscard]] uint16_t getSpellId() const { - return m_spellId; - } - void setSpellId(uint16_t id) { - m_spellId = id; - } + [[nodiscard]] const std::string &getName() const; + void setName(std::string n); + [[nodiscard]] uint16_t getSpellId() const; + void setSpellId(uint16_t id); void postCastSpell(const std::shared_ptr &player, bool finishedCast = true, bool payCost = true) const; static void postCastSpell(const std::shared_ptr &player, uint32_t manaCost, uint32_t soulCost); [[nodiscard]] virtual bool isInstant() const = 0; - [[nodiscard]] bool isLearnable() const { - return learnable; - } + [[nodiscard]] bool isLearnable() const; uint32_t getManaCost(const std::shared_ptr &player) const; - [[nodiscard]] uint32_t getSoulCost() const { - return soul; - } - void setSoulCost(uint32_t s) { - soul = s; - } - [[nodiscard]] uint32_t getLevel() const { - return level; - } - void setLevel(uint32_t lvl) { - level = lvl; - } - [[nodiscard]] uint32_t getMagicLevel() const { - return magLevel; - } - void setMagicLevel(uint32_t lvl) { - magLevel = lvl; - } - [[nodiscard]] uint32_t getMana() const { - return mana; - } - void setMana(uint32_t m) { - mana = m; - } - [[nodiscard]] uint32_t getManaPercent() const { - return manaPercent; - } - void setManaPercent(uint32_t m) { - manaPercent = m; - } - [[nodiscard]] bool isPremium() const { - return premium; - } - void setPremium(bool p) { - premium = p; - } - [[nodiscard]] bool isEnabled() const { - return enabled; - } - void setEnabled(bool e) { - enabled = e; - } - - [[nodiscard]] const VocSpellMap &getVocMap() const { - return vocSpellMap; - } - void addVocMap(uint16_t vocationId, bool b) { - if (vocationId == 0XFFFF) { - g_logger().error("Vocation overflow for spell: {}", getName()); - return; - } - - g_logger().trace("Adding spell: {} to voc id: {}", getName(), vocationId); - vocSpellMap[vocationId] = b; - } - - SpellGroup_t getGroup() { - return group; - } - void setGroup(SpellGroup_t g) { - group = g; - } - SpellGroup_t getSecondaryGroup() { - return secondaryGroup; - } - void setSecondaryGroup(SpellGroup_t g) { - secondaryGroup = g; - } - - [[nodiscard]] uint32_t getCooldown() const { - return cooldown; - } - void setCooldown(uint32_t cd) { - cooldown = cd; - } - [[nodiscard]] uint32_t getSecondaryCooldown() const { - return secondaryGroupCooldown; - } - void setSecondaryCooldown(uint32_t cd) { - secondaryGroupCooldown = cd; - } - [[nodiscard]] uint32_t getGroupCooldown() const { - return groupCooldown; - } - void setGroupCooldown(uint32_t cd) { - groupCooldown = cd; - } - - [[nodiscard]] int32_t getRange() const { - return range; - } - void setRange(int32_t r) { - range = r; - } - - [[nodiscard]] bool getNeedTarget() const { - return needTarget; - } - void setNeedTarget(bool n) { - needTarget = n; - } - [[nodiscard]] bool getNeedWeapon() const { - return needWeapon; - } - void setNeedWeapon(bool n) { - needWeapon = n; - } - [[nodiscard]] bool getNeedLearn() const { - return learnable; - } - void setNeedLearn(bool n) { - learnable = n; - } - [[nodiscard]] bool getSelfTarget() const { - return selfTarget; - } - void setSelfTarget(bool s) { - selfTarget = s; - } - [[nodiscard]] bool getBlockingSolid() const { - return blockingSolid; - } - void setBlockingSolid(bool b) { - blockingSolid = b; - } - [[nodiscard]] bool getBlockingCreature() const { - return blockingCreature; - } - void setBlockingCreature(bool b) { - blockingCreature = b; - } - [[nodiscard]] bool getAggressive() const { - return aggressive; - } - void setAggressive(bool a) { - aggressive = a; - } - [[nodiscard]] bool getAllowOnSelf() const { - return allowOnSelf; - } - void setAllowOnSelf(bool s) { - allowOnSelf = s; - } - [[nodiscard]] bool getLockedPZ() const { - return pzLocked; - } - void setLockedPZ(bool b) { - pzLocked = b; - } + [[nodiscard]] uint32_t getSoulCost() const; + void setSoulCost(uint32_t s); + [[nodiscard]] uint32_t getLevel() const; + void setLevel(uint32_t lvl); + [[nodiscard]] uint32_t getMagicLevel() const; + void setMagicLevel(uint32_t lvl); + [[nodiscard]] uint32_t getMana() const; + void setMana(uint32_t m); + [[nodiscard]] uint32_t getManaPercent() const; + void setManaPercent(uint32_t m); + [[nodiscard]] bool isPremium() const; + void setPremium(bool p); + [[nodiscard]] bool isEnabled() const; + void setEnabled(bool e); + + [[nodiscard]] const VocSpellMap &getVocMap() const; + void addVocMap(uint16_t vocationId, bool b); + + SpellGroup_t getGroup(); + void setGroup(SpellGroup_t g); + SpellGroup_t getSecondaryGroup(); + void setSecondaryGroup(SpellGroup_t g); + + [[nodiscard]] uint32_t getCooldown() const; + void setCooldown(uint32_t cd); + [[nodiscard]] uint32_t getSecondaryCooldown() const; + void setSecondaryCooldown(uint32_t cd); + [[nodiscard]] uint32_t getGroupCooldown() const; + void setGroupCooldown(uint32_t cd); + + [[nodiscard]] int32_t getRange() const; + void setRange(int32_t r); + + [[nodiscard]] bool getNeedTarget() const; + void setNeedTarget(bool n); + [[nodiscard]] bool getNeedWeapon() const; + void setNeedWeapon(bool n); + [[nodiscard]] bool getNeedLearn() const; + void setNeedLearn(bool n); + [[nodiscard]] bool getSelfTarget() const; + void setSelfTarget(bool s); + [[nodiscard]] bool getBlockingSolid() const; + void setBlockingSolid(bool b); + [[nodiscard]] bool getBlockingCreature() const; + void setBlockingCreature(bool b); + [[nodiscard]] bool getAggressive() const; + void setAggressive(bool a); + [[nodiscard]] bool getAllowOnSelf() const; + void setAllowOnSelf(bool s); + [[nodiscard]] bool getLockedPZ() const; + void setLockedPZ(bool b); /** * @brief Get whether the wheel of destiny is upgraded. @@ -327,23 +209,13 @@ class Spell : public BaseSpell { */ void setWheelOfDestinyBoost(WheelSpellBoost_t boost, WheelSpellGrade_t grade, int32_t value); - SpellType_t spellType = SPELL_UNDEFINED; - - [[nodiscard]] const std::string &getWords() const { - return m_words; - } + [[nodiscard]] const std::string &getWords() const; - void setWords(std::string_view newWord) { - m_words = newWord.data(); - } + void setWords(std::string_view newWord); - [[nodiscard]] const std::string &getSeparator() const { - return m_separator; - } + [[nodiscard]] const std::string &getSeparator() const; - void setSeparator(std::string_view newSeparator) { - m_separator = newSeparator.data(); - } + void setSeparator(std::string_view newSeparator); void getCombatDataAugment(const std::shared_ptr &player, CombatDamage &damage) const; int32_t calculateAugmentSpellCooldownReduction(const std::shared_ptr &player) const; @@ -358,6 +230,7 @@ class Spell : public BaseSpell { SpellGroup_t group = SPELLGROUP_NONE; SpellGroup_t secondaryGroup = SPELLGROUP_NONE; + SpellType_t spellType = SPELL_UNDEFINED; uint32_t cooldown = 1000; uint32_t groupCooldown = 1000; @@ -374,8 +247,6 @@ class Spell : public BaseSpell { bool pzLocked = false; bool whellOfDestinyUpgraded = false; - std::array(WheelSpellBoost_t::TOTAL_COUNT)> wheelOfDestinyRegularBoost = { 0 }; - std::array(WheelSpellBoost_t::TOTAL_COUNT)> wheelOfDestinyUpgradedBoost = { 0 }; private: uint32_t mana = 0; @@ -393,6 +264,8 @@ class Spell : public BaseSpell { std::string name; std::string m_words; std::string m_separator; + + friend class SpellFunctions; }; class InstantSpell final : public Script, public Spell { @@ -407,46 +280,22 @@ class InstantSpell final : public Script, public Spell { // Scripting spell bool executeCastSpell(const std::shared_ptr &creature, const LuaVariant &var) const; - [[nodiscard]] bool isInstant() const override { - return true; - } - [[nodiscard]] bool getHasParam() const { - return hasParam; - } - void setHasParam(bool p) { - hasParam = p; - } - [[nodiscard]] bool getHasPlayerNameParam() const { - return hasPlayerNameParam; - } - void setHasPlayerNameParam(bool p) { - hasPlayerNameParam = p; - } - [[nodiscard]] bool getNeedDirection() const { - return needDirection; - } - void setNeedDirection(bool n) { - needDirection = n; - } - [[nodiscard]] bool getNeedCasterTargetOrDirection() const { - return casterTargetOrDirection; - } - void setNeedCasterTargetOrDirection(bool d) { - casterTargetOrDirection = d; - } - [[nodiscard]] bool getBlockWalls() const { - return checkLineOfSight; - } - void setBlockWalls(bool w) { - checkLineOfSight = w; - } + [[nodiscard]] bool isInstant() const override; + [[nodiscard]] bool getHasParam() const; + void setHasParam(bool p); + [[nodiscard]] bool getHasPlayerNameParam() const; + void setHasPlayerNameParam(bool p); + [[nodiscard]] bool getNeedDirection() const; + void setNeedDirection(bool n); + [[nodiscard]] bool getNeedCasterTargetOrDirection() const; + void setNeedCasterTargetOrDirection(bool d); + [[nodiscard]] bool getBlockWalls() const; + void setBlockWalls(bool w); bool canCast(const std::shared_ptr &player) const; bool canThrowSpell(const std::shared_ptr &creature, const std::shared_ptr &target) const; private: - [[nodiscard]] std::string getScriptTypeName() const override { - return "onCastSpell"; - } + [[nodiscard]] std::string getScriptTypeName() const override; bool needDirection = false; bool hasParam = false; @@ -460,12 +309,8 @@ class RuneSpell final : public Action, public Spell { using Action::Action; ReturnValue canExecuteAction(const std::shared_ptr &player, const Position &toPos) override; - bool hasOwnErrorHandler() override { - return true; - } - std::shared_ptr getTarget(const std::shared_ptr &, const std::shared_ptr &targetCreature, const Position &, uint8_t) const override { - return targetCreature; - } + bool hasOwnErrorHandler() override; + std::shared_ptr getTarget(const std::shared_ptr &, const std::shared_ptr &targetCreature, const Position &, uint8_t) const override; bool executeUse(const std::shared_ptr &player, const std::shared_ptr &item, const Position &fromPosition, const std::shared_ptr &target, const Position &toPosition, bool isHotkey) override; @@ -475,29 +320,14 @@ class RuneSpell final : public Action, public Spell { // Scripting spell bool executeCastSpell(const std::shared_ptr &creature, const LuaVariant &var, bool isHotkey) const; - [[nodiscard]] bool isInstant() const override { - return false; - } - [[nodiscard]] uint16_t getRuneItemId() const { - return runeId; - } - void setRuneItemId(uint16_t i) { - runeId = i; - } - [[nodiscard]] uint32_t getCharges() const { - return charges; - } - void setCharges(uint32_t c) { - if (c > 0) { - hasCharges = true; - } - charges = c; - } + [[nodiscard]] bool isInstant() const override; + [[nodiscard]] uint16_t getRuneItemId() const; + void setRuneItemId(uint16_t i); + [[nodiscard]] uint32_t getCharges() const; + void setCharges(uint32_t c); private: - [[nodiscard]] std::string getScriptTypeName() const override { - return "onCastSpell"; - } + [[nodiscard]] std::string getScriptTypeName() const override; bool internalCastSpell(const std::shared_ptr &creature, const LuaVariant &var, bool isHotkey) const; diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index 32b5ec10941..baf93fb651a 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -8,13 +8,20 @@ */ #include "creatures/creature.hpp" -#include "declarations.hpp" -#include "game/scheduling/dispatcher.hpp" -#include "game/game.hpp" + +#include "config/configmanager.hpp" +#include "creatures/combat/condition.hpp" +#include "creatures/combat/combat.hpp" #include "creatures/monsters/monster.hpp" +#include "creatures/players/grouping/party.hpp" +#include "game/game.hpp" +#include "game/scheduling/dispatcher.hpp" #include "game/zones/zone.hpp" -#include "map/spectators.hpp" #include "lib/metrics/metrics.hpp" +#include "lua/creature/creatureevent.hpp" +#include "map/spectators.hpp" +#include "creatures/players/player.hpp" +#include "server/network/protocol/protocolgame.hpp" Creature::Creature() { Creature::onIdleStatus(); @@ -1666,6 +1673,37 @@ bool Creature::unregisterCreatureEvent(const std::string &name) { return true; } +std::shared_ptr Creature::getParent() { + return getTile(); +} + +void Creature::setParent(std::weak_ptr cylinder) { + const auto oldGroundSpeed = walk.groundSpeed; + walk.groundSpeed = 150; + + if (const auto &lockedCylinder = cylinder.lock()) { + const auto &newParent = lockedCylinder->getTile(); + position = newParent->getPosition(); + m_tile = newParent; + + if (newParent->getGround()) { + const auto &it = Item::items[newParent->getGround()->getID()]; + if (it.speed > 0) { + walk.groundSpeed = it.speed; + } + } + } + + if (walk.groundSpeed != oldGroundSpeed) { + walk.recache(); + } +} + +// creature script events +bool Creature::hasEventRegistered(CreatureEventType_t event) const { + return (0 != (scriptEventsBitField & (static_cast(1) << event))); +} + CreatureEventList Creature::getCreatureEvents(CreatureEventType_t type) const { CreatureEventList tmpEventList; @@ -1763,6 +1801,14 @@ bool Creature::isInvisible() const { != conditions.end(); } +ZoneType_t Creature::getZoneType() { + if (getTile()) { + return getTile()->getZoneType(); + } + + return ZONE_NORMAL; +} + bool Creature::getPathTo(const Position &targetPos, std::vector &dirList, const FindPathParams &fpp) { metrics::method_latency measure(__METHOD_NAME__); if (fpp.maxSearchDist != 0 || fpp.keepDistance) { diff --git a/src/creatures/creature.hpp b/src/creatures/creature.hpp index 5a8463e9e30..46f628188e5 100644 --- a/src/creatures/creature.hpp +++ b/src/creatures/creature.hpp @@ -9,19 +9,16 @@ #pragma once -#include "declarations.hpp" -#include "creatures/combat/condition.hpp" -#include "utils/utils_definitions.hpp" -#include "lua/creature/creatureevent.hpp" -#include "map/map.hpp" +#include "creatures/creatures_definitions.hpp" +#include "game/game_definitions.hpp" #include "game/movement/position.hpp" -#include "items/tile.hpp" - -using ConditionList = std::list>; -using CreatureEventList = std::list>; +#include "items/thing.hpp" +#include "map/map_const.hpp" +#include "utils/utils_definitions.hpp" +class CreatureEvent; +class Condition; class Map; -class Thing; class Container; class Player; class Monster; @@ -29,6 +26,19 @@ class Npc; class Item; class Tile; class Zone; +class MonsterType; +class Cylinder; +class ItemType; + +struct CreatureIcon; +struct Position; + +enum CreatureType_t : uint8_t; +enum ZoneType_t : uint8_t; +enum CreatureEventType_t : uint8_t; + +using ConditionList = std::list>; +using CreatureEventList = std::list>; static constexpr uint8_t WALK_TARGET_NEARBY_EXTRA_COST = 2; static constexpr uint8_t WALK_FLOOR_CHANGE_EXTRA_COST = 2; @@ -295,13 +305,7 @@ class Creature : virtual public Thing, public SharedObject { return outfit == 75 || outfit == 266 || outfit == 302; } bool isInvisible() const; - ZoneType_t getZoneType() { - if (getTile()) { - return getTile()->getZoneType(); - } - - return ZONE_NORMAL; - } + ZoneType_t getZoneType(); std::unordered_set> getZones(); @@ -546,31 +550,9 @@ class Creature : virtual public Thing, public SharedObject { bool registerCreatureEvent(const std::string &name); bool unregisterCreatureEvent(const std::string &name); - std::shared_ptr getParent() override final { - return getTile(); - } - - void setParent(std::weak_ptr cylinder) override final { - const auto oldGroundSpeed = walk.groundSpeed; - walk.groundSpeed = 150; + std::shared_ptr getParent() final; - if (const auto &lockedCylinder = cylinder.lock()) { - const auto &newParent = lockedCylinder->getTile(); - position = newParent->getPosition(); - m_tile = newParent; - - if (newParent->getGround()) { - const auto &it = Item::items[newParent->getGround()->getID()]; - if (it.speed > 0) { - walk.groundSpeed = it.speed; - } - } - } - - if (walk.groundSpeed != oldGroundSpeed) { - walk.recache(); - } - } + void setParent(std::weak_ptr cylinder) final; const Position &getPosition() override final { return position; @@ -818,9 +800,7 @@ class Creature : virtual public Thing, public SharedObject { std::map creatureIcons = {}; // creature script events - bool hasEventRegistered(CreatureEventType_t event) const { - return (0 != (scriptEventsBitField & (static_cast(1) << event))); - } + bool hasEventRegistered(CreatureEventType_t event) const; CreatureEventList getCreatureEvents(CreatureEventType_t type) const; void updateMapCache(); diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp index 7ca12916946..f7ba1e203b1 100644 --- a/src/creatures/creatures_definitions.hpp +++ b/src/creatures/creatures_definitions.hpp @@ -1584,8 +1584,6 @@ struct RespawnType { bool underground; }; -struct LootBlock; - struct LootBlock { uint16_t id; uint32_t countmax; diff --git a/src/creatures/interactions/chat.cpp b/src/creatures/interactions/chat.cpp index f81eaa96553..326b2555128 100644 --- a/src/creatures/interactions/chat.cpp +++ b/src/creatures/interactions/chat.cpp @@ -8,9 +8,24 @@ */ #include "creatures/interactions/chat.hpp" + +#include "config/configmanager.hpp" +#include "creatures/players/player.hpp" #include "game/game.hpp" -#include "utils/pugicast.hpp" #include "game/scheduling/dispatcher.hpp" +#include "lib/di/container.hpp" +#include "utils/pugicast.hpp" + +PrivateChatChannel::PrivateChatChannel(uint16_t channelId, std::string channelName) : + ChatChannel(channelId, std::move(channelName)) { } + +uint32_t PrivateChatChannel::getOwner() const { + return owner; +} + +void PrivateChatChannel::setOwner(uint32_t newOwner) { + this->owner = newOwner; +} bool PrivateChatChannel::isInvited(uint32_t guid) const { if (guid == getOwner()) { @@ -78,6 +93,14 @@ void PrivateChatChannel::closeChannel() const { } } +const InvitedMap* PrivateChatChannel::getInvitedUsers() const { + return &invites; +} + +ChatChannel::ChatChannel(uint16_t channelId, std::string channelName) : + name(std::move(channelName)), + id(channelId) { } + bool ChatChannel::addUser(const std::shared_ptr &player) { if (users.contains(player->getID())) { return false; @@ -147,6 +170,30 @@ void ChatChannel::sendToAll(const std::string &message, SpeakClasses type) const } } +const std::string &ChatChannel::getName() const { + return name; +} + +uint16_t ChatChannel::getId() const { + return id; +} + +const UsersMap &ChatChannel::getUsers() const { + return users; +} + +const InvitedMap* ChatChannel::getInvitedUsers() const { + return nullptr; +} + +uint32_t ChatChannel::getOwner() const { + return 0; +} + +bool ChatChannel::isPublicChannel() const { + return publicChannel; +} + bool ChatChannel::talk(const std::shared_ptr &fromPlayer, SpeakClasses type, const std::string &text) const { if (!users.contains(fromPlayer->getID())) { return false; @@ -294,6 +341,10 @@ Chat::Chat() : scriptInterface.initState(); } +Chat &Chat::getInstance() { + return inject(); +} + bool Chat::load() { pugi::xml_document doc; auto coreFolder = g_configManager().getString(CORE_DIRECTORY); @@ -671,3 +722,7 @@ std::shared_ptr Chat::getPrivateChannel(const std::shared_pt } return nullptr; } + +LuaScriptInterface* Chat::getScriptInterface() { + return &scriptInterface; +} diff --git a/src/creatures/interactions/chat.hpp b/src/creatures/interactions/chat.hpp index 7e2b471f4be..9dd347aa5b4 100644 --- a/src/creatures/interactions/chat.hpp +++ b/src/creatures/interactions/chat.hpp @@ -9,10 +9,10 @@ #pragma once -#include "utils/utils_definitions.hpp" -#include "lib/di/container.hpp" #include "lua/scripts/luascript.hpp" +enum SpeakClasses : uint8_t; + class Party; class Player; @@ -22,9 +22,7 @@ using InvitedMap = std::map>; class ChatChannel { public: ChatChannel() = default; - ChatChannel(uint16_t channelId, std::string channelName) : - name(std::move(channelName)), - id(channelId) { } + ChatChannel(uint16_t channelId, std::string channelName); virtual ~ChatChannel() = default; @@ -35,26 +33,14 @@ class ChatChannel { bool talk(const std::shared_ptr &fromPlayer, SpeakClasses type, const std::string &text) const; void sendToAll(const std::string &message, SpeakClasses type) const; - const std::string &getName() const { - return name; - } - uint16_t getId() const { - return id; - } - const UsersMap &getUsers() const { - return users; - } - virtual const InvitedMap* getInvitedUsers() const { - return nullptr; - } - - virtual uint32_t getOwner() const { - return 0; - } - - bool isPublicChannel() const { - return publicChannel; - } + const std::string &getName() const; + uint16_t getId() const; + const UsersMap &getUsers() const; + virtual const InvitedMap* getInvitedUsers() const; + + virtual uint32_t getOwner() const; + + bool isPublicChannel() const; bool executeOnJoinEvent(const std::shared_ptr &player) const; bool executeCanJoinEvent(const std::shared_ptr &player) const; @@ -79,15 +65,10 @@ class ChatChannel { class PrivateChatChannel final : public ChatChannel { public: - PrivateChatChannel(uint16_t channelId, [[maybe_unused]] std::string channelName) : - ChatChannel(channelId, std::move(channelName)) { } + PrivateChatChannel(uint16_t channelId, [[maybe_unused]] std::string channelName); - uint32_t getOwner() const override { - return owner; - } - void setOwner(uint32_t newOwner) { - this->owner = newOwner; - } + uint32_t getOwner() const override; + void setOwner(uint32_t newOwner); bool isInvited(uint32_t guid) const; @@ -98,9 +79,7 @@ class PrivateChatChannel final : public ChatChannel { void closeChannel() const; - [[nodiscard]] const InvitedMap* getInvitedUsers() const override { - return &invites; - } + [[nodiscard]] const InvitedMap* getInvitedUsers() const override; private: InvitedMap invites; @@ -117,9 +96,7 @@ class Chat { Chat(const Chat &) = delete; Chat &operator=(const Chat &) = delete; - static Chat &getInstance() { - return inject(); - } + static Chat &getInstance(); bool load(); @@ -139,9 +116,7 @@ class Chat { std::shared_ptr getGuildChannelById(uint32_t guildId); std::shared_ptr getPrivateChannel(const std::shared_ptr &player) const; - LuaScriptInterface* getScriptInterface() { - return &scriptInterface; - } + LuaScriptInterface* getScriptInterface(); private: std::map> normalChannels; diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index 92c10a9f534..2e9140dcc18 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -8,10 +8,15 @@ */ #include "creatures/monsters/monster.hpp" + +#include "config/configmanager.hpp" #include "creatures/combat/spells.hpp" +#include "creatures/monsters/monsters.hpp" +#include "creatures/players/player.hpp" #include "creatures/players/wheel/player_wheel.hpp" #include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" +#include "items/tile.hpp" #include "lua/callbacks/event_callback.hpp" #include "lua/callbacks/events_callbacks.hpp" #include "map/spectators.hpp" @@ -53,6 +58,20 @@ Monster::Monster(const std::shared_ptr &mType) : } } +std::shared_ptr Monster::getMonster() { + return static_self_cast(); +} + +std::shared_ptr Monster::getMonster() const { + return static_self_cast(); +} + +void Monster::setID() { + if (id == 0) { + id = monsterAutoID++; + } +} + void Monster::addList() { g_game().addMonster(static_self_cast()); } @@ -85,6 +104,12 @@ void Monster::setName(const std::string &name) { } } +// Real monster name, set on monster creation "createMonsterType(typeName)" + +const std::string &Monster::getTypeName() const { + return mType->typeName; +} + const std::string &Monster::getNameDescription() const { if (nameDescription.empty()) { return mType->nameDescription; @@ -92,6 +117,26 @@ const std::string &Monster::getNameDescription() const { return nameDescription; } +void Monster::setNameDescription(std::string_view newNameDescription) { + this->nameDescription = newNameDescription; +} + +std::string Monster::getDescription(int32_t) { + return nameDescription + '.'; +} + +CreatureType_t Monster::getType() const { + return CREATURETYPE_MONSTER; +} + +const Position &Monster::getMasterPos() const { + return masterPos; +} + +void Monster::setMasterPos(Position pos) { + masterPos = pos; +} + bool Monster::canWalkOnFieldType(CombatType_t combatType) const { switch (combatType) { case COMBAT_ENERGYDAMAGE: @@ -156,6 +201,69 @@ void Monster::addDefense(int32_t defense) { g_logger().trace("[{}] new defense {}", __FUNCTION__, m_defense); } +Faction_t Monster::getFaction() const { + if (const auto &master = getMaster()) { + return master->getFaction(); + } + return mType->info.faction; +} + +bool Monster::isEnemyFaction(Faction_t faction) const { + const auto &master = getMaster(); + if (master && master->getMonster()) { + return master->getMonster()->isEnemyFaction(faction); + } + return mType->info.enemyFactions.empty() ? false : mType->info.enemyFactions.contains(faction); +} + +bool Monster::isPushable() { + return mType->info.pushable && baseSpeed != 0; +} + +bool Monster::isAttackable() const { + return mType->info.isAttackable; +} + +bool Monster::canPushItems() const { + return mType->info.canPushItems; +} + +bool Monster::canPushCreatures() const { + return mType->info.canPushCreatures; +} + +bool Monster::isRewardBoss() const { + return mType->info.isRewardBoss; +} + +bool Monster::isHostile() const { + return mType->info.isHostile; +} + +bool Monster::isFamiliar() const { + return mType->info.isFamiliar; +} + +bool Monster::canSeeInvisibility() const { + return isImmune(CONDITION_INVISIBLE); +} + +uint16_t Monster::critChance() const { + return mType->info.critChance; +} + +uint32_t Monster::getManaCost() const { + return mType->info.manaCost; +} + +RespawnType Monster::getRespawnType() const { + return mType->info.respawnType; +} + +void Monster::setSpawnMonster(const std::shared_ptr &newSpawnMonster) { + this->spawnMonster = newSpawnMonster; +} + uint32_t Monster::getHealingCombatValue(CombatType_t healingType) const { auto it = mType->info.healingMap.find(healingType); if (it != mType->info.healingMap.end()) { @@ -465,7 +573,8 @@ bool Monster::addTarget(const std::shared_ptr &creature, bool pushFron targetList.emplace_back(creature); } - if (!getMaster() && getFaction() != FACTION_DEFAULT && creature->getPlayer()) { + const auto &master = getMaster(); + if (!master && getFaction() != FACTION_DEFAULT && creature->getPlayer()) { totalPlayersOnScreen++; } @@ -482,7 +591,8 @@ bool Monster::removeTarget(const std::shared_ptr &creature) { return false; } - if (!getMaster() && getFaction() != FACTION_DEFAULT && creature->getPlayer()) { + const auto &master = getMaster(); + if (!master && getFaction() != FACTION_DEFAULT && creature->getPlayer()) { totalPlayersOnScreen--; } @@ -539,10 +649,10 @@ void Monster::onCreatureEnter(const std::shared_ptr &creature) { } bool Monster::isFriend(const std::shared_ptr &creature) const { - if (isSummon() && getMaster()->getPlayer()) { - const auto &masterPlayer = getMaster()->getPlayer(); + const auto &master = getMaster(); + const auto &masterPlayer = master ? master->getPlayer() : nullptr; + if (isSummon() && masterPlayer) { auto tmpPlayer = creature->getPlayer(); - if (!tmpPlayer) { const auto &creatureMaster = creature->getMaster(); if (creatureMaster && creatureMaster->getPlayer()) { @@ -550,7 +660,7 @@ bool Monster::isFriend(const std::shared_ptr &creature) const { } } - if (tmpPlayer && (tmpPlayer == getMaster() || masterPlayer->isPartner(tmpPlayer))) { + if (tmpPlayer && (tmpPlayer == master || masterPlayer->isPartner(tmpPlayer))) { return true; } } @@ -563,8 +673,10 @@ bool Monster::isOpponent(const std::shared_ptr &creature) const { return false; } - if (isSummon() && getMaster()->getPlayer()) { - return creature != getMaster(); + const auto &master = getMaster(); + const auto &masterPlayer = master ? master->getPlayer() : nullptr; + if (isSummon() && masterPlayer) { + return creature != master; } if (creature->getPlayer() && creature->getPlayer()->hasFlag(PlayerFlags_t::IgnoredByMonsters)) { @@ -575,13 +687,23 @@ bool Monster::isOpponent(const std::shared_ptr &creature) const { return isEnemyFaction(creature->getFaction()) || creature->getFaction() == FACTION_PLAYER; } - if ((creature->getPlayer()) || (creature->getMaster() && creature->getMaster()->getPlayer())) { + const auto &creatureMaster = creature->getMaster(); + const auto &creaturePlayer = creatureMaster ? creatureMaster->getPlayer() : nullptr; + if (creature->getPlayer() || creaturePlayer) { return true; } return false; } +uint64_t Monster::getLostExperience() const { + return skillLoss ? mType->info.experience : 0; +} + +uint16_t Monster::getLookCorpse() const { + return mType->info.lookcorpse; +} + void Monster::onCreatureLeave(const std::shared_ptr &creature) { // update friendList if (isFriend(creature)) { @@ -747,6 +869,10 @@ void Monster::onFollowCreatureComplete(const std::shared_ptr &creature } } +RaceType_t Monster::getRace() const { + return mType->info.race; +} + float Monster::getMitigation() const { float mitigation = mType->info.mitigation * getDefenseMultiplier(); if (g_configManager().getBoolean(DISABLE_MONSTER_ARMOR)) { @@ -755,6 +881,10 @@ float Monster::getMitigation() const { return std::min(mitigation, 30.f); } +int32_t Monster::getArmor() const { + return mType->info.armor * getDefenseMultiplier(); +} + BlockType_t Monster::blockHit(const std::shared_ptr &attacker, const CombatType_t &combatType, int32_t &damage, bool checkDefense /* = false*/, bool checkArmor /* = false*/, bool /* field = false */) { BlockType_t blockType = Creature::blockHit(attacker, combatType, damage, checkDefense, checkArmor); @@ -805,6 +935,10 @@ bool Monster::isTarget(const std::shared_ptr &creature) { return true; } +bool Monster::isFleeing() const { + return !isSummon() && getHealth() <= runAwayHealth && challengeFocusDuration <= 0 && challengeMeleeDuration <= 0; +} + bool Monster::selectTarget(const std::shared_ptr &creature) { if (!isTarget(creature)) { return false; @@ -866,6 +1000,10 @@ void Monster::updateIdleStatus() { setIdle(idle); } +bool Monster::getIdleStatus() const { + return isIdle; +} + bool Monster::isInSpawnLocation() const { if (!spawnMonster) { return true; @@ -947,17 +1085,18 @@ void Monster::onThink(uint32_t interval) { const auto &attackedCreature = getAttackedCreature(); const auto &followCreature = getFollowCreature(); if (isSummon()) { + const auto &master = getMaster(); if (attackedCreature.get() == this) { setFollowCreature(nullptr); } else if (attackedCreature && followCreature != attackedCreature) { // This happens just after a master orders an attack, so lets follow it aswell. setFollowCreature(attackedCreature); - } else if (getMaster() && getMaster()->getAttackedCreature()) { + } else if (master && master->getAttackedCreature()) { // This happens if the monster is summoned during combat - selectTarget(getMaster()->getAttackedCreature()); - } else if (getMaster() != followCreature) { + selectTarget(master->getAttackedCreature()); + } else if (master && master != followCreature) { // Our master has not ordered us to attack anything, lets follow him around instead. - setFollowCreature(getMaster()); + setFollowCreature(master); } } else if (!targetList.empty()) { const bool attackedCreatureIsDisconnected = attackedCreature && attackedCreature->getPlayer() && attackedCreature->getPlayer()->isDisconnected(); @@ -1035,6 +1174,10 @@ void Monster::doAttacking(uint32_t interval) { } } +bool Monster::hasExtraSwing() { + return extraMeleeAttack; +} + bool Monster::canUseAttack(const Position &pos, const std::shared_ptr &target) const { if (isHostile()) { const Position &targetPos = target->getPosition(); @@ -2015,6 +2158,71 @@ bool Monster::getDistanceStep(const Position &targetPos, Direction &moveDirectio return true; } +bool Monster::isTargetNearby() const { + return stepDuration >= 1; +} + +bool Monster::isIgnoringFieldDamage() const { + return ignoreFieldDamage; +} + +bool Monster::israndomStepping() const { + return randomStepping; +} + +void Monster::setIgnoreFieldDamage(bool ignore) { + ignoreFieldDamage = ignore; +} + +bool Monster::getIgnoreFieldDamage() const { + return ignoreFieldDamage; +} + +uint16_t Monster::getRaceId() const { + return mType->info.raceid; +} + +// Hazard system +bool Monster::getHazard() const { + return hazard; +} + +void Monster::setHazard(bool value) { + hazard = value; +} + +bool Monster::getHazardSystemCrit() const { + return hazardCrit; +} + +void Monster::setHazardSystemCrit(bool value) { + hazardCrit = value; +} + +bool Monster::getHazardSystemDodge() const { + return hazardDodge; +} + +void Monster::setHazardSystemDodge(bool value) { + hazardDodge = value; +} + +bool Monster::getHazardSystemDamageBoost() const { + return hazardDamageBoost; +} + +void Monster::setHazardSystemDamageBoost(bool value) { + hazardDamageBoost = value; +} + +bool Monster::getHazardSystemDefenseBoost() const { + return hazardDefenseBoost; +} + +void Monster::setHazardSystemDefenseBoost(bool value) { + hazardDefenseBoost = value; +} + bool Monster::canWalkTo(Position pos, Direction moveDirection) { pos = getNextPosition(moveDirection, pos); if (isInSpawnRange(pos)) { @@ -2254,6 +2462,27 @@ bool Monster::changeTargetDistance(int32_t distance, uint32_t duration /* = 1200 return true; } +bool Monster::isChallenged() const { + return challengeFocusDuration > 0; +} + +std::vector Monster::getIcons() const { + auto creatureIcons = Creature::getIcons(); + if (!creatureIcons.empty()) { + return creatureIcons; + } + + using enum CreatureIconModifications_t; + if (challengeMeleeDuration > 0 && mType->info.targetDistance > targetDistance) { + return { CreatureIcon(TurnedMelee) }; + } else if (varBuffs[BUFF_DAMAGERECEIVED] > 100) { + return { CreatureIcon(HigherDamageReceived) }; + } else if (varBuffs[BUFF_DAMAGEDEALT] < 100) { + return { CreatureIcon(LowerDamageDealt) }; + } + return {}; +} + bool Monster::isImmune(ConditionType_t conditionType) const { return m_isImmune || mType->info.m_conditionImmunities[static_cast(conditionType)]; } @@ -2262,6 +2491,38 @@ bool Monster::isImmune(CombatType_t combatType) const { return m_isImmune || mType->info.m_damageImmunities[combatTypeToIndex(combatType)]; } +void Monster::setImmune(bool immune) { + m_isImmune = immune; +} + +bool Monster::isImmune() const { + return m_isImmune; +} + +float Monster::getAttackMultiplier() const { + float multiplier = mType->getAttackMultiplier(); + if (auto stacks = getForgeStack(); stacks > 0) { + multiplier *= (1.35 + (stacks - 1) * 0.1); + } + return multiplier; +} + +float Monster::getDefenseMultiplier() const { + float multiplier = mType->getDefenseMultiplier(); + if (auto stacks = getForgeStack(); stacks > 0) { + multiplier *= (1 + (0.1 * stacks)); + } + return multiplier; +} + +bool Monster::isDead() const { + return m_isDead; +} + +void Monster::setDead(bool isDead) { + m_isDead = isDead; +} + void Monster::getPathSearchParams(const std::shared_ptr &creature, FindPathParams &fpp) { Creature::getPathSearchParams(creature, fpp); @@ -2269,7 +2530,8 @@ void Monster::getPathSearchParams(const std::shared_ptr &creature, Fin fpp.maxTargetDist = targetDistance; if (isSummon()) { - if (getMaster() == creature) { + const auto &master = getMaster(); + if (master && master == creature) { fpp.maxTargetDist = 2; fpp.fullPathSearch = true; } else if (targetDistance <= 1) { @@ -2320,6 +2582,46 @@ void Monster::configureForgeSystem() { g_game().sendUpdateCreature(static_self_cast()); } +bool Monster::canBeForgeMonster() const { + return getForgeStack() == 0 && !isSummon() && !isRewardBoss() && canDropLoot() && isForgeCreature() && getRaceId() > 0; +} + +bool Monster::isForgeCreature() const { + return mType->info.isForgeCreature; +} + +void Monster::setForgeMonster(bool forge) const { + mType->info.isForgeCreature = forge; +} + +uint16_t Monster::getForgeStack() const { + return forgeStack; +} + +void Monster::setForgeStack(uint16_t stack) { + forgeStack = stack; +} + +ForgeClassifications_t Monster::getMonsterForgeClassification() const { + return monsterForgeClassification; +} + +void Monster::setMonsterForgeClassification(ForgeClassifications_t classification) { + monsterForgeClassification = classification; +} + +void Monster::setTimeToChangeFiendish(time_t time) { + timeToChangeFiendish = time; +} + +time_t Monster::getTimeToChangeFiendish() const { + return timeToChangeFiendish; +} + +std::shared_ptr Monster::getMonsterType() const { + return mType; +} + void Monster::clearFiendishStatus() { timeToChangeFiendish = 0; forgeStack = 0; diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index 3b7026e4134..c52a2a6de05 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -8,13 +8,17 @@ */ #pragma once +#include "creatures/creature.hpp" +#include "lua/lua_definitions.hpp" -#include "creatures/monsters/monsters.hpp" -#include "declarations.hpp" -#include "items/tile.hpp" - +struct spellBlock_t; +class MonsterType; +class Tile; class Creature; class Game; +class SpawnMonster; + +using CreatureVector = std::vector>; class Monster final : public Creature { public: @@ -28,18 +32,10 @@ class Monster final : public Creature { Monster(const Monster &) = delete; Monster &operator=(const Monster &) = delete; - std::shared_ptr getMonster() override { - return static_self_cast(); - } - std::shared_ptr getMonster() const override { - return static_self_cast(); - } + std::shared_ptr getMonster() override; + std::shared_ptr getMonster() const override; - void setID() override { - if (id == 0) { - id = monsterAutoID++; - } - } + void setID() override; void addList() override; void removeList() override; @@ -48,91 +44,40 @@ class Monster final : public Creature { void setName(const std::string &name); // Real monster name, set on monster creation "createMonsterType(typeName)" - const std::string &getTypeName() const override { - return mType->typeName; - } + const std::string &getTypeName() const override; const std::string &getNameDescription() const override; - void setNameDescription(const std::string &nameDescription) { - this->nameDescription = nameDescription; - }; - std::string getDescription(int32_t) override { - return nameDescription + '.'; - } + void setNameDescription(std::string_view nameDescription); + ; + std::string getDescription(int32_t) override; - CreatureType_t getType() const override { - return CREATURETYPE_MONSTER; - } + CreatureType_t getType() const override; - const Position &getMasterPos() const { - return masterPos; - } - void setMasterPos(Position pos) { - masterPos = pos; - } + const Position &getMasterPos() const; + void setMasterPos(Position pos); - RaceType_t getRace() const override { - return mType->info.race; - } + RaceType_t getRace() const override; float getMitigation() const override; - int32_t getArmor() const override { - return mType->info.armor * getDefenseMultiplier(); - } + int32_t getArmor() const override; int32_t getDefense() const override; void addDefense(int32_t defense); - Faction_t getFaction() const override { - auto master = getMaster(); - if (getMaster()) { - return getMaster()->getFaction(); - } - return mType->info.faction; - } + Faction_t getFaction() const override; - bool isEnemyFaction(Faction_t faction) const { - const auto &master = getMaster(); - if (master && master->getMonster()) { - return master->getMonster()->isEnemyFaction(faction); - } - return mType->info.enemyFactions.empty() ? false : mType->info.enemyFactions.contains(faction); - } + bool isEnemyFaction(Faction_t faction) const; - bool isPushable() override { - return mType->info.pushable && baseSpeed != 0; - } - bool isAttackable() const override { - return mType->info.isAttackable; - } - bool canPushItems() const { - return mType->info.canPushItems; - } - bool canPushCreatures() const { - return mType->info.canPushCreatures; - } - bool isRewardBoss() const { - return mType->info.isRewardBoss; - } - bool isHostile() const { - return mType->info.isHostile; - } - bool isFamiliar() const { - return mType->info.isFamiliar; - } - bool canSeeInvisibility() const override { - return isImmune(CONDITION_INVISIBLE); - } - uint16_t critChance() const { - return mType->info.critChance; - } - uint32_t getManaCost() const { - return mType->info.manaCost; - } - RespawnType getRespawnType() const { - return mType->info.respawnType; - } - void setSpawnMonster(const std::shared_ptr &newSpawnMonster) { - this->spawnMonster = newSpawnMonster; - } + bool isPushable() override; + bool isAttackable() const override; + bool canPushItems() const; + bool canPushCreatures() const; + bool isRewardBoss() const; + bool isHostile() const; + bool isFamiliar() const; + bool canSeeInvisibility() const override; + uint16_t critChance() const; + uint32_t getManaCost() const; + RespawnType getRespawnType() const; + void setSpawnMonster(const std::shared_ptr &newSpawnMonster); double_t getReflectPercent(CombatType_t combatType, bool useCharges = false) const override; uint32_t getHealingCombatValue(CombatType_t healingType) const; @@ -159,32 +104,15 @@ class Monster final : public Creature { bool challengeCreature(const std::shared_ptr &creature, int targetChangeCooldown) override; bool changeTargetDistance(int32_t distance, uint32_t duration = 12000); - bool isChallenged() const { - return challengeFocusDuration > 0; - } + bool isChallenged() const; - std::vector getIcons() const override { - auto creatureIcons = Creature::getIcons(); - if (!creatureIcons.empty()) { - return creatureIcons; - } - if (challengeMeleeDuration > 0 && mType->info.targetDistance > targetDistance) { - return { CreatureIcon(CreatureIconModifications_t::TurnedMelee) }; - } else if (varBuffs[BUFF_DAMAGERECEIVED] > 100) { - return { CreatureIcon(CreatureIconModifications_t::HigherDamageReceived) }; - } else if (varBuffs[BUFF_DAMAGEDEALT] < 100) { - return { CreatureIcon(CreatureIconModifications_t::LowerDamageDealt) }; - } - return {}; - } + std::vector getIcons() const override; void setNormalCreatureLight() override; bool getCombatValues(int32_t &min, int32_t &max) override; void doAttacking(uint32_t interval) override; - bool hasExtraSwing() override { - return extraMeleeAttack; - } + bool hasExtraSwing() override; bool searchTarget(TargetSearchType_t searchType = TARGETSEARCH_DEFAULT); bool selectTarget(const std::shared_ptr &creature); @@ -222,64 +150,30 @@ class Monster final : public Creature { } bool isTarget(const std::shared_ptr &creature); - bool isFleeing() const { - return !isSummon() && getHealth() <= runAwayHealth && challengeFocusDuration <= 0 && challengeMeleeDuration <= 0; - } + bool isFleeing() const; bool getDistanceStep(const Position &targetPos, Direction &direction, bool flee = false); - bool isTargetNearby() const { - return stepDuration >= 1; - } - bool isIgnoringFieldDamage() const { - return ignoreFieldDamage; - } - bool israndomStepping() const { - return randomStepping; - } - void setIgnoreFieldDamage(bool ignore) { - ignoreFieldDamage = ignore; - } - bool getIgnoreFieldDamage() const { - return ignoreFieldDamage; - } - uint16_t getRaceId() const { - return mType->info.raceid; - } + bool isTargetNearby() const; + bool isIgnoringFieldDamage() const; + bool israndomStepping() const; + void setIgnoreFieldDamage(bool ignore); + bool getIgnoreFieldDamage() const; + uint16_t getRaceId() const; // Hazard system - bool getHazard() const { - return hazard; - } - void setHazard(bool value) { - hazard = value; - } + bool getHazard() const; + void setHazard(bool value); - bool getHazardSystemCrit() const { - return hazardCrit; - } - void setHazardSystemCrit(bool value) { - hazardCrit = value; - } + bool getHazardSystemCrit() const; + void setHazardSystemCrit(bool value); - bool getHazardSystemDodge() const { - return hazardDodge; - } - void setHazardSystemDodge(bool value) { - hazardDodge = value; - } + bool getHazardSystemDodge() const; + void setHazardSystemDodge(bool value); - bool getHazardSystemDamageBoost() const { - return hazardDamageBoost; - } - void setHazardSystemDamageBoost(bool value) { - hazardDamageBoost = value; - } - bool getHazardSystemDefenseBoost() const { - return hazardDefenseBoost; - } - void setHazardSystemDefenseBoost(bool value) { - hazardDefenseBoost = value; - } + bool getHazardSystemDamageBoost() const; + void setHazardSystemDamageBoost(bool value); + bool getHazardSystemDefenseBoost() const; + void setHazardSystemDefenseBoost(bool value); // Hazard end void updateTargetList(); @@ -292,81 +186,41 @@ class Monster final : public Creature { void configureForgeSystem(); - bool canBeForgeMonster() const { - return getForgeStack() == 0 && !isSummon() && !isRewardBoss() && canDropLoot() && isForgeCreature() && getRaceId() > 0; - } + bool canBeForgeMonster() const; - bool isForgeCreature() const { - return mType->info.isForgeCreature; - } + bool isForgeCreature() const; - void setForgeMonster(bool forge) const { - mType->info.isForgeCreature = forge; - } + void setForgeMonster(bool forge) const; - uint16_t getForgeStack() const { - return forgeStack; - } + uint16_t getForgeStack() const; - void setForgeStack(uint16_t stack) { - forgeStack = stack; - } + void setForgeStack(uint16_t stack); - ForgeClassifications_t getMonsterForgeClassification() const { - return monsterForgeClassification; - } + ForgeClassifications_t getMonsterForgeClassification() const; - void setMonsterForgeClassification(ForgeClassifications_t classification) { - monsterForgeClassification = classification; - } + void setMonsterForgeClassification(ForgeClassifications_t classification); - void setTimeToChangeFiendish(time_t time) { - timeToChangeFiendish = time; - } + void setTimeToChangeFiendish(time_t time); - time_t getTimeToChangeFiendish() const { - return timeToChangeFiendish; - } + time_t getTimeToChangeFiendish() const; - std::shared_ptr getMonsterType() const { - return mType; - } + std::shared_ptr getMonsterType() const; void clearFiendishStatus(); bool canDropLoot() const; bool isImmune(ConditionType_t conditionType) const override; bool isImmune(CombatType_t combatType) const override; - void setImmune(bool immune) { - m_isImmune = immune; - } - bool isImmune() const { - return m_isImmune; - } + void setImmune(bool immune); + bool isImmune() const; - float getAttackMultiplier() const { - float multiplier = mType->getAttackMultiplier(); - if (auto stacks = getForgeStack(); stacks > 0) { - multiplier *= (1.35 + (stacks - 1) * 0.1); - } - return multiplier; - } + float getAttackMultiplier() const; - float getDefenseMultiplier() const { - float multiplier = mType->getDefenseMultiplier(); - if (auto stacks = getForgeStack(); stacks > 0) { - multiplier *= (1 + (0.1 * stacks)); - } - return multiplier; - } + float getDefenseMultiplier() const; - bool isDead() const override { - return m_isDead; - } + bool isDead() const override; - void setDead(bool isDead) { - m_isDead = isDead; - } + void setDead(bool isDead); protected: void onExecuteAsyncTasks() override; @@ -449,9 +303,7 @@ class Monster final : public Creature { void setIdle(bool idle); void updateIdleStatus(); - bool getIdleStatus() const { - return isIdle; - } + bool getIdleStatus() const; void onAddCondition(ConditionType_t type) override; void onEndCondition(ConditionType_t type) override; @@ -477,12 +329,8 @@ class Monster final : public Creature { bool isFriend(const std::shared_ptr &creature) const; bool isOpponent(const std::shared_ptr &creature) const; - uint64_t getLostExperience() const override { - return skillLoss ? mType->info.experience : 0; - } - uint16_t getLookCorpse() const override { - return mType->info.lookcorpse; - } + uint64_t getLostExperience() const override; + uint16_t getLookCorpse() const override; void dropLoot(const std::shared_ptr &corpse, const std::shared_ptr &lastHitCreature) override; void getPathSearchParams(const std::shared_ptr &creature, FindPathParams &fpp) override; bool useCacheMap() const override { diff --git a/src/creatures/monsters/monsters.cpp b/src/creatures/monsters/monsters.cpp index 75cec75fe16..87fd967ab46 100644 --- a/src/creatures/monsters/monsters.cpp +++ b/src/creatures/monsters/monsters.cpp @@ -9,10 +9,13 @@ #include "creatures/monsters/monsters.hpp" -#include "creatures/combat/spells.hpp" +#include "config/configmanager.hpp" #include "creatures/combat/combat.hpp" +#include "creatures/combat/condition.hpp" +#include "creatures/combat/spells.hpp" #include "game/game.hpp" #include "items/weapons/weapons.hpp" +#include "lua/scripts/luascript.hpp" void MonsterType::loadLoot(const std::shared_ptr &monsterType, LootBlock lootBlock) const { if (lootBlock.childLoot.empty()) { @@ -307,6 +310,38 @@ bool MonsterType::loadCallback(LuaScriptInterface* scriptInterface) { return true; } +uint16_t MonsterType::getBaseSpeed() const { + return info.baseSpeed; +} + +void MonsterType::setBaseSpeed(const uint16_t initBaseSpeed) { + info.baseSpeed = initBaseSpeed; +} + +float MonsterType::getHealthMultiplier() const { + return isBoss() ? g_configManager().getFloat(RATE_BOSS_HEALTH) : g_configManager().getFloat(RATE_MONSTER_HEALTH); +} + +float MonsterType::getAttackMultiplier() const { + return isBoss() ? g_configManager().getFloat(RATE_BOSS_ATTACK) : g_configManager().getFloat(RATE_MONSTER_ATTACK); +} + +float MonsterType::getDefenseMultiplier() const { + return isBoss() ? g_configManager().getFloat(RATE_BOSS_DEFENSE) : g_configManager().getFloat(RATE_MONSTER_DEFENSE); +} + +bool MonsterType::isBoss() const { + return !info.bosstiaryClass.empty(); +} + +Monsters &Monsters::getInstance() { + return inject(); +} + +void Monsters::clear() { + monsters.clear(); +} + std::shared_ptr Monsters::getMonsterType(const std::string &name, bool silent /* = false*/) const { std::string lowerCaseName = asLowerCaseString(name); if (auto it = monsters.find(lowerCaseName); diff --git a/src/creatures/monsters/monsters.hpp b/src/creatures/monsters/monsters.hpp index 523a320557d..35c4d73050d 100644 --- a/src/creatures/monsters/monsters.hpp +++ b/src/creatures/monsters/monsters.hpp @@ -9,9 +9,13 @@ #pragma once +#include "creatures/creatures_definitions.hpp" +#include "game/game_definitions.hpp" #include "io/io_bosstiary.hpp" -#include "creatures/creature.hpp" -#include "declarations.hpp" +#include "utils/utils_definitions.hpp" + +class LuaScriptInterface; +class ConditionDamage; class Loot { public: @@ -181,29 +185,17 @@ class MonsterType { MonsterInfo info; - uint16_t getBaseSpeed() const { - return info.baseSpeed; - } + uint16_t getBaseSpeed() const; - void setBaseSpeed(const uint16_t initBaseSpeed) { - info.baseSpeed = initBaseSpeed; - } + void setBaseSpeed(const uint16_t initBaseSpeed); - float getHealthMultiplier() const { - return isBoss() ? g_configManager().getFloat(RATE_BOSS_HEALTH) : g_configManager().getFloat(RATE_MONSTER_HEALTH); - } + float getHealthMultiplier() const; - float getAttackMultiplier() const { - return isBoss() ? g_configManager().getFloat(RATE_BOSS_ATTACK) : g_configManager().getFloat(RATE_MONSTER_ATTACK); - } + float getAttackMultiplier() const; - float getDefenseMultiplier() const { - return isBoss() ? g_configManager().getFloat(RATE_BOSS_DEFENSE) : g_configManager().getFloat(RATE_MONSTER_DEFENSE); - } + float getDefenseMultiplier() const; - bool isBoss() const { - return !info.bosstiaryClass.empty(); - } + bool isBoss() const; void loadLoot(const std::shared_ptr &monsterType, LootBlock lootblock) const; @@ -265,13 +257,9 @@ class Monsters { Monsters(const Monsters &) = delete; Monsters &operator=(const Monsters &) = delete; - static Monsters &getInstance() { - return inject(); - } + static Monsters &getInstance(); - void clear() { - monsters.clear(); - } + void clear(); std::shared_ptr getMonsterType(const std::string &name, bool silent = false) const; std::shared_ptr getMonsterTypeByRaceId(uint16_t raceId, bool isBoss = false) const; diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index 8578c3e2a15..9dbf3f2745c 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -8,16 +8,21 @@ */ #include "creatures/monsters/spawns/spawn_monster.hpp" -#include "game/game.hpp" + +#include "config/configmanager.hpp" #include "creatures/monsters/monster.hpp" +#include "creatures/monsters/monsters.hpp" +#include "creatures/players/player.hpp" +#include "game/game.hpp" +#include "game/movement/position.hpp" #include "game/scheduling/dispatcher.hpp" #include "game/scheduling/events_scheduler.hpp" -#include "lua/creature/events.hpp" +#include "game/zones/zone.hpp" #include "lua/callbacks/event_callback.hpp" #include "lua/callbacks/events_callbacks.hpp" -#include "utils/pugicast.hpp" -#include "game/zones/zone.hpp" +#include "lua/creature/events.hpp" #include "map/spectators.hpp" +#include "utils/pugicast.hpp" static constexpr int32_t MONSTER_MINSPAWN_INTERVAL = 1000; // 1 second static constexpr int32_t MONSTER_MAXSPAWN_INTERVAL = 86400000; // 1 day @@ -138,6 +143,18 @@ void SpawnsMonster::clear() { filemonstername.clear(); } +bool SpawnsMonster::isStarted() const { + return started; +} + +bool SpawnsMonster::isLoaded() const { + return loaded; +} + +std::vector> &SpawnsMonster::getspawnMonsterList() { + return spawnMonsterList; +} + bool SpawnsMonster::isInZone(const Position ¢erPos, int32_t radius, const Position &pos) { if (radius == -1) { return true; @@ -158,6 +175,29 @@ SpawnMonster::~SpawnMonster() { stopEvent(); } +// moveable + +SpawnMonster::SpawnMonster(SpawnMonster &&rhs) noexcept : + spawnedMonsterMap(std::move(rhs.spawnedMonsterMap)), + spawnMonsterMap(std::move(rhs.spawnMonsterMap)), + centerPos(rhs.centerPos), + radius(rhs.radius), + interval(rhs.interval), + checkSpawnMonsterEvent(rhs.checkSpawnMonsterEvent) { } + +SpawnMonster &SpawnMonster::operator=(SpawnMonster &&rhs) noexcept { + if (this != &rhs) { + spawnMonsterMap = std::move(rhs.spawnMonsterMap); + spawnedMonsterMap = std::move(rhs.spawnedMonsterMap); + + checkSpawnMonsterEvent = rhs.checkSpawnMonsterEvent; + centerPos = rhs.centerPos; + radius = rhs.radius; + interval = rhs.interval; + } + return *this; +} + bool SpawnMonster::findPlayer(const Position &pos) { auto spectators = Spectators().find(pos); return std::ranges::any_of(spectators, [](const auto &spectator) { @@ -300,6 +340,10 @@ void SpawnMonster::cleanup() { }); } +const Position &SpawnMonster::getCenterPos() const { + return centerPos; +} + bool SpawnMonster::addMonster(const std::string &name, const Position &pos, Direction dir, uint32_t scheduleInterval, uint32_t weight /*= 1*/) { std::string variant; for (const auto &zone : Zone::getZones(pos)) { diff --git a/src/creatures/monsters/spawns/spawn_monster.hpp b/src/creatures/monsters/spawns/spawn_monster.hpp index a20fd050c47..d825ad73c23 100644 --- a/src/creatures/monsters/spawns/spawn_monster.hpp +++ b/src/creatures/monsters/spawns/spawn_monster.hpp @@ -11,6 +11,8 @@ #include "game/movement/position.hpp" +enum Direction : uint8_t; +struct Position; class Monster; class MonsterType; @@ -36,26 +38,9 @@ class SpawnMonster : public SharedObject { SpawnMonster &operator=(const SpawnMonster &) = delete; // moveable - SpawnMonster(SpawnMonster &&rhs) noexcept : - spawnedMonsterMap(std::move(rhs.spawnedMonsterMap)), - spawnMonsterMap(std::move(rhs.spawnMonsterMap)), - centerPos(rhs.centerPos), - radius(rhs.radius), - interval(rhs.interval), - checkSpawnMonsterEvent(rhs.checkSpawnMonsterEvent) { } - - SpawnMonster &operator=(SpawnMonster &&rhs) noexcept { - if (this != &rhs) { - spawnMonsterMap = std::move(rhs.spawnMonsterMap); - spawnedMonsterMap = std::move(rhs.spawnedMonsterMap); - - checkSpawnMonsterEvent = rhs.checkSpawnMonsterEvent; - centerPos = rhs.centerPos; - radius = rhs.radius; - interval = rhs.interval; - } - return *this; - } + SpawnMonster(SpawnMonster &&rhs) noexcept; + + SpawnMonster &operator=(SpawnMonster &&rhs) noexcept; bool addMonster(const std::string &name, const Position &pos, Direction dir, uint32_t interval, uint32_t weight = 1); void removeMonster(const std::shared_ptr &monster); @@ -72,9 +57,7 @@ class SpawnMonster : public SharedObject { bool isInSpawnMonsterZone(const Position &pos) const; void cleanup(); - const Position &getCenterPos() const { - return centerPos; - } + const Position &getCenterPos() const; void setMonsterVariant(const std::string &variant); @@ -102,15 +85,9 @@ class SpawnsMonster { void startup(); void clear(); - bool isStarted() const { - return started; - } - bool isLoaded() const { - return loaded; - } - std::vector> &getspawnMonsterList() { - return spawnMonsterList; - } + bool isStarted() const; + bool isLoaded() const; + std::vector> &getspawnMonsterList(); private: std::vector> spawnMonsterList; diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp index 0985881e971..e2ab99a1dd4 100644 --- a/src/creatures/npcs/npc.cpp +++ b/src/creatures/npcs/npc.cpp @@ -8,13 +8,17 @@ */ #include "creatures/npcs/npc.hpp" + +#include "config/configmanager.hpp" +#include "creatures/creature.hpp" #include "creatures/npcs/npcs.hpp" -#include "declarations.hpp" +#include "creatures/players/player.hpp" #include "game/game.hpp" -#include "lua/callbacks/creaturecallback.hpp" #include "game/scheduling/dispatcher.hpp" -#include "map/spectators.hpp" #include "lib/metrics/metrics.hpp" +#include "lua/callbacks/creaturecallback.hpp" +#include "lua/global/shared_object.hpp" +#include "map/spectators.hpp" int32_t Npc::despawnRange; int32_t Npc::despawnRadius; @@ -50,10 +54,96 @@ Npc::Npc(const std::shared_ptr &npcType) : } } +Npc &Npc::getInstance() { + return inject(); +} + +std::shared_ptr Npc::getNpc() { + return static_self_cast(); +} + +std::shared_ptr Npc::getNpc() const { + return static_self_cast(); +} + +void Npc::setID() { + if (id == 0) { + id = npcAutoID++; + } +} + void Npc::addList() { g_game().addNpc(static_self_cast()); } +const std::string &Npc::getName() const { + return npcType->name; +} + +// Real npc name, set on npc creation "createNpcType(typeName)" +const std::string &Npc::getTypeName() const { + return npcType->typeName; +} + +const std::string &Npc::getNameDescription() const { + return npcType->nameDescription; +} + +std::string Npc::getDescription(int32_t) { + return strDescription + '.'; +} + +void Npc::setName(std::string newName) const { + npcType->name = std::move(newName); +} + +CreatureType_t Npc::getType() const { + return CREATURETYPE_NPC; +} + +const Position &Npc::getMasterPos() const { + return masterPos; +} + +void Npc::setMasterPos(Position pos) { + masterPos = pos; +} + +uint8_t Npc::getSpeechBubble() const { + return npcType->info.speechBubble; +} + +void Npc::setSpeechBubble(const uint8_t bubble) const { + npcType->info.speechBubble = bubble; +} + +uint16_t Npc::getCurrency() const { + return npcType->info.currencyId; +} + +void Npc::setCurrency(uint16_t currency) { + npcType->info.currencyId = currency; +} + +const std::vector &Npc::getShopItemVector(uint32_t playerGUID) const { + if (playerGUID != 0) { + auto it = shopPlayers.find(playerGUID); + if (it != shopPlayers.end() && !it->second.empty()) { + return it->second; + } + } + + return npcType->info.shopItemVector; +} + +bool Npc::isPushable() { + return npcType->info.pushable; +} + +bool Npc::isAttackable() const { + return false; +} + void Npc::removeList() { g_game().removeNpc(static_self_cast()); } @@ -65,6 +155,18 @@ bool Npc::canInteract(const Position &pos, uint32_t range /* = 4 */) { return Creature::canSee(getPosition(), pos, range, range); } +bool Npc::canSeeInvisibility() const { + return true; +} + +RespawnType Npc::getRespawnType() const { + return npcType->info.respawnType; +} + +void Npc::setSpawnNpc(const std::shared_ptr &newSpawn) { + spawnNpc = newSpawn; +} + bool Npc::isInteractingWithPlayer(uint32_t playerId) { if (playerInteractions.empty()) { return false; @@ -689,6 +791,10 @@ bool Npc::getRandomStep(Direction &moveDirection) { return false; } +void Npc::setNormalCreatureLight() { + internalLight = npcType->info.light; +} + bool Npc::isShopPlayer(uint32_t playerGUID) const { return shopPlayers.contains(playerGUID); } diff --git a/src/creatures/npcs/npc.hpp b/src/creatures/npcs/npc.hpp index 79b4c78c544..dc3e16961f1 100644 --- a/src/creatures/npcs/npc.hpp +++ b/src/creatures/npcs/npc.hpp @@ -9,12 +9,13 @@ #pragma once -#include "creatures/npcs/npcs.hpp" -#include "creatures/players/player.hpp" -#include "declarations.hpp" -#include "items/tile.hpp" -#include "lib/di/container.hpp" +#include "creatures/creature.hpp" +enum Direction : uint8_t; +struct Position; +class NpcType; +class Player; +class Tile; class Creature; class Game; class SpawnNpc; @@ -32,98 +33,45 @@ class Npc final : public Creature { Npc(const Npc &) = delete; void operator=(const std::shared_ptr &) = delete; - static Npc &getInstance() { - return inject(); - } + static Npc &getInstance(); - std::shared_ptr getNpc() override { - return static_self_cast(); - } - std::shared_ptr getNpc() const override { - return static_self_cast(); - } + std::shared_ptr getNpc() override; + std::shared_ptr getNpc() const override; - void setID() override { - if (id == 0) { - id = npcAutoID++; - } - } + void setID() override; void removeList() override; void addList() override; - const std::string &getName() const override { - return npcType->name; - } + const std::string &getName() const override; // Real npc name, set on npc creation "createNpcType(typeName)" - const std::string &getTypeName() const override { - return npcType->typeName; - } - const std::string &getNameDescription() const override { - return npcType->nameDescription; - } - std::string getDescription(int32_t) override { - return strDescription + '.'; - } - - void setName(std::string newName) const { - npcType->name = std::move(newName); - } - - CreatureType_t getType() const override { - return CREATURETYPE_NPC; - } - - const Position &getMasterPos() const { - return masterPos; - } - void setMasterPos(Position pos) { - masterPos = pos; - } - - uint8_t getSpeechBubble() const override { - return npcType->info.speechBubble; - } - void setSpeechBubble(const uint8_t bubble) const { - npcType->info.speechBubble = bubble; - } - - uint16_t getCurrency() const { - return npcType->info.currencyId; - } - void setCurrency(uint16_t currency) const { - npcType->info.currencyId = currency; - } - - const std::vector &getShopItemVector(uint32_t playerGUID) const { - if (playerGUID != 0) { - auto it = shopPlayers.find(playerGUID); - if (it != shopPlayers.end() && !it->second.empty()) { - return it->second; - } - } - - return npcType->info.shopItemVector; - } - - bool isPushable() override { - return npcType->info.pushable; - } - - bool isAttackable() const override { - return false; - } + const std::string &getTypeName() const override; + const std::string &getNameDescription() const override; + std::string getDescription(int32_t) override; + + void setName(std::string newName) const; + + CreatureType_t getType() const override; + + const Position &getMasterPos() const; + void setMasterPos(Position pos); + + uint8_t getSpeechBubble() const override; + void setSpeechBubble(const uint8_t bubble) const; + + uint16_t getCurrency() const; + void setCurrency(uint16_t currency); + + const std::vector &getShopItemVector(uint32_t playerGUID) const; + + bool isPushable() override; + + bool isAttackable() const override; bool canInteract(const Position &pos, uint32_t range = 4); - bool canSeeInvisibility() const override { - return true; - } - RespawnType getRespawnType() const { - return npcType->info.respawnType; - } - void setSpawnNpc(const std::shared_ptr &newSpawn) { - spawnNpc = newSpawn; - } + bool canSeeInvisibility() const override; + RespawnType getRespawnType() const; + void setSpawnNpc(const std::shared_ptr &newSpawn); void setPlayerInteraction(uint32_t playerId, uint16_t topicId = 0); void removePlayerInteraction(const std::shared_ptr &player); @@ -149,9 +97,7 @@ class Npc final : public Creature { bool getNextStep(Direction &nextDirection, uint32_t &flags) override; bool getRandomStep(Direction &moveDirection); - void setNormalCreatureLight() override { - internalLight = npcType->info.light; - } + void setNormalCreatureLight() override; bool isShopPlayer(uint32_t playerGUID) const; diff --git a/src/creatures/npcs/npcs.cpp b/src/creatures/npcs/npcs.cpp index b6c6a5cb8e7..cff44588d71 100644 --- a/src/creatures/npcs/npcs.cpp +++ b/src/creatures/npcs/npcs.cpp @@ -7,13 +7,13 @@ * Website: https://docs.opentibiabr.com/ */ -#include "declarations.hpp" -#include "creatures/combat/combat.hpp" -#include "lua/scripts/lua_environment.hpp" -#include "creatures/combat/spells.hpp" #include "creatures/npcs/npcs.hpp" -#include "lua/scripts/scripts.hpp" + +#include "config/configmanager.hpp" #include "game/game.hpp" +#include "lua/scripts/lua_environment.hpp" +#include "lua/scripts/luascript.hpp" +#include "lua/scripts/scripts.hpp" bool NpcType::canSpawn(const Position &pos) const { bool canSpawn = true; @@ -129,6 +129,10 @@ bool Npcs::reload() { return false; } +Npcs &Npcs::getInstance() { + return inject(); +} + std::shared_ptr Npcs::getNpcType(const std::string &name, bool create /* = false*/) { const std::string key = asLowerCaseString(name); auto it = npcs.find(key); diff --git a/src/creatures/npcs/npcs.hpp b/src/creatures/npcs/npcs.hpp index c12f6125bfe..b91e1547c07 100644 --- a/src/creatures/npcs/npcs.hpp +++ b/src/creatures/npcs/npcs.hpp @@ -9,8 +9,10 @@ #pragma once -#include "creatures/creature.hpp" -#include "lib/di/container.hpp" +#include "creatures/creatures_definitions.hpp" +#include "utils/utils_definitions.hpp" + +class LuaScriptInterface; class Shop { public: @@ -100,9 +102,7 @@ class Npcs { Npcs(const Npcs &) = delete; Npcs &operator=(const Npcs &) = delete; - static Npcs &getInstance() { - return inject(); - } + static Npcs &getInstance(); std::shared_ptr getNpcType(const std::string &name, bool create = false); diff --git a/src/creatures/npcs/spawns/spawn_npc.cpp b/src/creatures/npcs/spawns/spawn_npc.cpp index 54450b14b3d..bf3b6a1bc65 100644 --- a/src/creatures/npcs/spawns/spawn_npc.cpp +++ b/src/creatures/npcs/spawns/spawn_npc.cpp @@ -8,14 +8,17 @@ */ #include "creatures/npcs/spawns/spawn_npc.hpp" + #include "creatures/npcs/npc.hpp" +#include "creatures/npcs/npcs.hpp" +#include "creatures/players/player.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 "utils/pugicast.hpp" +#include "lua/creature/events.hpp" #include "map/spectators.hpp" +#include "utils/pugicast.hpp" static constexpr int32_t MINSPAWN_INTERVAL = 1000; // 1 second static constexpr int32_t MAXSPAWN_INTERVAL = 86400000; // 1 day @@ -119,6 +122,30 @@ void SpawnsNpc::clear() { fileName.clear(); } +bool SpawnsNpc::isStarted() const { + return started; +} + +bool SpawnsNpc::setStarted(bool setStarted) { + return started = setStarted; +} + +bool SpawnsNpc::isLoaded() const { + return loaded; +} + +bool SpawnsNpc::setLoaded(bool setLoaded) { + return loaded = setLoaded; +} + +std::string SpawnsNpc::setFileName(std::string setName) { + return fileName = std::move(setName); +} + +std::vector> &SpawnsNpc::getSpawnNpcList() { + return spawnNpcList; +} + bool SpawnsNpc::isInZone(const Position ¢erPos, int32_t radius, const Position &pos) { if (radius == -1) { return true; @@ -181,6 +208,10 @@ bool SpawnNpc::spawnNpc(uint32_t spawnId, const std::shared_ptr &npcTyp return true; } +uint32_t SpawnNpc::getInterval() const { + return interval; +} + void SpawnNpc::startup() { for (const auto &[spawnId, npcInfo] : spawnNpcMap) { if (spawnId == 0) { diff --git a/src/creatures/npcs/spawns/spawn_npc.hpp b/src/creatures/npcs/spawns/spawn_npc.hpp index 96c9597559f..38bbb55aee2 100644 --- a/src/creatures/npcs/spawns/spawn_npc.hpp +++ b/src/creatures/npcs/spawns/spawn_npc.hpp @@ -10,7 +10,6 @@ #pragma once #include "game/movement/position.hpp" -#include "items/tile.hpp" class Npc; class NpcType; @@ -36,9 +35,7 @@ class SpawnNpc final : public SharedObject { bool addNpc(const std::string &name, const Position &pos, Direction dir, uint32_t interval); void removeNpc(const std::shared_ptr &npc); - uint32_t getInterval() const { - return interval; - } + uint32_t getInterval() const; void startup(); void startSpawnNpcCheck(); @@ -76,27 +73,15 @@ class SpawnsNpc { void startup(); void clear(); - bool isStarted() const { - return started; - } - bool setStarted(bool setStarted) { - return started = setStarted; - } - - bool isLoaded() const { - return loaded; - } - bool setLoaded(bool setLoaded) { - return loaded = setLoaded; - } - - std::string setFileName(std::string setName) { - return fileName = std::move(setName); - } - - std::vector> &getSpawnNpcList() { - return spawnNpcList; - } + bool isStarted() const; + bool setStarted(bool setStarted); + + bool isLoaded() const; + bool setLoaded(bool setLoaded); + + std::string setFileName(std::string setName); + + std::vector> &getSpawnNpcList(); private: std::vector> spawnNpcList; diff --git a/src/creatures/players/achievement/player_achievement.cpp b/src/creatures/players/achievement/player_achievement.cpp index a8ef3fefdb7..0be4244b25e 100644 --- a/src/creatures/players/achievement/player_achievement.cpp +++ b/src/creatures/players/achievement/player_achievement.cpp @@ -7,7 +7,7 @@ * Website: https://docs.opentibiabr.com/ */ -#include "player_achievement.hpp" +#include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/player.hpp" #include "game/game.hpp" diff --git a/src/creatures/players/cyclopedia/player_badge.cpp b/src/creatures/players/cyclopedia/player_badge.cpp index 566de007549..6ed7451b1c3 100644 --- a/src/creatures/players/cyclopedia/player_badge.cpp +++ b/src/creatures/players/cyclopedia/player_badge.cpp @@ -7,14 +7,15 @@ * Website: https://docs.opentibiabr.com/ */ -#include "player_badge.hpp" -#include "enums/player_cyclopedia.hpp" +#include "creatures/players/cyclopedia/player_badge.hpp" + +#include "account/account.hpp" #include "creatures/players/player.hpp" +#include "enums/account_errors.hpp" +#include "enums/player_cyclopedia.hpp" #include "game/game.hpp" #include "kv/kv.hpp" -#include "enums/account_errors.hpp" - PlayerBadge::PlayerBadge(Player &player) : m_player(player) { } diff --git a/src/creatures/players/cyclopedia/player_cyclopedia.cpp b/src/creatures/players/cyclopedia/player_cyclopedia.cpp index bf2093a83cb..980d6897c31 100644 --- a/src/creatures/players/cyclopedia/player_cyclopedia.cpp +++ b/src/creatures/players/cyclopedia/player_cyclopedia.cpp @@ -7,15 +7,15 @@ * Website: https://docs.opentibiabr.com/ */ -#include "player_cyclopedia.hpp" +#include "creatures/players/cyclopedia/player_cyclopedia.hpp" -#include "database/databasetasks.hpp" #include "creatures/players/player.hpp" +#include "database/databasetasks.hpp" +#include "enums/player_blessings.hpp" +#include "enums/player_cyclopedia.hpp" #include "game/game.hpp" #include "kv/kv.hpp" -#include "enums/player_blessings.hpp" - PlayerCyclopedia::PlayerCyclopedia(Player &player) : m_player(player) { } diff --git a/src/creatures/players/cyclopedia/player_title.cpp b/src/creatures/players/cyclopedia/player_title.cpp index 5bbe8252fba..089d02f281c 100644 --- a/src/creatures/players/cyclopedia/player_title.cpp +++ b/src/creatures/players/cyclopedia/player_title.cpp @@ -7,9 +7,12 @@ * Website: https://docs.opentibiabr.com/ */ -#include "player_title.hpp" +#include "creatures/players/cyclopedia/player_title.hpp" +#include "creatures/appearance/mounts/mounts.hpp" +#include "creatures/monsters/monsters.hpp" #include "creatures/players/player.hpp" +#include "enums/account_group_type.hpp" #include "game/game.hpp" #include "kv/kv.hpp" #include "enums/account_group_type.hpp" @@ -183,7 +186,7 @@ bool PlayerTitle::checkGold(uint32_t amount) const { bool PlayerTitle::checkMount(uint32_t amount) const { uint8_t total = 0; - for (const auto &mount : g_game().mounts.getMounts()) { + for (const auto &mount : g_game().mounts->getMounts()) { if (m_player.hasMount(mount)) { total++; } diff --git a/src/creatures/players/cyclopedia/player_title.hpp b/src/creatures/players/cyclopedia/player_title.hpp index c3098f80159..72362424ea2 100644 --- a/src/creatures/players/cyclopedia/player_title.hpp +++ b/src/creatures/players/cyclopedia/player_title.hpp @@ -9,9 +9,10 @@ #pragma once -#include "creatures/creatures_definitions.hpp" #include "enums/player_cyclopedia.hpp" +enum PlayerSex_t : uint8_t; + class Player; class KV; diff --git a/src/creatures/players/grouping/familiars.cpp b/src/creatures/players/grouping/familiars.cpp index fba87d88678..66e9b41bc35 100644 --- a/src/creatures/players/grouping/familiars.cpp +++ b/src/creatures/players/grouping/familiars.cpp @@ -8,10 +8,14 @@ */ #include "creatures/players/grouping/familiars.hpp" -#include "lib/di/container.hpp" + #include "config/configmanager.hpp" +#include "lib/di/container.hpp" #include "utils/pugicast.hpp" #include "utils/tools.hpp" +#include + +std::vector> familiars[VOCATION_LAST + 1]; Familiars &Familiars::getInstance() { return inject(); @@ -24,6 +28,10 @@ bool Familiars::reload() { return loadFromXml(); } +std::vector> &Familiars::getFamiliars(uint16_t vocation) { + return familiars[vocation]; +} + bool Familiars::loadFromXml() { pugi::xml_document doc; const auto folder = g_configManager().getString(CORE_DIRECTORY) + "/XML/familiars.xml"; diff --git a/src/creatures/players/grouping/familiars.hpp b/src/creatures/players/grouping/familiars.hpp index e00e1fb8884..502874747ef 100644 --- a/src/creatures/players/grouping/familiars.hpp +++ b/src/creatures/players/grouping/familiars.hpp @@ -9,8 +9,6 @@ #pragma once -#include "declarations.hpp" - struct FamiliarEntry { constexpr explicit FamiliarEntry(uint16_t initLookType) : lookType(initLookType) { } @@ -37,12 +35,7 @@ class Familiars { bool loadFromXml(); bool reload(); - std::vector> &getFamiliars(uint16_t vocation) { - return familiars[vocation]; - } + std::vector> &getFamiliars(uint16_t vocation); [[nodiscard]] std::shared_ptr getFamiliarByLookType(uint16_t vocation, uint16_t lookType) const; - -private: - std::vector> familiars[VOCATION_LAST + 1]; }; diff --git a/src/creatures/players/grouping/groups.cpp b/src/creatures/players/grouping/groups.cpp index c6788dc6a13..dc2607f46b9 100644 --- a/src/creatures/players/grouping/groups.cpp +++ b/src/creatures/players/grouping/groups.cpp @@ -7,9 +7,10 @@ * Website: https://docs.opentibiabr.com/ */ +#include "creatures/players/grouping/groups.hpp" + #include "config/configmanager.hpp" #include "game/game.hpp" -#include "creatures/players/grouping/groups.hpp" #include "utils/pugicast.hpp" #include "utils/tools.hpp" @@ -106,3 +107,7 @@ std::shared_ptr Groups::getGroup(uint16_t id) const { } return nullptr; } + +std::vector> &Groups::getGroups() { + return groups_vector; +} diff --git a/src/creatures/players/grouping/groups.hpp b/src/creatures/players/grouping/groups.hpp index af319e95772..91328a0e641 100644 --- a/src/creatures/players/grouping/groups.hpp +++ b/src/creatures/players/grouping/groups.hpp @@ -9,7 +9,7 @@ #pragma once -#include "declarations.hpp" +#include "utils/utils_definitions.hpp" struct Group { std::string name; @@ -27,9 +27,7 @@ class Groups { static bool reload(); bool load(); [[nodiscard]] std::shared_ptr getGroup(uint16_t id) const; - std::vector> &getGroups() { - return groups_vector; - } + std::vector> &getGroups(); private: std::vector> groups_vector; diff --git a/src/creatures/players/grouping/guild.cpp b/src/creatures/players/grouping/guild.cpp index a9bac7989ac..b00f3a264c1 100644 --- a/src/creatures/players/grouping/guild.cpp +++ b/src/creatures/players/grouping/guild.cpp @@ -8,6 +8,7 @@ */ #include "creatures/players/grouping/guild.hpp" + #include "game/game.hpp" void Guild::addMember(const std::shared_ptr &player) { diff --git a/src/creatures/players/grouping/party.cpp b/src/creatures/players/grouping/party.cpp index 0098236bfa8..e63ca14b629 100644 --- a/src/creatures/players/grouping/party.cpp +++ b/src/creatures/players/grouping/party.cpp @@ -8,10 +8,15 @@ */ #include "creatures/players/grouping/party.hpp" + +#include "config/configmanager.hpp" +#include "creatures/creature.hpp" +#include "creatures/players/player.hpp" #include "game/game.hpp" -#include "lua/creature/events.hpp" +#include "game/movement/position.hpp" #include "lua/callbacks/event_callback.hpp" #include "lua/callbacks/events_callbacks.hpp" +#include "lua/creature/events.hpp" std::shared_ptr Party::create(const std::shared_ptr &leader) { auto party = std::make_shared(); @@ -23,6 +28,39 @@ std::shared_ptr Party::create(const std::shared_ptr &leader) { return party; } +std::shared_ptr Party::getParty() { + return static_self_cast(); +} + +std::shared_ptr Party::getLeader() const { + return m_leader.lock(); +} + +std::vector> Party::getPlayers() const { + std::vector> players; + for (auto &member : memberList) { + players.push_back(member); + } + players.push_back(getLeader()); + return players; +} + +std::vector> Party::getMembers() { + return memberList; +} + +std::vector> Party::getInvitees() { + return inviteList; +} + +size_t Party::getMemberCount() const { + return memberList.size(); +} + +size_t Party::getInvitationCount() const { + return inviteList.size(); +} + void Party::disband() { if (!g_events().eventPartyOnDisband(getParty())) { return; @@ -388,6 +426,10 @@ void Party::broadcastPartyMessage(MessageClasses msgClass, const std::string &ms } } +bool Party::empty() const { + return memberList.empty() && inviteList.empty(); +} + void Party::updateSharedExperience() { if (sharedExpActive) { const bool result = getSharedExperienceStatus() == SHAREDEXP_OK; @@ -443,8 +485,16 @@ bool Party::setSharedExperience(const std::shared_ptr &player, bool newS return true; } +bool Party::isSharedExperienceActive() const { + return sharedExpActive; +} + +bool Party::isSharedExperienceEnabled() const { + return sharedExpEnabled; +} + void Party::shareExperience(uint64_t experience, const std::shared_ptr &target /* = nullptr*/) { - const auto &leader = getLeader(); + auto leader = getLeader(); if (!leader) { return; } @@ -853,3 +903,18 @@ void Party::reloadPrices() const { } } } + +std::shared_ptr Party::getPlayerPartyAnalyzerStruct(uint32_t playerId) const { + if (auto it = std::ranges::find_if(membersData, [playerId](const std::shared_ptr &preyIt) { + return preyIt->id == playerId; + }); + it != membersData.end()) { + return *it; + } + + return nullptr; +} + +uint32_t Party::getAnalyzerTimeNow() const { + return static_cast(time(nullptr) - trackerTime); +} diff --git a/src/creatures/players/grouping/party.hpp b/src/creatures/players/grouping/party.hpp index 4c96c6a8824..cef450c0c77 100644 --- a/src/creatures/players/grouping/party.hpp +++ b/src/creatures/players/grouping/party.hpp @@ -9,9 +9,9 @@ #pragma once -#include "creatures/players/player.hpp" -#include "creatures/monsters/monsters.hpp" -#include "lib/di/container.hpp" +#include "creatures/creatures_definitions.hpp" + +enum MessageClasses : uint8_t; enum SharedExpStatus_t : uint8_t { SHAREDEXP_OK, @@ -21,40 +21,25 @@ enum SharedExpStatus_t : uint8_t { SHAREDEXP_EMPTYPARTY }; +struct Position; + class Player; class Party; +class Item; +class Creature; class Party final : public SharedObject { public: static std::shared_ptr create(const std::shared_ptr &leader); - std::shared_ptr getParty() { - return static_self_cast(); - } - - std::shared_ptr getLeader() const { - return m_leader.lock(); - } - std::vector> getPlayers() const { - std::vector> players; - for (auto &member : memberList) { - players.emplace_back(member); - } - players.emplace_back(getLeader()); - return players; - } - std::vector> getMembers() { - return memberList; - } - std::vector> getInvitees() { - return inviteList; - } - size_t getMemberCount() const { - return memberList.size(); - } - size_t getInvitationCount() const { - return inviteList.size(); - } + std::shared_ptr getParty(); + + std::shared_ptr getLeader() const; + std::vector> getPlayers() const; + std::vector> getMembers(); + std::vector> getInvitees(); + size_t getMemberCount() const; + size_t getInvitationCount() const; void disband(); bool invitePlayer(const std::shared_ptr &player); @@ -68,19 +53,13 @@ class Party final : public SharedObject { bool isPlayerInvited(const std::shared_ptr &player) const; void updateAllPartyIcons(); void broadcastPartyMessage(MessageClasses msgClass, const std::string &msg, bool sendToInvitations = false); - bool empty() const { - return memberList.empty() && inviteList.empty(); - } + bool empty() const; bool canOpenCorpse(uint32_t ownerId) const; void shareExperience(uint64_t experience, const std::shared_ptr &target = nullptr); bool setSharedExperience(const std::shared_ptr &player, bool sharedExpActive, bool silent = false); - bool isSharedExperienceActive() const { - return sharedExpActive; - } - bool isSharedExperienceEnabled() const { - return sharedExpEnabled; - } + bool isSharedExperienceActive() const; + bool isSharedExperienceEnabled() const; bool canUseSharedExperience(const std::shared_ptr &player); SharedExpStatus_t getMemberSharedExperienceStatus(const std::shared_ptr &player); void updateSharedExperience(); @@ -104,20 +83,9 @@ class Party final : public SharedObject { void resetAnalyzer(); void reloadPrices() const; - std::shared_ptr getPlayerPartyAnalyzerStruct(uint32_t playerId) const { - if (auto it = std::ranges::find_if(membersData, [playerId](const std::shared_ptr &preyIt) { - return preyIt->id == playerId; - }); - it != membersData.end()) { - return *it; - } - - return nullptr; - } + std::shared_ptr getPlayerPartyAnalyzerStruct(uint32_t playerId) const; - uint32_t getAnalyzerTimeNow() const { - return static_cast(time(nullptr) - trackerTime); - } + uint32_t getAnalyzerTimeNow() const; public: // Party analyzer diff --git a/src/creatures/players/imbuements/imbuements.cpp b/src/creatures/players/imbuements/imbuements.cpp index 4fe13bc5ad7..c71a56fd588 100644 --- a/src/creatures/players/imbuements/imbuements.cpp +++ b/src/creatures/players/imbuements/imbuements.cpp @@ -8,8 +8,17 @@ */ #include "creatures/players/imbuements/imbuements.hpp" -#include "lua/creature/events.hpp" + +#include "config/configmanager.hpp" +#include "creatures/players/player.hpp" +#include "items/item.hpp" +#include "lib/di/container.hpp" #include "utils/pugicast.hpp" +#include + +Imbuements &Imbuements::getInstance() { + return inject(); +} Imbuement* Imbuements::getImbuement(uint16_t id) { if (id == 0) { @@ -368,3 +377,43 @@ std::vector Imbuements::getImbuements(const std::shared_ptr return imbuements; } + +uint16_t Imbuement::getID() const { + return id; +} + +uint16_t Imbuement::getBaseID() const { + return baseid; +} + +uint32_t Imbuement::getStorage() const { + return storage; +} + +bool Imbuement::isPremium() const { + return premium; +} + +std::string Imbuement::getName() const { + return name; +} + +std::string Imbuement::getDescription() const { + return description; +} + +std::string Imbuement::getSubGroup() const { + return subgroup; +} + +uint16_t Imbuement::getCategory() const { + return category; +} + +const std::vector> &Imbuement::getItems() const { + return items; +} + +uint16_t Imbuement::getIconID() const { + return icon + (baseid - 1); +} diff --git a/src/creatures/players/imbuements/imbuements.hpp b/src/creatures/players/imbuements/imbuements.hpp index c89e3f80df0..b2429cc8137 100644 --- a/src/creatures/players/imbuements/imbuements.hpp +++ b/src/creatures/players/imbuements/imbuements.hpp @@ -9,9 +9,7 @@ #pragma once -#include "creatures/players/player.hpp" -#include "declarations.hpp" -#include "lib/di/container.hpp" +#include "creatures/creatures_definitions.hpp" class Player; class Item; @@ -53,9 +51,7 @@ class Imbuements { Imbuements(const Imbuements &) = delete; Imbuements &operator=(const Imbuements &) = delete; - static Imbuements &getInstance() { - return inject(); - } + static Imbuements &getInstance(); Imbuement* getImbuement(uint16_t id); @@ -83,43 +79,23 @@ class Imbuement { Imbuement(uint16_t initId, uint16_t initBaseId) : id(initId), baseid(initBaseId) { } - uint16_t getID() const { - return id; - } + uint16_t getID() const; - uint16_t getBaseID() const { - return baseid; - } + uint16_t getBaseID() const; - uint32_t getStorage() const { - return storage; - } + uint32_t getStorage() const; - bool isPremium() const { - return premium; - } - std::string getName() const { - return name; - } - std::string getDescription() const { - return description; - } + bool isPremium() const; + std::string getName() const; + std::string getDescription() const; - std::string getSubGroup() const { - return subgroup; - } + std::string getSubGroup() const; - uint16_t getCategory() const { - return category; - } + uint16_t getCategory() const; - const std::vector> &getItems() const { - return items; - } + const std::vector> &getItems() const; - uint16_t getIconID() const { - return icon + (baseid - 1); - } + uint16_t getIconID() const; uint16_t icon = 1; int32_t stats[maxSkillOrStatId + 1] = {}; @@ -137,9 +113,11 @@ class Imbuement { friend class Item; private: - bool premium = false; - uint32_t storage = 0; - uint16_t id, baseid, category = 0; + bool premium {}; + uint32_t storage {}; + uint16_t id {}; + uint16_t baseid {}; + uint16_t category {}; std::string name; std::string description; std::string subgroup; diff --git a/src/creatures/players/management/ban.cpp b/src/creatures/players/management/ban.cpp index 0a4efcd2559..8ed990af72c 100644 --- a/src/creatures/players/management/ban.cpp +++ b/src/creatures/players/management/ban.cpp @@ -8,6 +8,7 @@ */ #include "creatures/players/management/ban.hpp" + #include "database/database.hpp" #include "database/databasetasks.hpp" #include "utils/tools.hpp" diff --git a/src/creatures/players/management/waitlist.cpp b/src/creatures/players/management/waitlist.cpp index 374d7329855..eceb8b70810 100644 --- a/src/creatures/players/management/waitlist.cpp +++ b/src/creatures/players/management/waitlist.cpp @@ -8,7 +8,12 @@ */ #include "creatures/players/management/waitlist.hpp" + +#include "config/configmanager.hpp" +#include "creatures/players/player.hpp" #include "game/game.hpp" +#include "lib/di/container.hpp" +#include "utils/tools.hpp" #include "enums/account_type.hpp" diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 3846d9ca99b..5dfb5fa4712 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -7,46 +7,64 @@ * Website: https://docs.opentibiabr.com/ */ +#include "creatures/players/player.hpp" + +#include "config/configmanager.hpp" +#include "core.hpp" +#include "creatures/appearance/mounts/mounts.hpp" #include "creatures/combat/combat.hpp" +#include "creatures/combat/condition.hpp" #include "creatures/interactions/chat.hpp" #include "creatures/monsters/monster.hpp" #include "creatures/monsters/monsters.hpp" -#include "creatures/players/player.hpp" +#include "creatures/npcs/npc.hpp" #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/wheel/wheel_gems.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" #include "creatures/players/cyclopedia/player_cyclopedia.hpp" #include "creatures/players/cyclopedia/player_title.hpp" +#include "creatures/players/grouping/party.hpp" +#include "creatures/players/imbuements/imbuements.hpp" #include "creatures/players/storages/storages.hpp" +#include "creatures/players/vip/player_vip.hpp" +#include "creatures/players/wheel/player_wheel.hpp" +#include "server/network/protocol/protocolgame.hpp" +#include "enums/account_errors.hpp" +#include "enums/account_group_type.hpp" +#include "enums/account_type.hpp" +#include "enums/object_category.hpp" +#include "enums/player_blessings.hpp" +#include "enums/player_icons.hpp" #include "game/game.hpp" #include "game/modal_window/modal_window.hpp" #include "game/scheduling/dispatcher.hpp" -#include "game/scheduling/task.hpp" #include "game/scheduling/save_manager.hpp" +#include "game/scheduling/task.hpp" #include "grouping/familiars.hpp" -#include "lua/creature/creatureevent.hpp" -#include "lua/creature/events.hpp" -#include "lua/callbacks/event_callback.hpp" -#include "lua/callbacks/events_callbacks.hpp" -#include "lua/creature/movement.hpp" +#include "grouping/guild.hpp" +#include "io/iobestiary.hpp" #include "io/iologindata.hpp" +#include "io/ioprey.hpp" #include "items/bed.hpp" -#include "map/town.hpp" +#include "items/containers/depot/depotchest.hpp" +#include "items/containers/depot/depotlocker.hpp" +#include "items/containers/rewards/reward.hpp" +#include "items/containers/rewards/rewardchest.hpp" +#include "items/items_classification.hpp" #include "items/weapons/weapons.hpp" -#include "core.hpp" -#include "map/spectators.hpp" #include "lib/metrics/metrics.hpp" - -#include "enums/object_category.hpp" -#include "enums/account_errors.hpp" -#include "enums/account_type.hpp" -#include "enums/account_group_type.hpp" -#include "enums/player_blessings.hpp" +#include "lua/callbacks/event_callback.hpp" +#include "lua/callbacks/events_callbacks.hpp" +#include "lua/creature/actions.hpp" +#include "lua/creature/creatureevent.hpp" +#include "lua/creature/events.hpp" +#include "lua/creature/movement.hpp" +#include "map/spectators.hpp" MuteCountMap Player::muteCountMap; -Player::Player(ProtocolGame_ptr p) : +Player::Player(std::shared_ptr p) : lastPing(OTSYS_TIME()), lastPong(lastPing), inbox(std::make_shared(ITEM_INBOX)), @@ -92,6 +110,10 @@ bool Player::setVocation(uint16_t vocId) { return true; } +uint16_t Player::getVocationId() const { + return vocation->getId(); +} + bool Player::isPushable() { if (hasFlag(PlayerFlags_t::CannotBePushed)) { return false; @@ -231,6 +253,18 @@ std::shared_ptr Player::getInventoryItem(Slots_t slot) const { return inventory[slot]; } +bool Player::isItemAbilityEnabled(Slots_t slot) const { + return inventoryAbilities[slot]; +} + +void Player::setItemAbility(Slots_t slot, bool enabled) { + inventoryAbilities[slot] = enabled; +} + +void Player::setVarSkill(skills_t skill, int32_t modifier) { + varSkills[skill] += modifier; +} + bool Player::isSuppress(ConditionType_t conditionType, bool attackerPlayer) const { auto minDelay = g_configManager().getNumber(MIN_DELAY_BETWEEN_CONDITIONS); if (IsConditionSuppressible(conditionType) && checkLastConditionTimeWithin(conditionType, minDelay)) { @@ -468,6 +502,18 @@ float Player::getDefenseFactor() const { } } +void Player::setLastWalkthroughAttempt(int64_t walkthroughAttempt) { + lastWalkthroughAttempt = walkthroughAttempt; +} + +void Player::setLastWalkthroughPosition(Position walkthroughPosition) { + lastWalkthroughPosition = walkthroughPosition; +} + +std::shared_ptr Player::getInbox() const { + return inbox; +} + std::unordered_set Player::getClientIcons() { std::unordered_set icons; @@ -500,6 +546,10 @@ std::unordered_set Player::getClientIcons() { return icons; } +const std::unordered_set> &Player::getCyclopediaMonsterTrackerSet(bool isBoss) const { + return isBoss ? m_bosstiaryMonsterTracker : m_bestiaryMonsterTracker; +} + void Player::addMonsterToCyclopediaTrackerList(const std::shared_ptr &mtype, bool isBoss, bool reloadClient /* = false */) { if (!client) { return; @@ -541,10 +591,79 @@ void Player::removeMonsterFromCyclopediaTrackerList(const std::shared_ptrsendBestiaryEntryChanged(raceid); + } +} + +void Player::refreshCyclopediaMonsterTracker(const std::unordered_set> &trackerList, bool isBoss) const { + if (client) { + client->refreshCyclopediaMonsterTracker(trackerList, isBoss); + } +} + bool Player::isBossOnBosstiaryTracker(const std::shared_ptr &monsterType) const { return monsterType ? m_bosstiaryMonsterTracker.contains(monsterType) : false; } +std::shared_ptr Player::getVocation() const { + return vocation; +} + +OperatingSystem_t Player::getOperatingSystem() const { + return operatingSystem; +} + +void Player::setOperatingSystem(OperatingSystem_t clientos) { + operatingSystem = clientos; +} + +bool Player::isOldProtocol() const { + return client && client->oldProtocol; +} + +uint32_t Player::getProtocolVersion() const { + if (!client) { + return 0; + } + + return client->getVersion(); +} + +bool Player::hasSecureMode() const { + return secureMode; +} + +void Player::setParty(std::shared_ptr newParty) { + m_party = std::move(newParty); +} + +std::shared_ptr Player::getParty() const { + return m_party; +} + +int32_t Player::getCleavePercent(bool useCharges) const { + int32_t result = cleavePercent; + for (const auto &item : getEquippedItems()) { + const ItemType &it = Item::items[item->getID()]; + if (!it.abilities) { + continue; + } + + const int32_t &cleave_percent = it.abilities->cleavePercent; + if (cleave_percent != 0) { + result += cleave_percent; + const uint16_t charges = item->getCharges(); + if (useCharges && charges != 0) { + g_game().transformItem(item, item->getID(), charges - 1); + } + } + } + + return result; +} + void Player::updateInventoryWeight() { if (hasFlag(PlayerFlags_t::HasInfiniteCapacity)) { return; @@ -630,16 +749,6 @@ phmap::flat_hash_map> Player::getAllSlotItems() c return itemMap; } -void Player::setTraining(bool value) { - for (const auto &[key, player] : g_game().getPlayers()) { - if (!this->isInGhostMode() || player->isAccessPlayer()) { - player->vip()->notifyStatusChange(static_self_cast(), value ? VipStatus_t::Training : VipStatus_t::Online, false); - } - } - vip()->setStatus(VipStatus_t::Training); - setExerciseTraining(value); -} - uint16_t Player::getLoyaltySkill(skills_t skill) const { uint16_t level = getBaseSkill(skill); absl::uint128 currReqTries = vocation->getReqSkillTries(skill, level); @@ -667,6 +776,69 @@ uint16_t Player::getLoyaltySkill(skills_t skill) const { return level; } +uint16_t Player::getBaseSkill(uint8_t skill) const { + return skills[skill].level; +} + +double_t Player::getSkillPercent(skills_t skill) const { + return skills[skill].percent; +} + +bool Player::getAddAttackSkill() const { + return addAttackSkillPoint; +} + +BlockType_t Player::getLastAttackBlockType() const { + return lastAttackBlockType; +} + +uint64_t Player::getLastConditionTime(ConditionType_t type) const { + if (!lastConditionTime.contains(static_cast(type))) { + return 0; + } + return lastConditionTime.at(static_cast(type)); +} + +void Player::updateLastConditionTime(ConditionType_t type) { + lastConditionTime[static_cast(type)] = OTSYS_TIME(); +} + +bool Player::checkLastConditionTimeWithin(ConditionType_t type, uint32_t interval) const { + if (!lastConditionTime.contains(static_cast(type))) { + return false; + } + const auto last = lastConditionTime.at(static_cast(type)); + return last > 0 && ((OTSYS_TIME() - last) < interval); +} + +uint64_t Player::getLastAttack() const { + return lastAttack; +} + +bool Player::checkLastAttackWithin(uint32_t interval) const { + return lastAttack > 0 && ((OTSYS_TIME() - lastAttack) < interval); +} + +void Player::updateLastAttack() { + if (lastAttack == 0) { + lastAttack = OTSYS_TIME() - getAttackSpeed() - 1; + return; + } + lastAttack = OTSYS_TIME(); +} + +uint64_t Player::getLastAggressiveAction() const { + return lastAggressiveAction; +} + +bool Player::checkLastAggressiveActionWithin(uint32_t interval) const { + return lastAggressiveAction > 0 && ((OTSYS_TIME() - lastAggressiveAction) < interval); +} + +void Player::updateLastAggressiveAction() { + lastAggressiveAction = OTSYS_TIME(); +} + void Player::addSkillAdvance(skills_t skill, uint64_t count) { uint64_t currReqTries = vocation->getReqSkillTries(skill, skills[skill].level); uint64_t nextReqTries = vocation->getReqSkillTries(skill, skills[skill].level + 1); @@ -936,6 +1108,10 @@ void Player::addStorageValueByName(const std::string &storageName, const int32_t addStorageValue(key, value, isLogin); } +std::shared_ptr Player::kv() const { + return g_kv().scoped("player")->scoped(fmt::format("{}", getGUID())); +} + bool Player::canSee(const Position &pos) { if (!client) { return false; @@ -1033,6 +1209,67 @@ bool Player::canWalkthroughEx(const std::shared_ptr &creature) const { } } +RaceType_t Player::getRace() const { + return RACE_BLOOD; +} + +uint64_t Player::getMoney() const { + uint64_t moneyCount = 0; + + auto countMoneyInContainer = [&moneyCount](const auto &self, const std::shared_ptr &container) -> void { + for (const auto &item : container->getItemList()) { + if (const auto &tmpContainer = item->getContainer()) { + self(self, tmpContainer); + } else { + moneyCount += item->getWorth(); + } + } + }; + + for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) { + const auto &item = inventory[i]; + if (!item) { + continue; + } + + if (const auto &container = item->getContainer()) { + countMoneyInContainer(countMoneyInContainer, container); + } else { + moneyCount += item->getWorth(); + } + } + + return moneyCount; +} + +std::pair Player::getForgeSliversAndCores() const { + uint64_t sliverCount = 0; + uint64_t coreCount = 0; + + // Check items from inventory + for (const auto &item : getAllInventoryItems()) { + if (!item) { + continue; + } + + sliverCount += item->getForgeSlivers(); + coreCount += item->getForgeCores(); + } + + // Check items from stash + for (const auto &stashToSend = getStashItems(); + const auto &[itemId, itemCount] : stashToSend) { + if (itemId == ITEM_FORGE_SLIVER) { + sliverCount += itemCount; + } + if (itemId == ITEM_FORGE_CORE) { + coreCount += itemCount; + } + } + + return std::make_pair(sliverCount, coreCount); +} + void Player::onReceiveMail() { if (isNearDepotBox()) { sendTextMessage(MESSAGE_EVENT_ADVANCE, "New mail has arrived."); @@ -1178,6 +1415,21 @@ void Player::setMainBackpackUnassigned(const std::shared_ptr &contain } } +bool Player::updateKillTracker(const std::shared_ptr &corpse, const std::string &playerName, const Outfit_t &creatureOutfit) const { + if (client) { + client->sendKillTrackerUpdate(corpse, playerName, creatureOutfit); + return true; + } + + return false; +} + +void Player::updatePartyTrackerAnalyzer() const { + if (client && m_party) { + client->updatePartyTrackerAnalyzer(m_party); + } +} + void Player::sendLootStats(const std::shared_ptr &item, uint8_t count) { uint64_t value = 0; if (item->getID() == ITEM_GOLD_COIN || item->getID() == ITEM_PLATINUM_COIN || item->getID() == ITEM_CRYSTAL_COIN) { @@ -1205,45 +1457,298 @@ void Player::sendLootStats(const std::shared_ptr &item, uint8_t count) { } } -bool Player::isNearDepotBox() { - const Position &pos = getPosition(); - for (int32_t cx = -1; cx <= 1; ++cx) { - for (int32_t cy = -1; cy <= 1; ++cy) { - const auto &posTile = g_game().map.getTile(static_cast(pos.x + cx), static_cast(pos.y + cy), pos.z); - if (!posTile) { - continue; - } +void Player::updateSupplyTracker(const std::shared_ptr &item) { + const auto &iType = Item::items.getItemType(item->getID()); + const auto value = iType.buyPrice; + g_metrics().addCounter("player_supply", value, { { "player", getName() } }); - if (posTile->hasFlag(TILESTATE_DEPOT)) { - return true; - } - } + if (client) { + client->sendUpdateSupplyTracker(item); } - return false; -} -std::shared_ptr Player::getDepotChest(uint32_t depotId, bool autoCreate) { - const auto it = depotChests.find(depotId); - if (it != depotChests.end()) { - return it->second; + if (m_party) { + m_party->addPlayerSupply(getPlayer(), item); } +} - if (!autoCreate) { - return nullptr; +void Player::updateImpactTracker(CombatType_t type, int32_t amount) const { + if (client) { + client->sendUpdateImpactTracker(type, amount); } +} - std::shared_ptr depotChest; - if (depotId > 0 && depotId < 18) { - depotChest = std::make_shared(ITEM_DEPOT_NULL + depotId); - } else if (depotId == 18) { - depotChest = std::make_shared(ITEM_DEPOT_XVIII); - } else if (depotId == 19) { - depotChest = std::make_shared(ITEM_DEPOT_XIX); - } else { - depotChest = std::make_shared(ITEM_DEPOT_XX); +void Player::updateInputAnalyzer(CombatType_t type, int32_t amount, const std::string &target) const { + if (client) { + client->sendUpdateInputAnalyzer(type, amount, target); } +} - depotChests[depotId] = depotChest; +void Player::createLeaderTeamFinder(NetworkMessage &msg) const { + if (client) { + client->createLeaderTeamFinder(msg); + } +} + +void Player::sendLeaderTeamFinder(bool reset) const { + if (client) { + client->sendLeaderTeamFinder(reset); + } +} + +void Player::sendTeamFinderList() const { + if (client) { + client->sendTeamFinderList(); + } +} + +void Player::sendCreatureHelpers(uint32_t creatureId, uint16_t helpers) const { + if (client) { + client->sendCreatureHelpers(creatureId, helpers); + } +} + +void Player::setItemCustomPrice(uint16_t itemId, uint64_t price) { + itemPriceMap[itemId] = price; +} + +uint32_t Player::getCharmPoints() const { + return charmPoints; +} + +void Player::setCharmPoints(uint32_t points) { + charmPoints = points; +} + +bool Player::hasCharmExpansion() const { + return charmExpansion; +} + +void Player::setCharmExpansion(bool onOff) { + charmExpansion = onOff; +} + +void Player::setUsedRunesBit(int32_t bit) { + UsedRunesBit = bit; +} + +int32_t Player::getUsedRunesBit() const { + return UsedRunesBit; +} + +void Player::setUnlockedRunesBit(int32_t bit) { + UnlockedRunesBit = bit; +} + +int32_t Player::getUnlockedRunesBit() const { + return UnlockedRunesBit; +} + +void Player::setImmuneCleanse(ConditionType_t conditiontype) { + cleanseCondition.first = conditiontype; + cleanseCondition.second = OTSYS_TIME() + 10000; +} + +bool Player::isImmuneCleanse(ConditionType_t conditiontype) const { + const uint64_t timenow = OTSYS_TIME(); + if ((cleanseCondition.first == conditiontype) + && (timenow <= cleanseCondition.second)) { + return true; + } + return false; +} + +void Player::setImmuneFear() { + m_fearCondition.first = CONDITION_FEARED; + m_fearCondition.second = OTSYS_TIME() + 10000; +} + +bool Player::isImmuneFear() const { + const uint64_t timenow = OTSYS_TIME(); + return (m_fearCondition.first == CONDITION_FEARED) && (timenow <= m_fearCondition.second); +} + +uint16_t Player::parseRacebyCharm(charmRune_t charmId, bool set, uint16_t newRaceid) { + uint16_t raceid = 0; + switch (charmId) { + case CHARM_WOUND: + if (set) { + charmRuneWound = newRaceid; + } else { + raceid = charmRuneWound; + } + break; + case CHARM_ENFLAME: + if (set) { + charmRuneEnflame = newRaceid; + } else { + raceid = charmRuneEnflame; + } + break; + case CHARM_POISON: + if (set) { + charmRunePoison = newRaceid; + } else { + raceid = charmRunePoison; + } + break; + case CHARM_FREEZE: + if (set) { + charmRuneFreeze = newRaceid; + } else { + raceid = charmRuneFreeze; + } + break; + case CHARM_ZAP: + if (set) { + charmRuneZap = newRaceid; + } else { + raceid = charmRuneZap; + } + break; + case CHARM_CURSE: + if (set) { + charmRuneCurse = newRaceid; + } else { + raceid = charmRuneCurse; + } + break; + case CHARM_CRIPPLE: + if (set) { + charmRuneCripple = newRaceid; + } else { + raceid = charmRuneCripple; + } + break; + case CHARM_PARRY: + if (set) { + charmRuneParry = newRaceid; + } else { + raceid = charmRuneParry; + } + break; + case CHARM_DODGE: + if (set) { + charmRuneDodge = newRaceid; + } else { + raceid = charmRuneDodge; + } + break; + case CHARM_ADRENALINE: + if (set) { + charmRuneAdrenaline = newRaceid; + } else { + raceid = charmRuneAdrenaline; + } + break; + case CHARM_NUMB: + if (set) { + charmRuneNumb = newRaceid; + } else { + raceid = charmRuneNumb; + } + break; + case CHARM_CLEANSE: + if (set) { + charmRuneCleanse = newRaceid; + } else { + raceid = charmRuneCleanse; + } + break; + case CHARM_BLESS: + if (set) { + charmRuneBless = newRaceid; + } else { + raceid = charmRuneBless; + } + break; + case CHARM_SCAVENGE: + if (set) { + charmRuneScavenge = newRaceid; + } else { + raceid = charmRuneScavenge; + } + break; + case CHARM_GUT: + if (set) { + charmRuneGut = newRaceid; + } else { + raceid = charmRuneGut; + } + break; + case CHARM_LOW: + if (set) { + charmRuneLowBlow = newRaceid; + } else { + raceid = charmRuneLowBlow; + } + break; + case CHARM_DIVINE: + if (set) { + charmRuneDivine = newRaceid; + } else { + raceid = charmRuneDivine; + } + break; + case CHARM_VAMP: + if (set) { + charmRuneVamp = newRaceid; + } else { + raceid = charmRuneVamp; + } + break; + case CHARM_VOID: + if (set) { + charmRuneVoid = newRaceid; + } else { + raceid = charmRuneVoid; + } + break; + default: + raceid = 0; + break; + } + return raceid; +} + +bool Player::isNearDepotBox() { + const Position &pos = getPosition(); + for (int32_t cx = -1; cx <= 1; ++cx) { + for (int32_t cy = -1; cy <= 1; ++cy) { + const auto &posTile = g_game().map.getTile(static_cast(pos.x + cx), static_cast(pos.y + cy), pos.z); + if (!posTile) { + continue; + } + + if (posTile->hasFlag(TILESTATE_DEPOT)) { + return true; + } + } + } + return false; +} + +std::shared_ptr Player::getDepotChest(uint32_t depotId, bool autoCreate) { + const auto it = depotChests.find(depotId); + if (it != depotChests.end()) { + return it->second; + } + + if (!autoCreate) { + return nullptr; + } + + std::shared_ptr depotChest; + if (depotId > 0 && depotId < 18) { + depotChest = std::make_shared(ITEM_DEPOT_NULL + depotId); + } else if (depotId == 18) { + depotChest = std::make_shared(ITEM_DEPOT_XVIII); + } else if (depotId == 19) { + depotChest = std::make_shared(ITEM_DEPOT_XIX); + } else { + depotChest = std::make_shared(ITEM_DEPOT_XX); + } + + depotChests[depotId] = depotChest; return depotChest; } @@ -1335,98 +1840,92 @@ std::vector> Player::getRewardsFromContainer(const std::sh return rewardItemsVector; } +void Player::sendCancelMessage(const std::string &msg) const { + if (client) { + client->sendTextMessage(TextMessage(MESSAGE_FAILURE, msg)); + } +} + void Player::sendCancelMessage(ReturnValue message) const { sendCancelMessage(getReturnMessage(message)); } -void Player::sendStats() { +void Player::sendCancelTarget() const { if (client) { - client->sendStats(); - lastStatsTrainingTime = getOfflineTrainingTime() / 60 / 1000; + client->sendCancelTarget(); } } -void Player::updateSupplyTracker(const std::shared_ptr &item) { - const auto &iType = Item::items.getItemType(item->getID()); - const auto value = iType.buyPrice; - g_metrics().addCounter("player_supply", value, { { "player", getName() } }); - +void Player::sendCancelWalk() const { if (client) { - client->sendUpdateSupplyTracker(item); + client->sendCancelWalk(); } +} - if (m_party) { - m_party->addPlayerSupply(getPlayer(), item); +void Player::sendChangeSpeed(const std::shared_ptr &creature, uint16_t newSpeed) const { + if (client) { + client->sendChangeSpeed(creature, newSpeed); } } -void Player::updateImpactTracker(CombatType_t type, int32_t amount) const { +void Player::sendCreatureHealth(const std::shared_ptr &creature) const { if (client) { - client->sendUpdateImpactTracker(type, amount); + client->sendCreatureHealth(creature); } } -void Player::sendPing() { - const int64_t timeNow = OTSYS_TIME(); - - bool hasLostConnection = false; - if ((timeNow - lastPing) >= 5000) { - lastPing = timeNow; - if (client) { - client->sendPing(); - } else { - hasLostConnection = true; - } +void Player::sendPartyCreatureUpdate(const std::shared_ptr &creature) const { + if (client) { + client->sendPartyCreatureUpdate(creature); } +} - const int64_t noPongTime = timeNow - lastPong; - const auto &attackedCreature = getAttackedCreature(); - if ((hasLostConnection || noPongTime >= 7000) && attackedCreature && attackedCreature->getPlayer()) { - setAttackedCreature(nullptr); +void Player::sendPartyCreatureShield(const std::shared_ptr &creature) const { + if (client) { + client->sendPartyCreatureShield(creature); } +} - if (noPongTime >= 60000 && canLogout() && g_creatureEvents().playerLogout(static_self_cast())) { - g_logger().info("Player {} has been kicked due to ping timeout. (has client: {})", getName(), client != nullptr); - if (client) { - client->logout(true, true); - } else { - g_game().removeCreature(static_self_cast(), true); - } +void Player::sendPartyCreatureSkull(const std::shared_ptr &creature) const { + if (client) { + client->sendPartyCreatureSkull(creature); } } -std::shared_ptr Player::getWriteItem(uint32_t &retWindowTextId, uint16_t &retMaxWriteLen) { - retWindowTextId = this->windowTextId; - retMaxWriteLen = this->maxWriteLen; - return writeItem; +void Player::sendPartyCreatureHealth(const std::shared_ptr &creature, uint8_t healthPercent) const { + if (client) { + client->sendPartyCreatureHealth(creature, healthPercent); + } } -void Player::setImbuingItem(const std::shared_ptr &item) { - imbuingItem = item; +void Player::sendPartyPlayerMana(const std::shared_ptr &player, uint8_t manaPercent) const { + if (client) { + client->sendPartyPlayerMana(player, manaPercent); + } } -void Player::setWriteItem(const std::shared_ptr &item, uint16_t maxWriteLength /*= 0*/) { - windowTextId++; +void Player::sendPartyCreatureShowStatus(const std::shared_ptr &creature, bool showStatus) const { + if (client) { + client->sendPartyCreatureShowStatus(creature, showStatus); + } +} - if (item) { - writeItem = item; - this->maxWriteLen = maxWriteLength; - } else { - writeItem = nullptr; - this->maxWriteLen = 0; +void Player::sendPartyPlayerVocation(const std::shared_ptr &player) const { + if (client) { + client->sendPartyPlayerVocation(player); } } -std::shared_ptr Player::getEditHouse(uint32_t &retWindowTextId, uint32_t &retListId) { - retWindowTextId = this->windowTextId; - retListId = this->editListId; - return editHouse; +void Player::sendPlayerVocation(const std::shared_ptr &player) const { + if (client) { + client->sendPlayerVocation(player); + } } -void Player::setEditHouse(const std::shared_ptr &house, uint32_t listId /*= 0*/) { - windowTextId++; - editHouse = house; - editListId = listId; +void Player::sendDistanceShoot(const Position &from, const Position &to, uint16_t type) const { + if (client) { + client->sendDistanceShoot(from, to, type); + } } void Player::sendHouseWindow(const std::shared_ptr &house, uint32_t listId) const { @@ -1440,134 +1939,190 @@ void Player::sendHouseWindow(const std::shared_ptr &house, uint32_t listI } } -void Player::onApplyImbuement(const Imbuement* imbuement, const std::shared_ptr &item, uint8_t slot, bool protectionCharm) { - if (!imbuement || !item) { - return; +void Player::sendCreatePrivateChannel(uint16_t channelId, const std::string &channelName) const { + if (client) { + client->sendCreatePrivateChannel(channelId, channelName); } +} - ImbuementInfo imbuementInfo; - if (item->getImbuementInfo(slot, &imbuementInfo)) { - g_logger().error("[Player::onApplyImbuement] - An error occurred while player with name {} try to apply imbuement, item already contains imbuement", this->getName()); - this->sendImbuementResult("An error ocurred, please reopen imbuement window."); - return; +void Player::sendClosePrivate(uint16_t channelId) { + if (channelId == CHANNEL_GUILD || channelId == CHANNEL_PARTY) { + g_chat().removeUserFromChannel(getPlayer(), channelId); } - const auto &items = imbuement->getItems(); - for (auto &[key, value] : items) { - const ItemType &itemType = Item::items[key]; - if (static_self_cast()->getItemTypeCount(key) + this->getStashItemCount(itemType.id) < value) { - this->sendImbuementResult("You don't have all necessary items."); - return; - } + if (client) { + client->sendClosePrivate(channelId); } +} - const BaseImbuement* baseImbuement = g_imbuements().getBaseByID(imbuement->getBaseID()); - if (!baseImbuement) { +void Player::sendIcons() { + if (!client) { return; } - uint32_t price = baseImbuement->price; - price += protectionCharm ? baseImbuement->protectionPrice : 0; + // Iterates over the Bakragore icons to check if the player has any + auto iconBakragore = IconBakragore::None; + for (const auto &icon : magic_enum::enum_values()) { + if (icon == IconBakragore::None) { + continue; + } - if (!g_game().removeMoney(static_self_cast(), price, 0, true)) { - const std::string message = fmt::format("You don't have {} gold coins.", price); + const auto &condition = getCondition(CONDITION_BAKRAGORE, CONDITIONID_DEFAULT, magic_enum::enum_integer(icon)); + if (condition) { + g_logger().debug("[{}] found active condition Bakragore with subId {}", __FUNCTION__, magic_enum::enum_integer(icon)); + iconBakragore = icon; + } + } - g_logger().error("[Player::onApplyImbuement] - An error occurred while player with name {} try to apply imbuement, player do not have money", this->getName()); - sendImbuementResult(message); - return; + // Remove the last icon so that Bakragore's is added + auto iconSet = getClientIcons(); + if (iconSet.size() >= 9 && iconBakragore != IconBakragore::None) { + std::vector tempVector(iconSet.begin(), iconSet.end()); + tempVector.pop_back(); + iconSet = std::unordered_set(tempVector.begin(), tempVector.end()); } - g_metrics().addCounter("balance_decrease", price, { { "player", getName() }, { "context", "apply_imbuement" } }); + client->sendIcons(iconSet, iconBakragore); +} - for (auto &[key, value] : items) { - std::stringstream withdrawItemMessage; +void Player::sendIconBakragore(IconBakragore icon) const { + if (client) { + client->sendIconBakragore(icon); + } +} - const uint32_t inventoryItemCount = getItemTypeCount(key); - if (inventoryItemCount >= value) { - removeItemOfType(key, value, -1, true); - continue; +void Player::removeBakragoreIcons() { + for (auto icon : magic_enum::enum_values()) { + if (hasCondition(CONDITION_BAKRAGORE, enumToValue(icon))) { + removeCondition(CONDITION_BAKRAGORE, CONDITIONID_DEFAULT, true); } + } +} - uint32_t mathItemCount = value; - if (inventoryItemCount > 0 && removeItemOfType(key, inventoryItemCount, -1, false)) { - mathItemCount = mathItemCount - inventoryItemCount; - } +void Player::removeBakragoreIcon(const IconBakragore icon) { + if (hasCondition(CONDITION_BAKRAGORE, enumToValue(icon))) { + removeCondition(CONDITION_BAKRAGORE, CONDITIONID_DEFAULT, true); + } +} - const ItemType &itemType = Item::items[key]; +void Player::sendClientCheck() const { + if (client) { + client->sendClientCheck(); + } +} - withdrawItemMessage << "Using " << mathItemCount << "x " << itemType.name << " from your supply stash. "; - withdrawItem(itemType.id, mathItemCount); - sendTextMessage(MESSAGE_STATUS, withdrawItemMessage.str()); +void Player::sendGameNews() const { + if (client) { + client->sendGameNews(); + } +} + +void Player::sendMagicEffect(const Position &pos, uint16_t type) const { + if (client) { + client->sendMagicEffect(pos, type); } +} - if (!protectionCharm && uniform_random(1, 100) > baseImbuement->percent) { - openImbuementWindow(item); - sendImbuementResult("Oh no!\n\nThe imbuement has failed. You have lost the astral sources and gold you needed for the imbuement.\n\nNext time use a protection charm to better your chances."); - openImbuementWindow(item); - return; +void Player::removeMagicEffect(const Position &pos, uint16_t type) const { + if (client) { + client->removeMagicEffect(pos, type); } +} - // Update imbuement stats item if the item is equipped - if (item->getParent() == getPlayer()) { - addItemImbuementStats(imbuement); +void Player::sendPing() { + const int64_t timeNow = OTSYS_TIME(); + + bool hasLostConnection = false; + if ((timeNow - lastPing) >= 5000) { + lastPing = timeNow; + if (client) { + client->sendPing(); + } else { + hasLostConnection = true; + } } - item->addImbuement(slot, imbuement->getID(), baseImbuement->duration); - openImbuementWindow(item); + const int64_t noPongTime = timeNow - lastPong; + const auto &attackedCreature = getAttackedCreature(); + if ((hasLostConnection || noPongTime >= 7000) && attackedCreature && attackedCreature->getPlayer()) { + setAttackedCreature(nullptr); + } + + if (noPongTime >= 60000 && canLogout() && g_creatureEvents().playerLogout(static_self_cast())) { + g_logger().info("Player {} has been kicked due to ping timeout. (has client: {})", getName(), client != nullptr); + if (client) { + client->logout(true, true); + } else { + g_game().removeCreature(static_self_cast(), true); + } + } } -void Player::onClearImbuement(const std::shared_ptr &item, uint8_t slot) { - if (!item) { - return; +void Player::sendPingBack() const { + if (client) { + client->sendPingBack(); } +} - ImbuementInfo imbuementInfo; - if (!item->getImbuementInfo(slot, &imbuementInfo)) { - g_logger().error("[Player::onClearImbuement] - An error occurred while player with name {} try to apply imbuement, item not contains imbuement", this->getName()); - this->sendImbuementResult("An error ocurred, please reopen imbuement window."); - return; +void Player::sendStats() { + if (client) { + client->sendStats(); + lastStatsTrainingTime = getOfflineTrainingTime() / 60 / 1000; } +} - const BaseImbuement* baseImbuement = g_imbuements().getBaseByID(imbuementInfo.imbuement->getBaseID()); - if (!baseImbuement) { - return; +void Player::sendBasicData() const { + if (client) { + client->sendBasicData(); } +} - if (!g_game().removeMoney(static_self_cast(), baseImbuement->removeCost, 0, true)) { - const std::string message = fmt::format("You don't have {} gold coins.", baseImbuement->removeCost); +void Player::sendBlessStatus() const { + if (client) { + client->sendBlessStatus(); + } +} - g_logger().error("[Player::onClearImbuement] - An error occurred while player with name {} try to apply imbuement, player do not have money", this->getName()); - this->sendImbuementResult(message); - this->openImbuementWindow(item); - return; +void Player::sendSkills() const { + if (client) { + client->sendSkills(); } - g_metrics().addCounter("balance_decrease", baseImbuement->removeCost, { { "player", getName() }, { "context", "clear_imbuement" } }); +} - if (item->getParent() == getPlayer()) { - removeItemImbuementStats(imbuementInfo.imbuement); +void Player::sendTextMessage(MessageClasses mclass, const std::string &message) const { + if (client) { + client->sendTextMessage(TextMessage(mclass, message)); } +} - item->clearImbuement(slot, imbuementInfo.imbuement->getID()); - this->openImbuementWindow(item); +void Player::sendTextMessage(const TextMessage &message) const { + if (client) { + client->sendTextMessage(message); + } } -void Player::openImbuementWindow(const std::shared_ptr &item) { - if (!client || !item) { - return; +void Player::sendReLoginWindow(uint8_t unfairFightReduction) const { + if (client) { + client->sendReLoginWindow(unfairFightReduction); } +} - if (item->getImbuementSlot() <= 0) { - this->sendTextMessage(MESSAGE_EVENT_ADVANCE, "This item is not imbuable."); - return; +void Player::sendTextWindow(const std::shared_ptr &item, uint16_t maxlen, bool canWrite) const { + if (client) { + client->sendTextWindow(windowTextId, item, maxlen, canWrite); } +} - const auto &itemParent = item->getTopParent(); - if (itemParent && itemParent != getPlayer()) { - this->sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have to pick up the item to imbue it."); - return; +void Player::sendToChannel(const std::shared_ptr &creature, SpeakClasses type, const std::string &text, uint16_t channelId) const { + if (client) { + client->sendToChannel(creature, type, text, channelId); } +} - client->openImbuementWindow(item); +void Player::sendShop(const std::shared_ptr &npc) const { + if (client) { + client->sendShop(npc); + } } void Player::sendSaleItemList(const std::map &inventoryMap) const { @@ -1576,203 +2131,273 @@ void Player::sendSaleItemList(const std::map &inventoryMap) } } -void Player::sendMarketEnter(uint32_t depotId) const { - if (!client || this->getLastDepotId() == -1 || !depotId) { - return; - } +bool Player::hasImbuingItem() const { + return imbuingItem != nullptr; +} - client->sendMarketEnter(depotId); +void Player::setImbuingItem(const std::shared_ptr &item) { + imbuingItem = item; } -// container -void Player::sendAddContainerItem(const std::shared_ptr &container, std::shared_ptr item) { - if (!client) { +void Player::addBlessing(uint8_t index, uint8_t count) { + if (blessings[index - 1] == 255) { return; } - if (!container) { + blessings[index - 1] += count; +} + +void Player::removeBlessing(uint8_t index, uint8_t count) { + if (blessings[index - 1] == 0) { return; } - for (const auto &[containerId, containerInfo] : openContainers) { - if (containerInfo.container != container) { - continue; - } + blessings[index - 1] -= count; +} - uint16_t slot = containerInfo.index; - if (container->getID() == ITEM_BROWSEFIELD) { - const uint16_t containerSize = container->size() - 1; - const uint16_t pageEnd = containerInfo.index + container->capacity() - 1; - if (containerSize > pageEnd) { - slot = pageEnd; - item = container->getItemByIndex(pageEnd); - } else { - slot = containerSize; - } - } else if (containerInfo.index >= container->capacity()) { - item = container->getItemByIndex(containerInfo.index - 1); - } - client->sendAddContainerItem(containerId, slot, item); +bool Player::hasBlessing(uint8_t index) const { + return blessings[index - 1] != 0; +} + +void Player::sendCloseShop() const { + if (client) { + client->sendCloseShop(); } } -void Player::sendUpdateContainerItem(const std::shared_ptr &container, uint16_t slot, const std::shared_ptr &newItem) { - if (!client) { +void Player::sendMarketEnter(uint32_t depotId) const { + if (!client || this->getLastDepotId() == -1 || !depotId) { return; } - for (const auto &[containerId, containerInfo] : openContainers) { - if (containerInfo.container != container) { - continue; - } - - if (slot < containerInfo.index) { - continue; - } - - const uint16_t pageEnd = containerInfo.index + container->capacity(); - if (slot >= pageEnd) { - continue; - } + client->sendMarketEnter(depotId); +} - client->sendUpdateContainerItem(containerId, slot, newItem); +void Player::sendMarketLeave() { + inMarket = false; + if (client) { + client->sendMarketLeave(); } } -void Player::sendRemoveContainerItem(const std::shared_ptr &container, uint16_t slot) { - if (!client) { - return; +void Player::sendMarketBrowseItem(uint16_t itemId, const MarketOfferList &buyOffers, const MarketOfferList &sellOffers, uint8_t tier) const { + if (client) { + client->sendMarketBrowseItem(itemId, buyOffers, sellOffers, tier); } +} - if (!container) { - return; +void Player::sendMarketBrowseOwnOffers(const MarketOfferList &buyOffers, const MarketOfferList &sellOffers) const { + if (client) { + client->sendMarketBrowseOwnOffers(buyOffers, sellOffers); } +} - for (auto &[containerId, containerInfo] : openContainers) { - if (containerInfo.container != container) { - continue; - } - - uint16_t &firstIndex = containerInfo.index; - if (firstIndex > 0 && firstIndex >= container->size() - 1) { - firstIndex -= container->capacity(); - sendContainer(containerId, container, false, firstIndex); - } - - client->sendRemoveContainerItem(containerId, std::max(slot, firstIndex), container->getItemByIndex(container->capacity() + firstIndex)); +void Player::sendMarketBrowseOwnHistory(const HistoryMarketOfferList &buyOffers, const HistoryMarketOfferList &sellOffers) const { + if (client) { + client->sendMarketBrowseOwnHistory(buyOffers, sellOffers); } } -void Player::onUpdateTileItem(const std::shared_ptr &updateTile, const Position &pos, const std::shared_ptr &oldItem, const ItemType &oldType, const std::shared_ptr &newItem, const ItemType &newType) { - Creature::onUpdateTileItem(updateTile, pos, oldItem, oldType, newItem, newType); - - if (oldItem != newItem) { - onRemoveTileItem(updateTile, pos, oldType, oldItem); +void Player::sendMarketDetail(uint16_t itemId, uint8_t tier) const { + if (client) { + client->sendMarketDetail(itemId, tier); } +} - if (tradeState != TRADE_TRANSFER) { - if (tradeItem && oldItem == tradeItem) { - g_game().internalCloseTrade(getPlayer()); - } +void Player::sendMarketAcceptOffer(const MarketOfferEx &offer) const { + if (client) { + client->sendMarketAcceptOffer(offer); } } -void Player::onRemoveTileItem(const std::shared_ptr &fromTile, const Position &pos, const ItemType &iType, const std::shared_ptr &item) { - Creature::onRemoveTileItem(fromTile, pos, iType, item); +void Player::sendMarketCancelOffer(const MarketOfferEx &offer) const { + if (client) { + client->sendMarketCancelOffer(offer); + } +} - if (tradeState != TRADE_TRANSFER) { - checkTradeState(item); +void Player::sendTradeItemRequest(const std::string &traderName, const std::shared_ptr &item, bool ack) const { + if (client) { + client->sendTradeItemRequest(traderName, item, ack); + } +} - if (tradeItem) { - const auto &container = item->getContainer(); - if (container && container->isHoldingItem(tradeItem)) { - g_game().internalCloseTrade(static_self_cast()); - } - } +void Player::sendTradeClose() const { + if (client) { + client->sendCloseTrade(); } +} - checkLootContainers(item->getContainer()); +void Player::sendWorldLight(LightInfo lightInfo) const { + if (client) { + client->sendWorldLight(lightInfo); + } } -void Player::onCreatureAppear(const std::shared_ptr &creature, bool isLogin) { - Creature::onCreatureAppear(creature, isLogin); +void Player::sendTibiaTime(int32_t time) const { + if (client) { + client->sendTibiaTime(time); + } +} - if (isLogin && creature == getPlayer()) { - onEquipInventory(); +void Player::sendChannelsDialog() const { + if (client) { + client->sendChannelsDialog(); + } +} - // Refresh bosstiary tracker onLogin - refreshCyclopediaMonsterTracker(true); - // Refresh bestiary tracker onLogin - refreshCyclopediaMonsterTracker(false); +void Player::sendOpenPrivateChannel(const std::string &receiver) const { + if (client) { + client->sendOpenPrivateChannel(receiver); + } +} - for (const auto &condition : storedConditionList) { - addCondition(condition); - } - storedConditionList.clear(); +void Player::sendExperienceTracker(int64_t rawExp, int64_t finalExp) const { + if (client) { + client->sendExperienceTracker(rawExp, finalExp); + } +} - updateRegeneration(); +void Player::sendOutfitWindow() const { + if (client) { + client->sendOutfitWindow(); + } +} - const auto &bed = g_game().getBedBySleeper(guid); - if (bed) { - bed->wakeUp(static_self_cast()); - } +// Imbuements - auto version = client->oldProtocol ? getProtocolVersion() : CLIENT_VERSION; - g_logger().info("{} has logged in. (Protocol: {})", name, version); +void Player::onApplyImbuement(const Imbuement* imbuement, const std::shared_ptr &item, uint8_t slot, bool protectionCharm) { + if (!imbuement || !item) { + return; + } - if (guild) { - guild->addMember(static_self_cast()); - } + ImbuementInfo imbuementInfo; + if (item->getImbuementInfo(slot, &imbuementInfo)) { + g_logger().error("[Player::onApplyImbuement] - An error occurred while player with name {} try to apply imbuement, item already contains imbuement", this->getName()); + this->sendImbuementResult("An error ocurred, please reopen imbuement window."); + return; + } - int32_t offlineTime; - if (getLastLogout() != 0) { - // Not counting more than 21 days to prevent overflow when multiplying with 1000 (for milliseconds). - offlineTime = std::min(time(nullptr) - getLastLogout(), 86400 * 21); - } else { - offlineTime = 0; + const auto &items = imbuement->getItems(); + for (auto &[key, value] : items) { + const ItemType &itemType = Item::items[key]; + if (static_self_cast()->getItemTypeCount(key) + this->getStashItemCount(itemType.id) < value) { + this->sendImbuementResult("You don't have all necessary items."); + return; } + } - for (const auto &condition : getMuteConditions()) { - condition->setTicks(condition->getTicks() - (offlineTime * 1000)); - if (condition->getTicks() <= 0) { - removeCondition(condition); - } - } + const BaseImbuement* baseImbuement = g_imbuements().getBaseByID(imbuement->getBaseID()); + if (!baseImbuement) { + return; + } - g_game().checkPlayersRecord(); - if (getLevel() < g_configManager().getNumber(ADVENTURERSBLESSING_LEVEL) && getVocationId() > VOCATION_NONE) { - for (uint8_t i = 2; i <= 6; i++) { - if (!hasBlessing(i)) { - addBlessing(i, 1); - } - } - sendBlessStatus(); + uint32_t price = baseImbuement->price; + price += protectionCharm ? baseImbuement->protectionPrice : 0; + + if (!g_game().removeMoney(static_self_cast(), price, 0, true)) { + const std::string message = fmt::format("You don't have {} gold coins.", price); + + g_logger().error("[Player::onApplyImbuement] - An error occurred while player with name {} try to apply imbuement, player do not have money", this->getName()); + sendImbuementResult(message); + return; + } + + g_metrics().addCounter("balance_decrease", price, { { "player", getName() }, { "context", "apply_imbuement" } }); + + for (auto &[key, value] : items) { + std::stringstream withdrawItemMessage; + + const uint32_t inventoryItemCount = getItemTypeCount(key); + if (inventoryItemCount >= value) { + removeItemOfType(key, value, -1, true); + continue; } - if (getCurrentMount() != 0) { - toggleMount(true); + uint32_t mathItemCount = value; + if (inventoryItemCount > 0 && removeItemOfType(key, inventoryItemCount, -1, false)) { + mathItemCount = mathItemCount - inventoryItemCount; } - g_game().changePlayerSpeed(static_self_cast(), 0); + const ItemType &itemType = Item::items[key]; + + withdrawItemMessage << "Using " << mathItemCount << "x " << itemType.name << " from your supply stash. "; + withdrawItem(itemType.id, mathItemCount); + sendTextMessage(MESSAGE_STATUS, withdrawItemMessage.str()); + } + + if (!protectionCharm && uniform_random(1, 100) > baseImbuement->percent) { + openImbuementWindow(item); + sendImbuementResult("Oh no!\n\nThe imbuement has failed. You have lost the astral sources and gold you needed for the imbuement.\n\nNext time use a protection charm to better your chances."); + openImbuementWindow(item); + return; } + + // Update imbuement stats item if the item is equipped + if (item->getParent() == getPlayer()) { + addItemImbuementStats(imbuement); + } + + item->addImbuement(slot, imbuement->getID(), baseImbuement->duration); + openImbuementWindow(item); } -void Player::onAttackedCreatureDisappear(bool isLogout) { - sendCancelTarget(); +void Player::onClearImbuement(const std::shared_ptr &item, uint8_t slot) { + if (!item) { + return; + } - if (!isLogout) { - sendTextMessage(MESSAGE_FAILURE, "Target lost."); + ImbuementInfo imbuementInfo; + if (!item->getImbuementInfo(slot, &imbuementInfo)) { + g_logger().error("[Player::onClearImbuement] - An error occurred while player with name {} try to apply imbuement, item not contains imbuement", this->getName()); + this->sendImbuementResult("An error ocurred, please reopen imbuement window."); + return; + } + + const BaseImbuement* baseImbuement = g_imbuements().getBaseByID(imbuementInfo.imbuement->getBaseID()); + if (!baseImbuement) { + return; + } + + if (!g_game().removeMoney(static_self_cast(), baseImbuement->removeCost, 0, true)) { + const std::string message = fmt::format("You don't have {} gold coins.", baseImbuement->removeCost); + + g_logger().error("[Player::onClearImbuement] - An error occurred while player with name {} try to apply imbuement, player do not have money", this->getName()); + this->sendImbuementResult(message); + this->openImbuementWindow(item); + return; + } + g_metrics().addCounter("balance_decrease", baseImbuement->removeCost, { { "player", getName() }, { "context", "clear_imbuement" } }); + + if (item->getParent() == getPlayer()) { + removeItemImbuementStats(imbuementInfo.imbuement); } + + item->clearImbuement(slot, imbuementInfo.imbuement->getID()); + this->openImbuementWindow(item); } -void Player::onFollowCreatureDisappear(bool isLogout) { - sendCancelTarget(); +void Player::openImbuementWindow(const std::shared_ptr &item) { + if (!client || !item) { + return; + } - if (!isLogout) { - sendTextMessage(MESSAGE_FAILURE, "Target lost."); + if (item->getImbuementSlot() <= 0) { + this->sendTextMessage(MESSAGE_EVENT_ADVANCE, "This item is not imbuable."); + return; + } + + const auto &itemParent = item->getTopParent(); + if (itemParent && itemParent != getPlayer()) { + this->sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have to pick up the item to imbue it."); + return; } + + client->openImbuementWindow(item); } +// inventory + void Player::onChangeZone(ZoneType_t zone) { if (zone == ZONE_PROTECTION) { if (getAttackedCreature() && !hasFlag(PlayerFlags_t::IgnoreProtectionZone)) { @@ -1834,47 +2459,6 @@ void Player::onAttackedCreatureChangeZone(ZoneType_t zone) { } } -void Player::onRemoveCreature(const std::shared_ptr &creature, bool isLogout) { - Creature::onRemoveCreature(creature, isLogout); - - if (const auto &player = getPlayer(); player == creature) { - if (isLogout) { - onDeEquipInventory(); - - if (m_party) { - m_party->leaveParty(player, true); - } - if (guild) { - guild->removeMember(player); - } - - g_game().removePlayerUniqueLogin(player); - loginPosition = getPosition(); - lastLogout = time(nullptr); - g_logger().info("{} has logged out", getName()); - g_chat().removeUserFromAllChannels(player); - clearPartyInvitations(); - } - - if (eventWalk != 0) { - setFollowCreature(nullptr); - } - - if (tradePartner) { - g_game().internalCloseTrade(player); - } - - closeShopWindow(); - - g_saveManager().savePlayer(player); - } - - if (creature == shopOwner) { - setShopOwner(nullptr); - sendCloseShop(); - } -} - bool Player::openShopWindow(const std::shared_ptr &npc, const std::vector &shopItems) { Benchmark brenchmark; if (!npc) { @@ -1932,163 +2516,8 @@ void Player::onWalk(Direction &dir) { g_callbacks().executeCallback(EventCallback_t::playerOnWalk, &EventCallback::playerOnWalk, getPlayer(), dir); } -void Player::onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) { - Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport); - - const auto &followCreature = getFollowCreature(); - if (hasFollowPath && (creature == followCreature || (creature.get() == this && followCreature))) { - isUpdatingPath = false; - g_game().updateCreatureWalk(getID()); // internally uses addEventWalk. - } - - if (creature != getPlayer()) { - return; - } - - if (tradeState != TRADE_TRANSFER) { - // check if we should close trade - if (tradeItem && !Position::areInRange<1, 1, 0>(tradeItem->getPosition(), getPosition())) { - g_game().internalCloseTrade(getPlayer()); - } - - if (tradePartner && !Position::areInRange<2, 2, 0>(tradePartner->getPosition(), getPosition())) { - g_game().internalCloseTrade(getPlayer()); - } - } - - // close modal windows - if (!modalWindows.empty()) { - // TODO: This shouldn't be hardcoded - for (const uint32_t modalWindowId : modalWindows) { - if (modalWindowId == std::numeric_limits::max()) { - sendTextMessage(MESSAGE_EVENT_ADVANCE, "Offline training aborted."); - break; - } - } - modalWindows.clear(); - } - - // leave market - if (inMarket) { - inMarket = false; - } - - if (m_party) { - m_party->updateSharedExperience(); - m_party->updatePlayerStatus(getPlayer(), oldPos, newPos); - } - - if (teleport || oldPos.z != newPos.z) { - int32_t ticks = g_configManager().getNumber(STAIRHOP_DELAY); - if (ticks > 0) { - if (const auto &condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_PACIFIED, ticks, 0)) { - addCondition(condition); - } - } - } -} - -void Player::onEquipInventory() { - for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) { - const auto &item = inventory[slot]; - if (item) { - item->startDecaying(); - g_moveEvents().onPlayerEquip(getPlayer(), item, static_cast(slot), false); - } - } -} - -void Player::onDeEquipInventory() { - for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) { - const auto &item = inventory[slot]; - if (item) { - g_moveEvents().onPlayerDeEquip(getPlayer(), item, static_cast(slot)); - } - } -} - -// container -void Player::onAddContainerItem(const std::shared_ptr &item) { - checkTradeState(item); -} - -void Player::onUpdateContainerItem(const std::shared_ptr &container, const std::shared_ptr &oldItem, const std::shared_ptr &newItem) { - if (oldItem != newItem) { - onRemoveContainerItem(container, oldItem); - } - - if (tradeState != TRADE_TRANSFER) { - checkTradeState(oldItem); - } -} - -void Player::onRemoveContainerItem(const std::shared_ptr &container, const std::shared_ptr &item) { - if (tradeState != TRADE_TRANSFER) { - checkTradeState(item); - - if (tradeItem) { - if (tradeItem->getParent() != container && container->isHoldingItem(tradeItem)) { - g_game().internalCloseTrade(static_self_cast()); - } - } - } - - checkLootContainers(item->getContainer()); -} - -void Player::onCloseContainer(const std::shared_ptr &container) { - if (!client) { - return; - } - - for (const auto &[containerId, containerInfo] : openContainers) { - if (containerInfo.container == container) { - client->sendCloseContainer(containerId); - } - } -} - -void Player::onSendContainer(const std::shared_ptr &container) { - if (!client || !container) { - return; - } - - const bool hasParent = container->hasParent(); - for (const auto &[containerId, containerInfo] : openContainers) { - if (containerInfo.container == container) { - client->sendContainer(containerId, container, hasParent, containerInfo.index); - } - } -} - -// inventory -void Player::onUpdateInventoryItem(const std::shared_ptr &oldItem, const std::shared_ptr &newItem) { - if (oldItem != newItem) { - onRemoveInventoryItem(oldItem); - } - - if (tradeState != TRADE_TRANSFER) { - checkTradeState(oldItem); - } -} - -void Player::onRemoveInventoryItem(const std::shared_ptr &item) { - if (tradeState != TRADE_TRANSFER) { - checkTradeState(item); - - if (tradeItem) { - const auto &container = item->getContainer(); - if (container && container->isHoldingItem(tradeItem)) { - g_game().internalCloseTrade(static_self_cast()); - } - } - } - - checkLootContainers(item->getContainer()); -} - -void Player::checkTradeState(const std::shared_ptr &item) { - if (!tradeItem || tradeState == TRADE_TRANSFER) { +void Player::checkTradeState(const std::shared_ptr &item) { + if (!tradeItem || tradeState == TRADE_TRANSFER) { return; } @@ -2171,6 +2600,17 @@ void Player::setNextPotionActionTask(const std::shared_ptr &task) { } } +void Player::setModuleDelay(uint8_t byteortype, int16_t delay) { + moduleDelayMap[byteortype] = OTSYS_TIME() + delay; +} + +bool Player::canRunModule(uint8_t byteortype) { + if (!moduleDelayMap[byteortype]) { + return true; + } + return moduleDelayMap[byteortype] <= OTSYS_TIME(); +} + uint32_t Player::getNextActionTime() const { return std::max(SCHEDULER_MINTICKS, nextAction - OTSYS_TIME()); } @@ -2179,86 +2619,328 @@ uint32_t Player::getNextPotionActionTime() const { return std::max(SCHEDULER_MINTICKS, nextPotionAction - OTSYS_TIME()); } -void Player::cancelPush() { - if (actionTaskEventPush != 0) { - g_dispatcher().stopEvent(actionTaskEventPush); - actionTaskEventPush = 0; - inEventMovePush = false; +std::shared_ptr Player::getWriteItem(uint32_t &retWindowTextId, uint16_t &retMaxWriteLen) { + retWindowTextId = this->windowTextId; + retMaxWriteLen = this->maxWriteLen; + return writeItem; +} + +void Player::setWriteItem(const std::shared_ptr &item, uint16_t maxWriteLength) { + windowTextId++; + + if (item) { + writeItem = item; + this->maxWriteLen = maxWriteLength; + } else { + writeItem = nullptr; + this->maxWriteLen = 0; } } -void Player::onThink(uint32_t interval) { - Creature::onThink(interval); +std::shared_ptr Player::getEditHouse(uint32_t &retWindowTextId, uint32_t &retListId) { + retWindowTextId = this->windowTextId; + retListId = this->editListId; + return editHouse; +} - sendPing(); +void Player::setEditHouse(const std::shared_ptr &house, uint32_t listId) { + windowTextId++; + editHouse = house; + editListId = listId; +} - MessageBufferTicks += interval; - if (MessageBufferTicks >= 1500) { - MessageBufferTicks = 0; - addMessageBuffer(); +void Player::learnInstantSpell(const std::string &spellName) { + if (!hasLearnedInstantSpell(spellName)) { + learnedInstantSpellList.emplace_back(spellName); } +} - // Transcendance (avatar trigger) - triggerTranscendance(); - // Momentum (cooldown resets) - triggerMomentum(); - const auto &playerTile = getTile(); - const bool vipStaysOnline = isVip() && g_configManager().getBoolean(VIP_STAY_ONLINE); - idleTime += interval; - if (playerTile && !playerTile->hasFlag(TILESTATE_NOLOGOUT) && !isAccessPlayer() && !isExerciseTraining() && !vipStaysOnline) { - const int32_t kickAfterMinutes = g_configManager().getNumber(KICK_AFTER_MINUTES); - if (idleTime > (kickAfterMinutes * 60000) + 60000) { - removePlayer(true); - } else if (client && idleTime == 60000 * kickAfterMinutes) { - std::ostringstream ss; - ss << "There was no variation in your behaviour for " << kickAfterMinutes << " minutes. You will be disconnected in one minute if there is no change in your actions until then."; - client->sendTextMessage(TextMessage(MESSAGE_ADMINISTRATOR, ss.str())); - } - } +void Player::forgetInstantSpell(const std::string &spellName) { + std::erase(learnedInstantSpellList, spellName); +} - if (g_game().getWorldType() != WORLD_TYPE_PVP_ENFORCED) { - checkSkullTicks(interval / 1000); +bool Player::hasLearnedInstantSpell(const std::string &spellName) const { + if (hasFlag(PlayerFlags_t::CannotUseSpells)) { + return false; } - addOfflineTrainingTime(interval); - if (lastStatsTrainingTime != getOfflineTrainingTime() / 60 / 1000) { - sendStats(); + if (hasFlag(PlayerFlags_t::IgnoreSpellCheck)) { + return true; } - // Wheel of destiny major spells - wheel()->onThink(); - - g_callbacks().executeCallback(EventCallback_t::playerOnThink, &EventCallback::playerOnThink, getPlayer(), interval); + return std::ranges::any_of(learnedInstantSpellList, [&](const auto &learnedSpellName) { + return strcasecmp(learnedSpellName.c_str(), spellName.c_str()) == 0; + }); } -uint32_t Player::isMuted() const { - if (hasFlag(PlayerFlags_t::CannotBeMuted)) { - return 0; +void Player::updateRegeneration() const { + if (!vocation) { + return; } - int32_t muteTicks = 0; - for (const auto &condition : conditions) { - if (condition->getType() == CONDITION_MUTED && condition->getTicks() > muteTicks) { - muteTicks = condition->getTicks(); - } + const auto &condition = getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT); + if (condition) { + condition->setParam(CONDITION_PARAM_HEALTHGAIN, vocation->getHealthGainAmount()); + condition->setParam(CONDITION_PARAM_HEALTHTICKS, vocation->getHealthGainTicks()); + condition->setParam(CONDITION_PARAM_MANAGAIN, vocation->getManaGainAmount()); + condition->setParam(CONDITION_PARAM_MANATICKS, vocation->getManaGainTicks()); } - return static_cast(muteTicks) / 1000; } -void Player::addMessageBuffer() { - if (MessageBufferCount > 0 && g_configManager().getNumber(MAX_MESSAGEBUFFER) != 0 && !hasFlag(PlayerFlags_t::CannotBeMuted)) { - --MessageBufferCount; - } +void Player::setScheduledSaleUpdate(bool scheduled) { + scheduledSaleUpdate = scheduled; } -void Player::removeMessageBuffer() { - if (hasFlag(PlayerFlags_t::CannotBeMuted)) { - return; - } +bool Player::getScheduledSaleUpdate() const { + return scheduledSaleUpdate; +} - const int32_t maxMessageBuffer = g_configManager().getNumber(MAX_MESSAGEBUFFER); - if (maxMessageBuffer != 0 && MessageBufferCount <= maxMessageBuffer + 1) { - if (++MessageBufferCount > maxMessageBuffer) { +bool Player::inPushEvent() const { + return inEventMovePush; +} + +void Player::pushEvent(bool b) { + inEventMovePush = b; +} + +bool Player::walkExhausted() const { + if (hasCondition(CONDITION_PARALYZE)) { + return lastWalking > OTSYS_TIME(); + } + + return false; +} + +void Player::setWalkExhaust(int64_t value) { + lastWalking = OTSYS_TIME() + value; +} + +const std::map &Player::getOpenContainers() const { + return openContainers; +} + +uint16_t Player::getBaseXpGain() const { + return baseXpGain; +} + +void Player::setBaseXpGain(uint16_t value) { + baseXpGain = std::min(std::numeric_limits::max(), value); +} + +uint16_t Player::getVoucherXpBoost() const { + return voucherXpBoost; +} + +void Player::setVoucherXpBoost(uint16_t value) { + voucherXpBoost = std::min(std::numeric_limits::max(), value); +} + +uint16_t Player::getGrindingXpBoost() const { + return grindingXpBoost; +} + +void Player::setGrindingXpBoost(uint16_t value) { + grindingXpBoost = std::min(std::numeric_limits::max(), value); +} + +uint16_t Player::getXpBoostPercent() const { + return xpBoostPercent; +} + +void Player::setXpBoostPercent(uint16_t percent) { + xpBoostPercent = percent; +} + +uint16_t Player::getStaminaXpBoost() const { + return staminaXpBoost; +} + +void Player::setStaminaXpBoost(uint16_t value) { + staminaXpBoost = std::min(std::numeric_limits::max(), value); +} + +void Player::setXpBoostTime(uint16_t timeLeft) { + // only allow time boosts of 12 hours or less + if (timeLeft > 12 * 3600) { + xpBoostTime = 12 * 3600; + return; + } + xpBoostTime = timeLeft; +} + +uint16_t Player::getXpBoostTime() const { + return xpBoostTime; +} + +int32_t Player::getIdleTime() const { + return idleTime; +} + +void Player::setTraining(bool value) { + for (const auto &[key, player] : g_game().getPlayers()) { + if (!this->isInGhostMode() || player->isAccessPlayer()) { + player->vip()->notifyStatusChange(static_self_cast(), value ? VipStatus_t::Training : VipStatus_t::Online, false); + } + } + vip()->setStatus(VipStatus_t::Training); + setExerciseTraining(value); +} + +void Player::addItemImbuementStats(const Imbuement* imbuement) { + bool requestUpdate = false; + // Check imbuement skills + for (int32_t skill = SKILL_FIRST; skill <= SKILL_LAST; ++skill) { + if (imbuement->skills[skill]) { + requestUpdate = true; + setVarSkill(static_cast(skill), imbuement->skills[skill]); + } + } + + // Check imbuement magic level + for (int32_t stat = STAT_FIRST; stat <= STAT_LAST; ++stat) { + if (imbuement->stats[stat]) { + requestUpdate = true; + setVarStats(static_cast(stat), imbuement->stats[stat]); + } + } + + // Add imbuement speed + if (imbuement->speed != 0) { + g_game().changeSpeed(static_self_cast(), imbuement->speed); + } + + // Add imbuement capacity + if (imbuement->capacity != 0) { + requestUpdate = true; + bonusCapacity = (capacity * imbuement->capacity) / 100; + } + + if (requestUpdate) { + sendStats(); + sendSkills(); + } +} + +void Player::removeItemImbuementStats(const Imbuement* imbuement) { + if (!imbuement) { + return; + } + + bool requestUpdate = false; + + for (int32_t skill = SKILL_FIRST; skill <= SKILL_LAST; ++skill) { + if (imbuement->skills[skill]) { + requestUpdate = true; + setVarSkill(static_cast(skill), -imbuement->skills[skill]); + } + } + + // Check imbuement magic level + for (int32_t stat = STAT_FIRST; stat <= STAT_LAST; ++stat) { + if (imbuement->stats[stat]) { + requestUpdate = true; + setVarStats(static_cast(stat), -imbuement->stats[stat]); + } + } + + // Remove imbuement speed + if (imbuement->speed != 0) { + g_game().changeSpeed(static_self_cast(), -imbuement->speed); + } + + // Remove imbuement capacity + if (imbuement->capacity != 0) { + requestUpdate = true; + bonusCapacity = 0; + } + + if (requestUpdate) { + sendStats(); + sendSkills(); + } +} + +void Player::updateImbuementTrackerStats() const { + if (imbuementTrackerWindowOpen) { + g_game().playerRequestInventoryImbuements(getID(), true); + } +} + +// User Interface action exhaustion + +bool Player::isUIExhausted(uint32_t exhaustionTime) const { + return (OTSYS_TIME() - lastUIInteraction < exhaustionTime); +} + +void Player::updateUIExhausted() { + lastUIInteraction = OTSYS_TIME(); +} + +bool Player::isQuickLootListedItem(const std::shared_ptr &item) const { + if (!item) { + return false; + } + + auto it = std::ranges::find(quickLootListItemIds, item->getID()); + return it != quickLootListItemIds.end(); +} + +void Player::setNextAction(int64_t time) { + if (time > nextAction) { + nextAction = time; + } +} + +bool Player::canDoAction() const { + return nextAction <= OTSYS_TIME(); +} + +void Player::setNextPotionAction(int64_t time) { + if (time > nextPotionAction) { + nextPotionAction = time; + } +} + +bool Player::canDoPotionAction() const { + return nextPotionAction <= OTSYS_TIME(); +} + +void Player::cancelPush() { + if (actionTaskEventPush != 0) { + g_dispatcher().stopEvent(actionTaskEventPush); + actionTaskEventPush = 0; + inEventMovePush = false; + } +} + +uint32_t Player::isMuted() const { + if (hasFlag(PlayerFlags_t::CannotBeMuted)) { + return 0; + } + + int32_t muteTicks = 0; + for (const auto &condition : conditions) { + if (condition->getType() == CONDITION_MUTED && condition->getTicks() > muteTicks) { + muteTicks = condition->getTicks(); + } + } + return static_cast(muteTicks) / 1000; +} + +void Player::addMessageBuffer() { + if (MessageBufferCount > 0 && g_configManager().getNumber(MAX_MESSAGEBUFFER) != 0 && !hasFlag(PlayerFlags_t::CannotBeMuted)) { + --MessageBufferCount; + } +} + +void Player::removeMessageBuffer() { + if (hasFlag(PlayerFlags_t::CannotBeMuted)) { + return; + } + + const int32_t maxMessageBuffer = g_configManager().getNumber(MAX_MESSAGEBUFFER); + if (maxMessageBuffer != 0 && MessageBufferCount <= maxMessageBuffer + 1) { + if (++MessageBufferCount > maxMessageBuffer) { uint32_t muteCount = 1; const auto it = muteCountMap.find(guid); if (it != muteCountMap.end()) { @@ -2626,7 +3308,11 @@ bool Player::hasShield() const { return false; } -BlockType_t Player::blockHit(const std::shared_ptr &attacker, const CombatType_t &combatType, int32_t &damage, bool checkDefense /* = false*/, bool checkArmor /* = false*/, bool field /* = false*/) { +bool Player::isPzLocked() const { + return pzLocked; +} + +BlockType_t Player::blockHit(const std::shared_ptr &attacker, const CombatType_t &combatType, int32_t &damage, bool checkDefense, bool checkArmor, bool field) { BlockType_t blockType = Creature::blockHit(attacker, combatType, damage, checkDefense, checkArmor, field); if (attacker) { sendCreatureSquare(attacker, SQ_COLOR_BLACK); @@ -2699,6 +3385,61 @@ BlockType_t Player::blockHit(const std::shared_ptr &attacker, const Co return blockType; } +void Player::doAttacking(uint32_t interval) { + if (lastAttack == 0) { + lastAttack = OTSYS_TIME() - getAttackSpeed() - 1; + } + + if (hasCondition(CONDITION_PACIFIED)) { + return; + } + + const auto &attackedCreature = getAttackedCreature(); + if (!attackedCreature) { + return; + } + + if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) { + bool result = false; + + const auto &tool = getWeapon(); + const auto &weapon = g_weapons().getWeapon(tool); + uint32_t delay = getAttackSpeed(); + bool classicSpeed = g_configManager().getBoolean(CLASSIC_ATTACK_SPEED); + + if (weapon) { + if (!weapon->interruptSwing()) { + result = weapon->useWeapon(static_self_cast(), tool, attackedCreature); + } else if (!classicSpeed && !canDoAction()) { + delay = getNextActionTime(); + } else { + result = weapon->useWeapon(static_self_cast(), tool, attackedCreature); + } + } else if (hasWeaponDistanceEquipped()) { + return; + } else { + result = Weapon::useFist(static_self_cast(), attackedCreature); + } + + const auto &task = createPlayerTask( + std::max(SCHEDULER_MINTICKS, delay), + [playerId = getID()] { g_game().checkCreatureAttack(playerId); }, + __FUNCTION__ + ); + + if (!classicSpeed) { + setNextActionTask(task, false); + } else { + g_dispatcher().scheduleEvent(task); + } + + if (result) { + updateLastAggressiveAction(); + updateLastAttack(); + } + } +} + void Player::death(const std::shared_ptr &lastHitCreature) { if (!g_configManager().getBoolean(TOGGLE_MOUNT_IN_PZ) && isMounted()) { dismount(); @@ -3052,6 +3793,10 @@ void Player::addInFightTicks(bool pzlock /*= false*/) { "Player::addInFightTicks"); } +void Player::setDailyReward(uint8_t reward) { + this->isDailyReward = reward; +} + void Player::removeList() { g_game().removePlayer(static_self_cast()); @@ -3077,26 +3822,23 @@ void Player::removePlayer(bool displayEffect, bool forced /*= true*/) { } } -// close container and its child containers -void Player::autoCloseContainers(const std::shared_ptr &container) { - std::vector closeList; - for (const auto &[containerId, containerInfo] : openContainers) { - auto tmpContainer = containerInfo.container; - while (tmpContainer) { - if (tmpContainer->isRemoved() || tmpContainer == container) { - closeList.emplace_back(containerId); - break; - } +uint64_t Player::getExpForLevel(const uint32_t level) { + return (((level - 6ULL) * level + 17ULL) * level - 12ULL) / 6ULL * 100ULL; +} - tmpContainer = std::dynamic_pointer_cast(tmpContainer->getParent()); - } +uint16_t Player::getStaminaMinutes() const { + return staminaMinutes; +} + +void Player::sendItemsPrice() const { + if (client) { + client->sendItemsPrice(); } +} - for (const uint32_t containerId : closeList) { - closeContainer(containerId); - if (client) { - client->sendCloseContainer(containerId); - } +void Player::sendForgingData() const { + if (client) { + client->sendForgingData(); } } @@ -3881,6 +4623,80 @@ bool Player::removeItemCountById(uint16_t itemId, uint32_t itemAmount, bool remo return false; } +void Player::addItemOnStash(uint16_t itemId, uint32_t amount) { + const auto it = stashItems.find(itemId); + if (it != stashItems.end()) { + stashItems[itemId] += amount; + return; + } + + stashItems[itemId] = amount; +} + +uint32_t Player::getStashItemCount(uint16_t itemId) const { + const auto it = stashItems.find(itemId); + if (it != stashItems.end()) { + return it->second; + } + return 0; +} + +bool Player::withdrawItem(uint16_t itemId, uint32_t amount) { + const auto it = stashItems.find(itemId); + if (it != stashItems.end()) { + if (it->second > amount) { + stashItems[itemId] -= amount; + } else if (it->second == amount) { + stashItems.erase(itemId); + } else { + return false; + } + return true; + } + return false; +} + +StashItemList Player::getStashItems() const { + return stashItems; +} + +uint32_t Player::getBaseCapacity() const { + if (hasFlag(PlayerFlags_t::CannotPickupItem)) { + return 0; + } + if (hasFlag(PlayerFlags_t::HasInfiniteCapacity)) { + return std::numeric_limits::max(); + } + return capacity; +} + +uint32_t Player::getCapacity() const { + if (hasFlag(PlayerFlags_t::CannotPickupItem)) { + return 0; + } + if (hasFlag(PlayerFlags_t::HasInfiniteCapacity)) { + return std::numeric_limits::max(); + } + return capacity + bonusCapacity + varStats[STAT_CAPACITY] + (m_wheelPlayer->getStat(WheelStat_t::CAPACITY) * 100); +} + +uint32_t Player::getBonusCapacity() const { + if (hasFlag(PlayerFlags_t::CannotPickupItem) || hasFlag(PlayerFlags_t::HasInfiniteCapacity)) { + return std::numeric_limits::max(); + } + return bonusCapacity; +} + +uint32_t Player::getFreeCapacity() const { + if (hasFlag(PlayerFlags_t::CannotPickupItem)) { + return 0; + } else if (hasFlag(PlayerFlags_t::HasInfiniteCapacity)) { + return std::numeric_limits::max(); + } else { + return std::max(0, getCapacity() - inventoryWeight); + } +} + ItemsTierCountList Player::getInventoryItemsId(bool ignoreStoreInbox /* false */) const { ItemsTierCountList itemMap; for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; i++) { @@ -3906,6 +4722,191 @@ ItemsTierCountList Player::getInventoryItemsId(bool ignoreStoreInbox /* false */ return itemMap; } +/******************************************************************************* + * Hazard system + ******************************************************************************/ +// Parser + +void Player::parseAttackRecvHazardSystem(CombatDamage &damage, const std::shared_ptr &monster) { + if (!monster || !monster->getHazard()) { + return; + } + + if (!g_configManager().getBoolean(TOGGLE_HAZARDSYSTEM)) { + return; + } + + if (damage.primary.type == COMBAT_HEALING) { + return; + } + + auto points = getHazardSystemPoints(); + if (m_party) { + for (const auto &partyMember : m_party->getMembers()) { + if (partyMember && partyMember->getHazardSystemPoints() < points) { + points = partyMember->getHazardSystemPoints(); + } + } + + if (m_party->getLeader() && m_party->getLeader()->getHazardSystemPoints() < points) { + points = m_party->getLeader()->getHazardSystemPoints(); + } + } + + if (points == 0) { + return; + } + + uint16_t stage = 0; + auto chance = static_cast(normal_random(1, 10000)); + auto critChance = g_configManager().getNumber(HAZARD_CRITICAL_CHANCE); + // Critical chance + if (monster->getHazardSystemCrit() && (lastHazardSystemCriticalHit + g_configManager().getNumber(HAZARD_CRITICAL_INTERVAL)) <= OTSYS_TIME() && chance <= critChance && !damage.critical) { + damage.critical = true; + damage.extension = true; + damage.exString = "(Hazard)"; + + stage = (points - 1) * static_cast(g_configManager().getNumber(HAZARD_CRITICAL_MULTIPLIER)); + damage.primary.value += static_cast(std::ceil((static_cast(damage.primary.value) * (5000 + stage)) / 10000)); + damage.secondary.value += static_cast(std::ceil((static_cast(damage.secondary.value) * (5000 + stage)) / 10000)); + lastHazardSystemCriticalHit = OTSYS_TIME(); + } + + // To prevent from punish the player twice with critical + damage boost, just uncomment code from the if + if (monster->getHazardSystemDamageBoost() /* && !damage.critical*/) { + stage = points * static_cast(g_configManager().getNumber(HAZARD_DAMAGE_MULTIPLIER)); + if (stage != 0) { + damage.extension = true; + damage.exString = "(Hazard)"; + damage.primary.value += static_cast(std::ceil((static_cast(damage.primary.value) * stage) / 10000)); + if (damage.secondary.value != 0) { + damage.secondary.value += static_cast(std::ceil((static_cast(damage.secondary.value) * stage) / 10000)); + } + } + } +} + +void Player::parseAttackDealtHazardSystem(CombatDamage &damage, const std::shared_ptr &monster) const { + if (!g_configManager().getBoolean(TOGGLE_HAZARDSYSTEM)) { + return; + } + + if (!monster || !monster->getHazard()) { + return; + } + + if (damage.primary.type == COMBAT_HEALING) { + return; + } + + auto points = getHazardSystemPoints(); + if (m_party) { + for (const auto &partyMember : m_party->getMembers()) { + if (partyMember && partyMember->getHazardSystemPoints() < points) { + points = partyMember->getHazardSystemPoints(); + } + } + + if (m_party->getLeader() && m_party->getLeader()->getHazardSystemPoints() < points) { + points = m_party->getLeader()->getHazardSystemPoints(); + } + } + + if (points == 0) { + return; + } + + // Dodge chance + uint16_t stage; + if (monster->getHazardSystemDodge()) { + stage = points * g_configManager().getNumber(HAZARD_DODGE_MULTIPLIER); + auto chance = static_cast(normal_random(1, 10000)); + if (chance <= stage) { + damage.primary.value = 0; + damage.secondary.value = 0; + return; + } + } + if (monster->getHazardSystemDefenseBoost()) { + stage = points * static_cast(g_configManager().getNumber(HAZARD_DEFENSE_MULTIPLIER)); + if (stage != 0) { + damage.exString = fmt::format("(hazard -{}%)", stage / 100.); + damage.primary.value -= static_cast(std::ceil((static_cast(damage.primary.value) * stage) / 10000)); + if (damage.secondary.value != 0) { + damage.secondary.value -= static_cast(std::ceil((static_cast(damage.secondary.value) * stage) / 10000)); + } + } + } +} + +// Points get: +// Points increase: +void Player::setHazardSystemPoints(int32_t count) { + if (!g_configManager().getBoolean(TOGGLE_HAZARDSYSTEM)) { + return; + } + addStorageValue(STORAGEVALUE_HAZARDCOUNT, std::max(0, std::min(0xFFFF, count)), true); + reloadHazardSystemPointsCounter = true; + if (count > 0) { + setIcon("hazard", CreatureIcon(CreatureIconQuests_t::Hazard, count)); + } else { + removeIcon("hazard"); + } +} + +uint16_t Player::getHazardSystemPoints() const { + const int32_t points = getStorageValue(STORAGEVALUE_HAZARDCOUNT); + if (points <= 0) { + return 0; + } + return static_cast(std::max(0, std::min(0xFFFF, points))); +} + +// Concoction system + +void Player::updateConcoction(uint16_t itemId, uint16_t timeLeft) { + if (timeLeft == 0) { + activeConcoctions.erase(itemId); + } else { + activeConcoctions[itemId] = timeLeft; + } +} + +std::map Player::getActiveConcoctions() const { + return activeConcoctions; +} + +bool Player::isConcoctionActive(Concoction_t concotion) const { + const auto itemId = static_cast(concotion); + if (!activeConcoctions.contains(itemId)) { + return false; + } + const auto timeLeft = activeConcoctions.at(itemId); + return timeLeft > 0; +} + +bool Player::checkAutoLoot(bool isBoss) const { + if (!g_configManager().getBoolean(AUTOLOOT)) { + return false; + } + if (g_configManager().getBoolean(VIP_SYSTEM_ENABLED) && g_configManager().getBoolean(VIP_AUTOLOOT_VIP_ONLY) && !isVip()) { + return false; + } + + auto featureKV = kv()->scoped("features")->get("autoloot"); + auto value = featureKV.has_value() ? featureKV->getNumber() : 0; + if (value == 2) { + return true; + } else if (value == 1) { + return !isBoss; + } + return false; +} + +QuickLootFilter_t Player::getQuickLootFilter() const { + return quickLootFilter; +} + std::vector> Player::getInventoryItemsFromId(uint16_t itemId, bool ignore /*= true*/) const { std::vector> itemVector; for (int i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) { @@ -4177,149 +5178,21 @@ std::shared_ptr Player::getThing(size_t index) const { return nullptr; } -void Player::postAddNotification(const std::shared_ptr &thing, const std::shared_ptr &oldParent, int32_t index, const CylinderLink_t link /*= LINK_OWNER*/) { - if (link == LINK_OWNER) { - // calling movement scripts - g_moveEvents().onPlayerEquip(getPlayer(), thing->getItem(), static_cast(index), false); +// TODO: review this function +bool Player::updateSaleShopList(const std::shared_ptr &item) { + const uint16_t itemId = item->getID(); + if (!itemId || !item) { + return true; } - bool requireListUpdate = true; - if (link == LINK_OWNER || link == LINK_TOPPARENT) { - const auto &item = oldParent ? oldParent->getItem() : nullptr; - const auto &container = item ? item->getContainer() : nullptr; - if (container) { - requireListUpdate = container->getHoldingPlayer() != getPlayer(); - } else { - requireListUpdate = oldParent != getPlayer(); - } + g_dispatcher().addEvent([creatureId = getID()] { g_game().updatePlayerSaleItems(creatureId); }, __FUNCTION__); + scheduledSaleUpdate = true; + return true; +} - updateInventoryWeight(); - updateItemsLight(); - sendInventoryIds(); - sendStats(); - } - - if (const auto &item = thing->getItem()) { - if (const auto &container = item->getContainer()) { - onSendContainer(container); - } - - if (shopOwner && !scheduledSaleUpdate && requireListUpdate) { - updateSaleShopList(item); - } - } else if (const auto &creature = thing->getCreature()) { - if (creature == getPlayer()) { - // check containers - std::vector> containers; - - for (const auto &[containerId, containerInfo] : openContainers) { - const auto &container = containerInfo.container; - if (container == nullptr) { - continue; - } - - if (!Position::areInRange<1, 1, 0>(container->getPosition(), getPosition())) { - containers.emplace_back(container); - } - } - - for (const auto &container : containers) { - autoCloseContainers(container); - } - } - } -} - -void Player::postRemoveNotification(const std::shared_ptr &thing, const std::shared_ptr &newParent, int32_t index, CylinderLink_t link /*= LINK_OWNER*/) { - if (!thing) { - return; - } - - const auto copyThing = thing; - const auto copyNewParent = newParent; - - if (link == LINK_OWNER) { - if (const auto &item = copyThing->getItem()) { - g_moveEvents().onPlayerDeEquip(getPlayer(), item, static_cast(index)); - } - } - bool requireListUpdate = true; - - if (link == LINK_OWNER || link == LINK_TOPPARENT) { - const auto &item = copyNewParent ? copyNewParent->getItem() : nullptr; - const auto &container = item ? item->getContainer() : nullptr; - if (container) { - requireListUpdate = container->getHoldingPlayer() != getPlayer(); - } else { - requireListUpdate = copyNewParent != getPlayer(); - } - - updateInventoryWeight(); - updateItemsLight(); - sendInventoryIds(); - sendStats(); - } - - if (const auto &item = copyThing->getItem()) { - if (const auto &container = item->getContainer()) { - checkLootContainers(container); - - if (container->isRemoved() || !Position::areInRange<1, 1, 0>(getPosition(), container->getPosition())) { - autoCloseContainers(container); - } else if (container->getTopParent() == getPlayer()) { - onSendContainer(container); - } else if (const auto &topContainer = std::dynamic_pointer_cast(container->getTopParent())) { - if (const auto &depotChest = std::dynamic_pointer_cast(topContainer)) { - bool isOwner = false; - - for (const auto &[depotId, depotChestMap] : depotChests) { - if (depotId == 0) { - continue; - } - - if (depotChestMap == depotChest) { - isOwner = true; - onSendContainer(container); - } - } - - if (!isOwner) { - autoCloseContainers(container); - } - } else { - onSendContainer(container); - } - } else { - autoCloseContainers(container); - } - } - - // force list update if item exists tier - if (item->getTier() > 0 && !requireListUpdate) { - requireListUpdate = true; - } - - if (shopOwner && !scheduledSaleUpdate && requireListUpdate) { - updateSaleShopList(item); - } - } -} - -// TODO: review this function -bool Player::updateSaleShopList(const std::shared_ptr &item) { - const uint16_t itemId = item->getID(); - if (!itemId || !item) { - return true; - } - - g_dispatcher().addEvent([creatureId = getID()] { g_game().updatePlayerSaleItems(creatureId); }, __FUNCTION__); - scheduledSaleUpdate = true; - return true; -} - -bool Player::hasShopItemForSale(uint16_t itemId, uint8_t subType) const { - if (!shopOwner) { - return false; +bool Player::hasShopItemForSale(uint16_t itemId, uint8_t subType) const { + if (!shopOwner) { + return false; } const ItemType &itemType = Item::items[itemId]; @@ -4354,6 +5227,30 @@ void Player::internalAddThing(uint32_t index, const std::shared_ptr &thin } } +// safe-trade functions + +void Player::setTradeState(TradeState_t state) { + tradeState = state; +} + +TradeState_t Player::getTradeState() const { + return tradeState; +} + +std::shared_ptr Player::getTradeItem() { + return tradeItem; +} + +// shop functions + +void Player::setShopOwner(std::shared_ptr owner) { + shopOwner = std::move(owner); +} + +std::shared_ptr Player::getShopOwner() const { + return shopOwner; +} + bool Player::setFollowCreature(const std::shared_ptr &creature) { if (!Creature::setFollowCreature(creature)) { setFollowCreature(nullptr); @@ -4407,61 +5304,6 @@ void Player::getPathSearchParams(const std::shared_ptr &creature, Find fpp.fullPathSearch = true; } -void Player::doAttacking(uint32_t) { - if (lastAttack == 0) { - lastAttack = OTSYS_TIME() - getAttackSpeed() - 1; - } - - if (hasCondition(CONDITION_PACIFIED)) { - return; - } - - const auto &attackedCreature = getAttackedCreature(); - if (!attackedCreature) { - return; - } - - if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) { - bool result = false; - - const auto &tool = getWeapon(); - const auto &weapon = g_weapons().getWeapon(tool); - uint32_t delay = getAttackSpeed(); - bool classicSpeed = g_configManager().getBoolean(CLASSIC_ATTACK_SPEED); - - if (weapon) { - if (!weapon->interruptSwing()) { - result = weapon->useWeapon(static_self_cast(), tool, attackedCreature); - } else if (!classicSpeed && !canDoAction()) { - delay = getNextActionTime(); - } else { - result = weapon->useWeapon(static_self_cast(), tool, attackedCreature); - } - } else if (hasWeaponDistanceEquipped()) { - return; - } else { - result = Weapon::useFist(static_self_cast(), attackedCreature); - } - - const auto &task = createPlayerTask( - std::max(SCHEDULER_MINTICKS, delay), - [playerId = getID()] { g_game().checkCreatureAttack(playerId); }, - __FUNCTION__ - ); - - if (!classicSpeed) { - setNextActionTask(task, false); - } else { - g_dispatcher().scheduleEvent(task); - } - - if (result) { - updateLastAggressiveAction(); - updateLastAttack(); - } - } -} - uint64_t Player::getGainedExperience(const std::shared_ptr &attacker) const { if (g_configManager().getBoolean(EXPERIENCE_FROM_PLAYERS)) { const auto &attackerPlayer = attacker->getPlayer(); @@ -4497,6 +5339,22 @@ void Player::setChaseMode(bool mode) { } } +void Player::setFightMode(FightMode_t mode) { + fightMode = mode; +} + +void Player::setSecureMode(bool mode) { + secureMode = mode; +} + +Faction_t Player::getFaction() const { + return faction; +} + +void Player::setFaction(Faction_t factionId) { + faction = factionId; +} + void Player::onWalkAborted() { setNextWalkActionTask(nullptr); sendCancelWalk(); @@ -4989,27 +5847,6 @@ bool Player::canWear(uint16_t lookType, uint8_t addons) const { return false; } -bool Player::canLogout() { - if (isConnecting) { - return false; - } - - const auto &tile = getTile(); - if (!tile) { - return false; - } - - if (tile->hasFlag(TILESTATE_NOLOGOUT)) { - return false; - } - - if (tile->hasFlag(TILESTATE_PROTECTIONZONE)) { - return true; - } - - return !isPzLocked() && !hasCondition(CONDITION_INFIGHT); -} - void Player::genReservedStorageRange() { // generate outfits range uint32_t outfits_key = PSTRG_OUTFITS_RANGE_START; @@ -5023,6 +5860,24 @@ void Player::genReservedStorageRange() { } } +void Player::setSpecialMenuAvailable(bool supplyStashBool, bool marketMenuBool, bool depotSearchBool) { + // Closing depot search when player have special container disabled and it's still open. + if (isDepotSearchOpen() && !depotSearchBool && depotSearch) { + depotSearchOnItem = { 0, 0 }; + sendCloseDepotSearch(); + } + + // Menu option 'stow, stow container ...' + // Menu option 'show in market' + // Menu option to open depot search + supplyStash = supplyStashBool; + marketMenu = marketMenuBool; + depotSearch = depotSearchBool; + if (client) { + client->sendSpecialContainersAvailable(); + } +} + void Player::addOutfit(uint16_t lookType, uint8_t addons) { for (auto &outfitEntry : outfits) { if (outfitEntry.lookType == lookType) { @@ -5146,39 +6001,135 @@ bool Player::getFamiliar(const std::shared_ptr &familiar) const { return true; } -void Player::setSex(PlayerSex_t newSex) { - sex = newSex; +void Player::setFamiliarLooktype(uint16_t familiarLooktype) { + this->defaultOutfit.lookFamiliarsType = familiarLooktype; } -void Player::setPronoun(PlayerPronoun_t newPronoun) { - pronoun = newPronoun; -} +bool Player::canLogout() { + if (isConnecting) { + return false; + } -Skulls_t Player::getSkull() const { - if (hasFlag(PlayerFlags_t::NotGainInFight)) { - return SKULL_NONE; + const auto &tile = getTile(); + if (!tile) { + return false; } - return skull; -} -Skulls_t Player::getSkullClient(const std::shared_ptr &creature) { - if (!creature || g_game().getWorldType() != WORLD_TYPE_PVP) { - return SKULL_NONE; + if (tile->hasFlag(TILESTATE_NOLOGOUT)) { + return false; } - const auto &player = creature->getPlayer(); - if (player && player->getSkull() == SKULL_NONE) { - if (player.get() == this) { - if (std::ranges::any_of(unjustifiedKills, [&](const auto &kill) { - return kill.unavenged && (getTimeNow() - kill.time) < g_configManager().getNumber(ORANGE_SKULL_DURATION) * 24 * 60 * 60; - })) { - return SKULL_ORANGE; - } - } + if (tile->hasFlag(TILESTATE_PROTECTIONZONE)) { + return true; + } - if (player->hasKilled(getPlayer())) { - return SKULL_ORANGE; - } + return !isPzLocked() && !hasCondition(CONDITION_INFIGHT); +} + +bool Player::hasKilled(const std::shared_ptr &player) const { + return std::ranges::any_of(unjustifiedKills, [&](const auto &kill) { + return kill.target == player->getGUID() && (getTimeNow() - kill.time) < g_configManager().getNumber(ORANGE_SKULL_DURATION) * 24 * 60 * 60 && kill.unavenged; + }); +} + +size_t Player::getMaxDepotItems() const { + if (group->maxDepotItems != 0) { + return group->maxDepotItems; + } + if (isPremium()) { + return g_configManager().getNumber(PREMIUM_DEPOT_LIMIT); + } + return g_configManager().getNumber(FREE_DEPOT_LIMIT); +} + +// tile +// send methods +// tile +// send methods + +void Player::sendAddTileItem(const std::shared_ptr &itemTile, const Position &pos, const std::shared_ptr &item) { + if (client) { + int32_t stackpos = itemTile->getStackposOfItem(static_self_cast(), item); + if (stackpos != -1) { + client->sendAddTileItem(pos, stackpos, item); + } + } +} + +void Player::sendUpdateTileItem(const std::shared_ptr &updateTile, const Position &pos, const std::shared_ptr &item) { + if (client) { + int32_t stackpos = updateTile->getStackposOfItem(static_self_cast(), item); + if (stackpos != -1) { + client->sendUpdateTileItem(pos, stackpos, item); + } + } +} + +void Player::sendRemoveTileThing(const Position &pos, int32_t stackpos) const { + if (stackpos != -1 && client) { + client->sendRemoveTileThing(pos, stackpos); + } +} + +void Player::sendUpdateTileCreature(const std::shared_ptr &creature) { + if (client) { + client->sendUpdateTileCreature(creature->getPosition(), creature->getTile()->getClientIndexOfCreature(static_self_cast(), creature), creature); + } +} + +std::string Player::getObjectPronoun() const { + return getPlayerObjectPronoun(pronoun, sex, name); +} + +std::string Player::getSubjectPronoun() const { + return getPlayerSubjectPronoun(pronoun, sex, name); +} + +std::string Player::getPossessivePronoun() const { + return getPlayerPossessivePronoun(pronoun, sex, name); +} + +std::string Player::getReflexivePronoun() const { + return getPlayerReflexivePronoun(pronoun, sex, name); +} + +std::string Player::getSubjectVerb(bool past) const { + return getVerbForPronoun(pronoun, past); +} + +void Player::setSex(PlayerSex_t newSex) { + sex = newSex; +} + +void Player::setPronoun(PlayerPronoun_t newPronoun) { + pronoun = newPronoun; +} + +Skulls_t Player::getSkull() const { + if (hasFlag(PlayerFlags_t::NotGainInFight)) { + return SKULL_NONE; + } + return skull; +} + +Skulls_t Player::getSkullClient(const std::shared_ptr &creature) { + if (!creature || g_game().getWorldType() != WORLD_TYPE_PVP) { + return SKULL_NONE; + } + + const auto &player = creature->getPlayer(); + if (player && player->getSkull() == SKULL_NONE) { + if (player.get() == this) { + if (std::ranges::any_of(unjustifiedKills, [&](const auto &kill) { + return kill.unavenged && (getTimeNow() - kill.time) < g_configManager().getNumber(ORANGE_SKULL_DURATION) * 24 * 60 * 60; + })) { + return SKULL_ORANGE; + } + } + + if (player->hasKilled(getPlayer())) { + return SKULL_ORANGE; + } if (player->hasAttacked(getPlayer())) { return SKULL_YELLOW; @@ -5191,10 +6142,12 @@ Skulls_t Player::getSkullClient(const std::shared_ptr &creature) { return Creature::getSkullClient(creature); } -bool Player::hasKilled(const std::shared_ptr &player) const { - return std::ranges::any_of(unjustifiedKills, [&](const auto &kill) { - return kill.target == player->getGUID() && (getTimeNow() - kill.time) < g_configManager().getNumber(ORANGE_SKULL_DURATION) * 24 * 60 * 60 && kill.unavenged; - }); +int64_t Player::getSkullTicks() const { + return skullTicks; +} + +void Player::setSkullTicks(int64_t ticks) { + skullTicks = ticks; } bool Player::hasAttacked(const std::shared_ptr &attacked) const { @@ -5266,6 +6219,18 @@ void Player::addUnjustifiedDead(const std::shared_ptr &attacked) { sendUnjustifiedPoints(); } +void Player::sendCreatureEmblem(const std::shared_ptr &creature) const { + if (client) { + client->sendCreatureEmblem(creature); + } +} + +void Player::sendCreatureSkull(const std::shared_ptr &creature) const { + if (client) { + client->sendCreatureSkull(creature); + } +} + void Player::checkSkullTicks(int64_t ticks) { const int64_t newTicks = skullTicks - ticks; if (newTicks < 0) { @@ -5279,11 +6244,41 @@ void Player::checkSkullTicks(int64_t ticks) { } } +void Player::updateBaseSpeed() { + if (baseSpeed >= PLAYER_MAX_SPEED) { + return; + } + + if (!hasFlag(PlayerFlags_t::SetMaxSpeed)) { + baseSpeed = static_cast(vocation->getBaseSpeed() + (level - 1)); + } else { + baseSpeed = PLAYER_MAX_SPEED; + } +} + bool Player::isPromoted() const { const uint16_t promotedVocation = g_vocations().getPromotedVocation(vocation->getId()); return promotedVocation == VOCATION_NONE && vocation->getId() != promotedVocation; } +uint32_t Player::getAttackSpeed() const { + bool onFistAttackSpeed = g_configManager().getBoolean(TOGGLE_ATTACK_SPEED_ONFIST); + uint32_t MAX_ATTACK_SPEED = g_configManager().getNumber(MAX_SPEED_ATTACKONFIST); + if (onFistAttackSpeed) { + uint32_t baseAttackSpeed = vocation->getAttackSpeed(); + uint32_t skillLevel = getSkillLevel(SKILL_FIST); + uint32_t attackSpeed = baseAttackSpeed - (skillLevel * g_configManager().getNumber(MULTIPLIER_ATTACKONFIST)); + + if (attackSpeed < MAX_ATTACK_SPEED) { + attackSpeed = MAX_ATTACK_SPEED; + } + + return attackSpeed; + } else { + return vocation->getAttackSpeed(); + } +} + double Player::getLostPercent() const { int32_t blessingCount = 0; const uint8_t maxBlessing = (operatingSystem == CLIENTOS_NEW_WINDOWS || operatingSystem == CLIENTOS_NEW_MAC) ? 8 : 6; @@ -5320,28 +6315,12 @@ double Player::getLostPercent() const { return lossPercent * (1 - (percentReduction / 100.)) / 100.; } -void Player::learnInstantSpell(const std::string &spellName) { - if (!hasLearnedInstantSpell(spellName)) { - learnedInstantSpellList.emplace_back(spellName); - } -} - -void Player::forgetInstantSpell(const std::string &spellName) { - std::erase(learnedInstantSpellList, spellName); +[[nodiscard]] const std::string &Player::getGuildNick() const { + return guildNick; } -bool Player::hasLearnedInstantSpell(const std::string &spellName) const { - if (hasFlag(PlayerFlags_t::CannotUseSpells)) { - return false; - } - - if (hasFlag(PlayerFlags_t::IgnoreSpellCheck)) { - return true; - } - - return std::ranges::any_of(learnedInstantSpellList, [&](const auto &learnedSpellName) { - return strcasecmp(learnedSpellName.c_str(), spellName.c_str()) == 0; - }); +void Player::setGuildNick(std::string nick) { + guildNick = std::move(nick); } bool Player::isInWar(const std::shared_ptr &player) const { @@ -5396,16 +6375,6 @@ uint32_t Player::getLoyaltyMagicLevel() const { return level; } -uint32_t Player::getCapacity() const { - if (hasFlag(PlayerFlags_t::CannotPickupItem)) { - return 0; - } - if (hasFlag(PlayerFlags_t::HasInfiniteCapacity)) { - return std::numeric_limits::max(); - } - return capacity + bonusCapacity + varStats[STAT_CAPACITY] + (m_wheelPlayer->getStat(WheelStat_t::CAPACITY) * 100); -} - int32_t Player::getMaxHealth() const { return std::max(1, healthMax + varStats[STAT_MAXHITPOINTS] + m_wheelPlayer->getStat(WheelStat_t::HEALTH)); } @@ -5414,10 +6383,15 @@ uint32_t Player::getMaxMana() const { return std::max(0, manaMax + varStats[STAT_MAXMANAPOINTS] + m_wheelPlayer->getStat(WheelStat_t::MANA)); } +bool Player::hasExtraSwing() { + return lastAttack > 0 && !checkLastAttackWithin(getAttackSpeed()); +} + uint16_t Player::getSkillLevel(skills_t skill) const { auto skillLevel = getLoyaltySkill(skill); skillLevel = std::max(0, skillLevel + varSkills[skill]); + const auto &maxValuePerSkill = getMaxValuePerSkill(); if (const auto it = maxValuePerSkill.find(skill); it != maxValuePerSkill.end()) { skillLevel = std::min(it->second, skillLevel); @@ -5482,29 +6456,16 @@ time_t Player::getPremiumLastDay() const { return account->getPremiumLastDay(); } +bool Player::isVip() const { + return g_configManager().getBoolean(VIP_SYSTEM_ENABLED) && (getPremiumDays() > 0 || getPremiumLastDay() > getTimeNow()); +} + void Player::setTibiaCoins(int32_t v) { coinBalance = v; } -int32_t Player::getCleavePercent(bool useCharges) const { - int32_t result = cleavePercent; - for (const auto &item : getEquippedItems()) { - const ItemType &it = Item::items[item->getID()]; - if (!it.abilities) { - continue; - } - - const int32_t &cleave_percent = it.abilities->cleavePercent; - if (cleave_percent != 0) { - result += cleave_percent; - const uint16_t charges = item->getCharges(); - if (useCharges && charges != 0) { - g_game().transformItem(item, item->getID(), charges - 1); - } - } - } - - return result; +void Player::setCleavePercent(int32_t value) { + cleavePercent = std::max(0, cleavePercent + value); } int32_t Player::getPerfectShotDamage(uint8_t range, bool useCharges) const { @@ -5536,6 +6497,17 @@ int32_t Player::getPerfectShotDamage(uint8_t range, bool useCharges) const { return result; } +void Player::setPerfectShotDamage(uint8_t range, int32_t damage) { + int32_t actualDamage = getPerfectShotDamage(range); + const bool aboveZero = (actualDamage != 0); + actualDamage += damage; + if (actualDamage == 0 && aboveZero) { + perfectShot.erase(range); + } else { + perfectShot[range] = actualDamage; + } +} + int32_t Player::getSpecializedMagicLevel(CombatType_t combat, bool useCharges) const { int32_t result = specializedMagicLevel[combatTypeToIndex(combat)]; for (const auto &item : getEquippedItems()) { @@ -5557,6 +6529,10 @@ int32_t Player::getSpecializedMagicLevel(CombatType_t combat, bool useCharges) c return result; } +void Player::setSpecializedMagicLevel(CombatType_t combat, int32_t value) { + specializedMagicLevel[combatTypeToIndex(combat)] = std::max(0, specializedMagicLevel[combatTypeToIndex(combat)] + value); +} + int32_t Player::getMagicShieldCapacityFlat(bool useCharges) const { int32_t result = magicShieldCapacityFlat; for (const auto &item : getEquippedItems()) { @@ -5578,6 +6554,10 @@ int32_t Player::getMagicShieldCapacityFlat(bool useCharges) const { return result; } +void Player::setMagicShieldCapacityFlat(int32_t value) { + magicShieldCapacityFlat += value; +} + int32_t Player::getMagicShieldCapacityPercent(bool useCharges) const { int32_t result = magicShieldCapacityPercent; for (const auto &item : getEquippedItems()) { @@ -5599,6 +6579,10 @@ int32_t Player::getMagicShieldCapacityPercent(bool useCharges) const { return result; } +void Player::setMagicShieldCapacityPercent(int32_t value) { + magicShieldCapacityPercent += value; +} + double_t Player::getReflectPercent(CombatType_t combat, bool useCharges) const { double_t result = reflectPercent[combatTypeToIndex(combat)]; for (const auto &item : getEquippedItems()) { @@ -5713,13 +6697,6 @@ bool Player::isPartner(const std::shared_ptr &player) const { return m_party == player->m_party; } -bool Player::isGuildMate(const std::shared_ptr &player) const { - if (!player || !guild) { - return false; - } - return guild == player->guild; -} - void Player::sendPlayerPartyIcons(const std::shared_ptr &player) const { sendPartyCreatureShield(player); sendPartyCreatureSkull(player); @@ -5772,6 +6749,30 @@ GuildEmblems_t Player::getGuildEmblem(const std::shared_ptr &player) con return GUILDEMBLEM_NEUTRAL; } +uint64_t Player::getSpentMana() const { + return manaSpent; +} + +bool Player::hasFlag(PlayerFlags_t flag) const { + return group->flags[static_cast(flag)]; +} + +void Player::setFlag(PlayerFlags_t flag) const { + group->flags[static_cast(flag)] = true; +} + +void Player::removeFlag(PlayerFlags_t flag) const { + group->flags[static_cast(flag)] = false; +} + +std::shared_ptr Player::getBedItem() { + return bedItem; +} + +void Player::setBedItem(std::shared_ptr b) { + bedItem = std::move(b); +} + void Player::sendUnjustifiedPoints() const { if (client) { double dayKills = 0; @@ -5829,7 +6830,7 @@ void Player::setCurrentMount(uint8_t mount) { } bool Player::hasAnyMount() const { - const auto &mounts = g_game().mounts.getMounts(); + const auto &mounts = g_game().mounts->getMounts(); return std::ranges::any_of(mounts, [&](const auto &mount) { return hasMount(mount); }); @@ -5837,7 +6838,7 @@ bool Player::hasAnyMount() const { uint8_t Player::getRandomMountId() const { std::vector playerMounts; - const auto mounts = g_game().mounts.getMounts(); + const auto mounts = g_game().mounts->getMounts(); for (const auto &mount : mounts) { if (hasMount(mount)) { playerMounts.emplace_back(mount->id); @@ -5885,7 +6886,7 @@ bool Player::toggleMount(bool mount) { currentMountId = getRandomMountId(); } - const auto ¤tMount = g_game().mounts.getMountByID(currentMountId); + const auto ¤tMount = g_game().mounts->getMountByID(currentMountId); if (!currentMount) { return false; } @@ -5928,7 +6929,7 @@ bool Player::toggleMount(bool mount) { } bool Player::tameMount(uint8_t mountId) { - if (!g_game().mounts.getMountByID(mountId)) { + if (!g_game().mounts->getMountByID(mountId)) { return false; } @@ -5947,7 +6948,7 @@ bool Player::tameMount(uint8_t mountId) { } bool Player::untameMount(uint8_t mountId) { - if (!g_game().mounts.getMountByID(mountId)) { + if (!g_game().mounts->getMountByID(mountId)) { return false; } @@ -5995,7 +6996,7 @@ bool Player::hasMount(const std::shared_ptr &mount) const { } void Player::dismount() { - const auto &mount = g_game().mounts.getMountByID(getCurrentMount()); + const auto &mount = g_game().mounts->getMountByID(getCurrentMount()); if (mount && mount->speed > 0) { g_game().changeSpeed(static_self_cast(), -mount->speed); } @@ -6152,6 +7153,34 @@ bool Player::addOfflineTrainingTries(skills_t skill, uint64_t tries) { return sendUpdate; } +void Player::addOfflineTrainingTime(int32_t addTime) { + offlineTrainingTime = std::min(12 * 3600 * 1000, offlineTrainingTime + addTime); +} + +void Player::removeOfflineTrainingTime(int32_t removeTime) { + offlineTrainingTime = std::max(0, offlineTrainingTime - removeTime); +} + +int32_t Player::getOfflineTrainingTime() const { + return offlineTrainingTime; +} + +int8_t Player::getOfflineTrainingSkill() const { + return offlineTrainingSkill; +} + +void Player::setOfflineTrainingSkill(int8_t skill) { + offlineTrainingSkill = skill; +} + +uint64_t Player::getBankBalance() const { + return bankBalance; +} + +void Player::setBankBalance(uint64_t balance) { + bankBalance = balance; +} + std::shared_ptr Player::getTown() const { return town; } @@ -6167,13 +7196,13 @@ void Player::onModalWindowHandled(uint32_t modalWindowId) { std::erase(modalWindows, modalWindowId); } -void Player::sendModalWindow(const ModalWindow &modalWindow) { - if (!client) { - return; +const Position &Player::getTemplePosition() const { + if (!town) { + static auto emptyPosition = Position(); + return emptyPosition; } - modalWindows.emplace_back(modalWindow.id); - client->sendModalWindow(modalWindow); + return town->getTemplePosition(); } void Player::clearModalWindows() { @@ -6205,1707 +7234,2810 @@ uint16_t Player::getHelpers() const { return 0u; } -void Player::sendClosePrivate(uint16_t channelId) { - if (channelId == CHANNEL_GUILD || channelId == CHANNEL_PARTY) { - g_chat().removeUserFromChannel(getPlayer(), channelId); +void Player::sendImbuementResult(const std::string &message) const { + if (client) { + client->sendImbuementResult(message); } +} +void Player::closeImbuementWindow() const { if (client) { - client->sendClosePrivate(channelId); + client->closeImbuementWindow(); } } -void Player::sendIcons() { - if (!client) { - return; +void Player::sendPodiumWindow(const std::shared_ptr &podium, const Position &position, uint16_t itemId, uint8_t stackpos) const { + if (client) { + client->sendPodiumWindow(podium, position, itemId, stackpos); } +} - // Iterates over the Bakragore icons to check if the player has any - auto iconBakragore = IconBakragore::None; - for (const auto &icon : magic_enum::enum_values()) { - if (icon == IconBakragore::None) { - continue; - } - - const auto &condition = getCondition(CONDITION_BAKRAGORE, CONDITIONID_DEFAULT, magic_enum::enum_integer(icon)); - if (condition) { - g_logger().debug("[{}] found active condition Bakragore with subId {}", __FUNCTION__, magic_enum::enum_integer(icon)); - iconBakragore = icon; - } +void Player::sendCloseContainer(uint8_t cid) const { + if (client) { + client->sendCloseContainer(cid); } +} - // Remove the last icon so that Bakragore's is added - auto iconSet = getClientIcons(); - if (iconSet.size() >= 9 && iconBakragore != IconBakragore::None) { - std::vector tempVector(iconSet.begin(), iconSet.end()); - tempVector.pop_back(); - iconSet = std::unordered_set(tempVector.begin(), tempVector.end()); +void Player::sendChannel(uint16_t channelId, const std::string &channelName, const UsersMap* channelUsers, const InvitedMap* invitedUsers) const { + if (client) { + client->sendChannel(channelId, channelName, channelUsers, invitedUsers); } +} - client->sendIcons(iconSet, iconBakragore); +void Player::sendTutorial(uint8_t tutorialId) const { + if (client) { + client->sendTutorial(tutorialId); + } } -void Player::sendIconBakragore(const IconBakragore icon) const { +void Player::sendAddMarker(const Position &pos, uint8_t markType, const std::string &desc) const { if (client) { - client->sendIconBakragore(icon); + client->sendAddMarker(pos, markType, desc); } } -void Player::removeBakragoreIcons() { - for (auto icon : magic_enum::enum_values()) { - if (hasCondition(CONDITION_BAKRAGORE, enumToValue(icon))) { - removeCondition(CONDITION_BAKRAGORE, CONDITIONID_DEFAULT, true); - } +void Player::sendItemInspection(uint16_t itemId, uint8_t itemCount, const std::shared_ptr &item, bool cyclopedia) const { + if (client) { + client->sendItemInspection(itemId, itemCount, item, cyclopedia); } } -void Player::removeBakragoreIcon(const IconBakragore icon) { - if (hasCondition(CONDITION_BAKRAGORE, enumToValue(icon))) { - removeCondition(CONDITION_BAKRAGORE, CONDITIONID_DEFAULT, true); +void Player::sendCyclopediaCharacterNoData(CyclopediaCharacterInfoType_t characterInfoType, uint8_t errorCode) const { + if (client) { + client->sendCyclopediaCharacterNoData(characterInfoType, errorCode); } } -void Player::sendCyclopediaCharacterAchievements(uint16_t secretsUnlocked, const std::vector> &achievementsUnlocked) const { +void Player::sendCyclopediaCharacterBaseInformation() const { if (client) { - client->sendCyclopediaCharacterAchievements(secretsUnlocked, achievementsUnlocked); + client->sendCyclopediaCharacterBaseInformation(); } } -uint64_t Player::getMoney() const { - uint64_t moneyCount = 0; - - auto countMoneyInContainer = [&moneyCount](const auto &self, const std::shared_ptr &container) -> void { - for (const auto &item : container->getItemList()) { - if (const auto &tmpContainer = item->getContainer()) { - self(self, tmpContainer); - } else { - moneyCount += item->getWorth(); - } - } - }; - - for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) { - const auto &item = inventory[i]; - if (!item) { - continue; - } - - if (const auto &container = item->getContainer()) { - countMoneyInContainer(countMoneyInContainer, container); - } else { - moneyCount += item->getWorth(); - } +void Player::sendCyclopediaCharacterGeneralStats() const { + if (client) { + client->sendCyclopediaCharacterGeneralStats(); } - - return moneyCount; } -std::pair Player::getForgeSliversAndCores() const { - uint64_t sliverCount = 0; - uint64_t coreCount = 0; - - // Check items from inventory - for (const auto &item : getAllInventoryItems()) { - if (!item) { - continue; - } - - sliverCount += item->getForgeSlivers(); - coreCount += item->getForgeCores(); +void Player::sendCyclopediaCharacterCombatStats() const { + if (client) { + client->sendCyclopediaCharacterCombatStats(); } +} - // Check items from stash - for (const auto &stashToSend = getStashItems(); - const auto &[itemId, itemCount] : stashToSend) { - if (itemId == ITEM_FORGE_SLIVER) { - sliverCount += itemCount; - } - if (itemId == ITEM_FORGE_CORE) { - coreCount += itemCount; - } +void Player::sendCyclopediaCharacterRecentDeaths(uint16_t page, uint16_t pages, const std::vector &entries) const { + if (client) { + client->sendCyclopediaCharacterRecentDeaths(page, pages, entries); } - - return std::make_pair(sliverCount, coreCount); } -size_t Player::getMaxDepotItems() const { - if (group->maxDepotItems != 0) { - return group->maxDepotItems; - } - if (isPremium()) { - return g_configManager().getNumber(PREMIUM_DEPOT_LIMIT); +void Player::sendCyclopediaCharacterRecentPvPKills(uint16_t page, uint16_t pages, const std::vector &entries) const { + if (client) { + client->sendCyclopediaCharacterRecentPvPKills(page, pages, entries); } - return g_configManager().getNumber(FREE_DEPOT_LIMIT); } -std::vector> Player::getMuteConditions() const { - std::vector> muteConditions; - muteConditions.reserve(conditions.size()); - - for (const auto &condition : conditions) { - if (condition->getTicks() <= 0) { - continue; - } - - const auto &type = condition->getType(); - if (type != CONDITION_MUTED && type != CONDITION_CHANNELMUTEDTICKS && type != CONDITION_YELLTICKS) { - continue; - } - - muteConditions.emplace_back(condition); +void Player::sendCyclopediaCharacterAchievements(uint16_t secretsUnlocked, const std::vector> &achievementsUnlocked) const { + if (client) { + client->sendCyclopediaCharacterAchievements(secretsUnlocked, achievementsUnlocked); } - return muteConditions; } -void Player::setGuild(const std::shared_ptr &newGuild) { - if (newGuild == guild) { - return; +void Player::sendCyclopediaCharacterItemSummary(const ItemsTierCountList &inventoryItems, const ItemsTierCountList &storeInboxItems, const StashItemList &supplyStashItems, const ItemsTierCountList &depotBoxItems, const ItemsTierCountList &inboxItems) const { + if (client) { + client->sendCyclopediaCharacterItemSummary(inventoryItems, storeInboxItems, supplyStashItems, depotBoxItems, inboxItems); } +} - if (guild) { - guild->removeMember(static_self_cast()); - guild = nullptr; +void Player::sendCyclopediaCharacterOutfitsMounts() const { + if (client) { + client->sendCyclopediaCharacterOutfitsMounts(); } +} - guildNick.clear(); - guildRank = nullptr; +void Player::sendCyclopediaCharacterStoreSummary() const { + if (client) { + client->sendCyclopediaCharacterStoreSummary(); + } +} - if (newGuild) { - const auto &rank = newGuild->getRankByLevel(1); - if (!rank) { - return; - } +void Player::sendCyclopediaCharacterInspection() const { + if (client) { + client->sendCyclopediaCharacterInspection(); + } +} - guild = newGuild; - guildRank = rank; - newGuild->addMember(static_self_cast()); +void Player::sendCyclopediaCharacterBadges() const { + if (client) { + client->sendCyclopediaCharacterBadges(); } } -void Player::updateRegeneration() const { - if (!vocation) { - return; +void Player::sendCyclopediaCharacterTitles() const { + if (client) { + client->sendCyclopediaCharacterTitles(); } +} - const auto &condition = getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT); - if (condition) { - condition->setParam(CONDITION_PARAM_HEALTHGAIN, vocation->getHealthGainAmount()); - condition->setParam(CONDITION_PARAM_HEALTHTICKS, vocation->getHealthGainTicks()); - condition->setParam(CONDITION_PARAM_MANAGAIN, vocation->getManaGainAmount()); - condition->setParam(CONDITION_PARAM_MANATICKS, vocation->getManaGainTicks()); +void Player::sendHighscoresNoData() const { + if (client) { + client->sendHighscoresNoData(); } } -// User Interface action exhaustion -bool Player::isUIExhausted(uint32_t exhaustionTime /*= 250*/) const { - return (OTSYS_TIME() - lastUIInteraction < exhaustionTime); +void Player::sendHighscores(const std::vector &characters, uint8_t categoryId, uint32_t vocationId, uint16_t page, uint16_t pages, uint32_t updateTimer) const { + if (client) { + client->sendHighscores(characters, categoryId, vocationId, page, pages, updateTimer); + } } -void Player::updateUIExhausted() { - lastUIInteraction = OTSYS_TIME(); +void Player::addAsyncOngoingTask(uint64_t flags) { + asyncOngoingTasks |= flags; } -void Player::setImmuneFear() { - m_fearCondition.first = CONDITION_FEARED; - m_fearCondition.second = OTSYS_TIME() + 10000; +bool Player::hasAsyncOngoingTask(uint64_t flags) const { + return (asyncOngoingTasks & flags); } -bool Player::isImmuneFear() const { - const uint64_t timenow = OTSYS_TIME(); - return (m_fearCondition.first == CONDITION_FEARED) && (timenow <= m_fearCondition.second); +void Player::resetAsyncOngoingTask(uint64_t flags) { + asyncOngoingTasks &= ~flags; } -uint64_t Player::getItemCustomPrice(uint16_t itemId, bool buyPrice /* = false*/) const { - auto it = itemPriceMap.find(itemId); - if (it != itemPriceMap.end()) { - return it->second; +void Player::sendEnterWorld() const { + if (client) { + client->sendEnterWorld(); } - - const std::map itemMap { { itemId, 1 } }; - return g_game().getItemMarketPrice(itemMap, buyPrice); } -uint16_t Player::getFreeBackpackSlots() const { - const auto &thing = getThing(CONST_SLOT_BACKPACK); - if (!thing) { - return 0; +void Player::sendFightModes() const { + if (client) { + client->sendFightModes(); } +} - const auto &backpack = thing->getContainer(); - if (!backpack) { - return 0; +void Player::sendNetworkMessage(const NetworkMessage &message) const { + if (client) { + client->writeToOutputBuffer(message); } +} - const uint16_t counter = std::max(0, backpack->getFreeSlots()); +void Player::receivePing() { + lastPong = OTSYS_TIME(); +} - return counter; +void Player::sendOpenStash(bool isNpc) const { + if (client && ((getLastDepotId() != -1) || isNpc)) { + client->sendOpenStash(); + } } -void Player::addItemImbuementStats(const Imbuement* imbuement) { - bool requestUpdate = false; - // Check imbuement skills - for (int32_t skill = SKILL_FIRST; skill <= SKILL_LAST; ++skill) { - if (imbuement->skills[skill]) { - requestUpdate = true; - setVarSkill(static_cast(skill), imbuement->skills[skill]); - } +void Player::sendTakeScreenshot(Screenshot_t screenshotType) const { + if (client) { + client->sendTakeScreenshot(screenshotType); } +} - // Check imbuement magic level - for (int32_t stat = STAT_FIRST; stat <= STAT_LAST; ++stat) { - if (imbuement->stats[stat]) { - requestUpdate = true; - setVarStats(static_cast(stat), imbuement->stats[stat]); - } +void Player::onThink(uint32_t interval) { + Creature::onThink(interval); + + sendPing(); + + MessageBufferTicks += interval; + if (MessageBufferTicks >= 1500) { + MessageBufferTicks = 0; + addMessageBuffer(); } - // Add imbuement speed - if (imbuement->speed != 0) { - g_game().changeSpeed(static_self_cast(), imbuement->speed); + // Transcendance (avatar trigger) + triggerTranscendance(); + // Momentum (cooldown resets) + triggerMomentum(); + const auto &playerTile = getTile(); + const bool vipStaysOnline = isVip() && g_configManager().getBoolean(VIP_STAY_ONLINE); + idleTime += interval; + if (playerTile && !playerTile->hasFlag(TILESTATE_NOLOGOUT) && !isAccessPlayer() && !isExerciseTraining() && !vipStaysOnline) { + const int32_t kickAfterMinutes = g_configManager().getNumber(KICK_AFTER_MINUTES); + if (idleTime > (kickAfterMinutes * 60000) + 60000) { + removePlayer(true); + } else if (client && idleTime == 60000 * kickAfterMinutes) { + std::ostringstream ss; + ss << "There was no variation in your behaviour for " << kickAfterMinutes << " minutes. You will be disconnected in one minute if there is no change in your actions until then."; + client->sendTextMessage(TextMessage(MESSAGE_ADMINISTRATOR, ss.str())); + } } - // Add imbuement capacity - if (imbuement->capacity != 0) { - requestUpdate = true; - bonusCapacity = (capacity * imbuement->capacity) / 100; + if (g_game().getWorldType() != WORLD_TYPE_PVP_ENFORCED) { + checkSkullTicks(interval / 1000); } - if (requestUpdate) { + addOfflineTrainingTime(interval); + if (lastStatsTrainingTime != getOfflineTrainingTime() / 60 / 1000) { sendStats(); - sendSkills(); } -} -void Player::removeItemImbuementStats(const Imbuement* imbuement) { - if (!imbuement) { - return; - } + // Wheel of destiny major spells + wheel()->onThink(); - bool requestUpdate = false; + g_callbacks().executeCallback(EventCallback_t::playerOnThink, &EventCallback::playerOnThink, getPlayer(), interval); +} - for (int32_t skill = SKILL_FIRST; skill <= SKILL_LAST; ++skill) { - if (imbuement->skills[skill]) { - requestUpdate = true; - setVarSkill(static_cast(skill), -imbuement->skills[skill]); - } +void Player::postAddNotification(const std::shared_ptr &thing, const std::shared_ptr &oldParent, int32_t index, CylinderLink_t link) { + if (link == LINK_OWNER) { + // calling movement scripts + g_moveEvents().onPlayerEquip(getPlayer(), thing->getItem(), static_cast(index), false); } - // Check imbuement magic level - for (int32_t stat = STAT_FIRST; stat <= STAT_LAST; ++stat) { - if (imbuement->stats[stat]) { - requestUpdate = true; - setVarStats(static_cast(stat), -imbuement->stats[stat]); + bool requireListUpdate = true; + if (link == LINK_OWNER || link == LINK_TOPPARENT) { + const auto &item = oldParent ? oldParent->getItem() : nullptr; + const auto &container = item ? item->getContainer() : nullptr; + if (container) { + requireListUpdate = container->getHoldingPlayer() != getPlayer(); + } else { + requireListUpdate = oldParent != getPlayer(); } - } - - // Remove imbuement speed - if (imbuement->speed != 0) { - g_game().changeSpeed(static_self_cast(), -imbuement->speed); - } - - // Remove imbuement capacity - if (imbuement->capacity != 0) { - requestUpdate = true; - bonusCapacity = 0; - } - if (requestUpdate) { + updateInventoryWeight(); + updateItemsLight(); + sendInventoryIds(); sendStats(); - sendSkills(); } -} -void Player::updateImbuementTrackerStats() const { - if (imbuementTrackerWindowOpen) { - g_game().playerRequestInventoryImbuements(getID(), true); - } -} + if (const auto &item = thing->getItem()) { + if (const auto &container = item->getContainer()) { + onSendContainer(container); + } -bool Player::addItemFromStash(uint16_t itemId, uint32_t itemCount) { - const uint32_t stackCount = 100u; + if (shopOwner && !scheduledSaleUpdate && requireListUpdate) { + updateSaleShopList(item); + } + } else if (const auto &creature = thing->getCreature()) { + if (creature == getPlayer()) { + // check containers + std::vector> containers; - while (itemCount > 0) { - const auto addValue = itemCount > stackCount ? stackCount : itemCount; - itemCount -= addValue; - const auto &newItem = Item::CreateItem(itemId, addValue); + for (const auto &[containerId, containerInfo] : openContainers) { + const auto &container = containerInfo.container; + if (container == nullptr) { + continue; + } - if (!g_game().tryRetrieveStashItems(static_self_cast(), newItem)) { - g_game().internalPlayerAddItem(static_self_cast(), newItem, true); + if (!Position::areInRange<1, 1, 0>(container->getPosition(), getPosition())) { + containers.emplace_back(container); + } + } + + for (const auto &container : containers) { + autoCloseContainers(container); + } } } +} - // This check is necessary because we need to block it when we retrieve an item from depot search. - if (!isDepotSearchOpenOnItem(itemId)) { - sendOpenStash(); +void Player::postRemoveNotification(const std::shared_ptr &thing, const std::shared_ptr &newParent, int32_t index, CylinderLink_t link) { + if (!thing) { + return; } - return true; -} + const auto copyThing = thing; + const auto copyNewParent = newParent; -void sendStowItems(const std::shared_ptr &item, const std::shared_ptr &stowItem, StashContainerList &itemDict) { - if (stowItem->getID() == item->getID()) { - itemDict.emplace_back(stowItem, stowItem->getItemCount()); + if (link == LINK_OWNER) { + if (const auto &item = copyThing->getItem()) { + g_moveEvents().onPlayerDeEquip(getPlayer(), item, static_cast(index)); + } } + bool requireListUpdate = true; - if (const auto &container = stowItem->getContainer()) { - std::ranges::copy_if(container->getStowableItems(), std::back_inserter(itemDict), [&item](const auto &stowable_it) { - return stowable_it.first->getID() == item->getID(); - }); - } -} + if (link == LINK_OWNER || link == LINK_TOPPARENT) { + const auto &item = copyNewParent ? copyNewParent->getItem() : nullptr; + const auto &container = item ? item->getContainer() : nullptr; + if (container) { + requireListUpdate = container->getHoldingPlayer() != getPlayer(); + } else { + requireListUpdate = copyNewParent != getPlayer(); + } -void Player::stowItem(const std::shared_ptr &item, uint32_t count, bool allItems) { - if (!item || !item->isItemStorable()) { - sendCancelMessage("This item cannot be stowed here."); - return; + updateInventoryWeight(); + updateItemsLight(); + sendInventoryIds(); + sendStats(); } - StashContainerList itemDict; - if (allItems) { - if (!item->isInsideDepot(true)) { - // Stow "all items" from player backpack - if (const auto &backpack = getInventoryItem(CONST_SLOT_BACKPACK)) { - sendStowItems(item, backpack, itemDict); - } + if (const auto &item = copyThing->getItem()) { + if (const auto &container = item->getContainer()) { + checkLootContainers(container); - // Stow "all items" from loot pouch - const auto &itemParent = item->getParent(); - const auto &lootPouch = itemParent->getItem(); - if (itemParent && lootPouch && lootPouch->getID() == ITEM_GOLD_POUCH) { - sendStowItems(item, lootPouch, itemDict); - } - } + if (container->isRemoved() || !Position::areInRange<1, 1, 0>(getPosition(), container->getPosition())) { + autoCloseContainers(container); + } else if (container->getTopParent() == getPlayer()) { + onSendContainer(container); + } else if (const auto &topContainer = std::dynamic_pointer_cast(container->getTopParent())) { + if (const auto &depotChest = std::dynamic_pointer_cast(topContainer)) { + bool isOwner = false; - // Stow locker items - const auto &depotLocker = getDepotLocker(getLastDepotId()); - const auto &[itemVector, itemMap] = requestLockerItems(depotLocker); - for (const auto &lockerItem : itemVector) { - if (lockerItem == nullptr) { - break; - } + for (const auto &[depotId, depotChestMap] : depotChests) { + if (depotId == 0) { + continue; + } - if (item->isInsideDepot(true)) { - sendStowItems(item, lockerItem, itemDict); + if (depotChestMap == depotChest) { + isOwner = true; + onSendContainer(container); + } + } + + if (!isOwner) { + autoCloseContainers(container); + } + } else { + onSendContainer(container); + } + } else { + autoCloseContainers(container); } } - } else if (item->getContainer()) { - itemDict = item->getContainer()->getStowableItems(); - for (const std::shared_ptr &containerItem : item->getContainer()->getItems(true)) { - uint32_t depotChest = g_configManager().getNumber(DEPOTCHEST); - bool validDepot = depotChest > 0 && depotChest < 21; - if (g_configManager().getBoolean(STASH_MOVING) && containerItem && !containerItem->isStackable() && validDepot) { - g_game().internalMoveItem(containerItem->getParent(), getDepotChest(depotChest, true), INDEX_WHEREEVER, containerItem, containerItem->getItemCount(), nullptr); - movedItems++; - moved = true; - } + + // force list update if item exists tier + if (item->getTier() > 0 && !requireListUpdate) { + requireListUpdate = true; + } + + if (shopOwner && !scheduledSaleUpdate && requireListUpdate) { + updateSaleShopList(item); } - } else { - itemDict.emplace_back(item, count); } +} - if (itemDict.empty()) { - sendCancelMessage("There is no stowable items on this container."); - return; +void Player::sendUpdateTile(const std::shared_ptr &updateTile, const Position &pos) const { + if (client) { + client->sendUpdateTile(updateTile, pos); } +} - stashContainer(itemDict); +void Player::sendChannelMessage(const std::string &author, const std::string &text, SpeakClasses type, uint16_t channel) const { + if (client) { + client->sendChannelMessage(author, text, type, channel); + } } -void Player::openPlayerContainers() { - std::vector>> openContainersList; +void Player::sendChannelEvent(uint16_t channelId, const std::string &playerName, ChannelEvent_t channelEvent) const { + if (client) { + client->sendChannelEvent(channelId, playerName, channelEvent); + } +} - for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; i++) { - const auto &item = inventory[i]; - if (!item) { - continue; - } +void Player::sendCreatureAppear(const std::shared_ptr &creature, const Position &pos, bool isLogin) { + if (!creature) { + return; + } - const auto &itemContainer = item->getContainer(); - if (itemContainer) { - const auto &cid = item->getAttribute(ItemAttribute_t::OPENCONTAINER); - if (cid > 0) { - openContainersList.emplace_back(cid, itemContainer); - } - for (ContainerIterator it = itemContainer->iterator(); it.hasNext(); it.advance()) { - const auto &subContainer = (*it)->getContainer(); - if (subContainer) { - const auto &subcid = (*it)->getAttribute(ItemAttribute_t::OPENCONTAINER); - if (subcid > 0) { - openContainersList.emplace_back(subcid, subContainer); - } - } - } - } + auto tile = creature->getTile(); + if (!tile) { + return; } - std::ranges::sort(openContainersList, [](const std::pair> &left, const std::pair> &right) { - return left.first < right.first; - }); + if (client) { + client->sendAddCreature(creature, pos, tile->getStackposOfCreature(static_self_cast(), creature), isLogin); + } +} - for (const auto &[containerId, container] : openContainersList) { - addContainer(containerId - 1, container); - onSendContainer(container); +void Player::sendCreatureMove(const std::shared_ptr &creature, const Position &newPos, int32_t newStackPos, const Position &oldPos, int32_t oldStackPos, bool teleport) const { + if (client) { + client->sendMoveCreature(creature, newPos, newStackPos, oldPos, oldStackPos, teleport); } } -void Player::initializePrey() { - if (preys.empty()) { - for (uint8_t slotId = PreySlot_First; slotId <= PreySlot_Last; slotId++) { - auto slot = std::make_unique(static_cast(slotId)); - if (!g_configManager().getBoolean(PREY_ENABLED)) { - slot->state = PreyDataState_Inactive; - } else if (slot->id == PreySlot_Three && !g_configManager().getBoolean(PREY_FREE_THIRD_SLOT)) { - slot->state = PreyDataState_Locked; - } else if (slot->id == PreySlot_Two && !isPremium()) { - slot->state = PreyDataState_Locked; - } else { - slot->state = PreyDataState_Selection; - slot->reloadMonsterGrid(getPreyBlackList(), getLevel()); - } +void Player::sendCreatureTurn(const std::shared_ptr &creature) { + if (!creature) { + return; + } - setPreySlotClass(slot); + auto tile = creature->getTile(); + if (!tile) { + return; + } + + if (client && canSeeCreature(creature)) { + int32_t stackpos = tile->getStackposOfCreature(static_self_cast(), creature); + if (stackpos != -1) { + client->sendCreatureTurn(creature, stackpos); } } } -void Player::removePreySlotById(PreySlot_t slotid) { - const auto it = std::ranges::remove_if(preys, [slotid](const auto &preyIt) { - return preyIt->id == slotid; - }).begin(); +void Player::sendCreatureSay(const std::shared_ptr &creature, SpeakClasses type, const std::string &text, const Position* pos) const { + if (client) { + client->sendCreatureSay(creature, type, text, pos); + } +} - preys.erase(it, preys.end()); +void Player::sendCreatureReload(const std::shared_ptr &creature) const { + if (client) { + client->reloadCreature(creature); + } } -void Player::initializeTaskHunting() { - if (taskHunting.empty()) { - for (uint8_t slotId = PreySlot_First; slotId <= PreySlot_Last; slotId++) { - auto slot = std::make_unique(static_cast(slotId)); - if (!g_configManager().getBoolean(TASK_HUNTING_ENABLED)) { - slot->state = PreyTaskDataState_Inactive; - } else if (slot->id == PreySlot_Three && !g_configManager().getBoolean(TASK_HUNTING_FREE_THIRD_SLOT)) { - slot->state = PreyTaskDataState_Locked; - } else if (slot->id == PreySlot_Two && !isPremium()) { - slot->state = PreyTaskDataState_Locked; - } else { - slot->state = PreyTaskDataState_Selection; - slot->reloadMonsterGrid(getTaskHuntingBlackList(), getLevel()); - } +void Player::sendPrivateMessage(const std::shared_ptr &speaker, SpeakClasses type, const std::string &text) const { + if (client) { + client->sendPrivateMessage(speaker, type, text); + } +} - setTaskHuntingSlotClass(slot); - } +void Player::sendCreatureSquare(const std::shared_ptr &creature, SquareColor_t color) const { + if (client) { + client->sendCreatureSquare(creature, color); } +} - if (client && g_configManager().getBoolean(TASK_HUNTING_ENABLED) && !client->oldProtocol) { - client->writeToOutputBuffer(g_ioprey().getTaskHuntingBaseDate()); +void Player::sendCreatureChangeOutfit(const std::shared_ptr &creature, const Outfit_t &outfit) const { + if (client) { + client->sendCreatureOutfit(creature, outfit); } } -std::string Player::getBlessingsName() const { - std::vector blessingNames; - for (const auto &bless : magic_enum::enum_values()) { - if (hasBlessing(enumToValue(bless))) { - std::string name = toStartCaseWithSpace(magic_enum::enum_name(bless).data()); - blessingNames.emplace_back(name); - } +void Player::sendCreatureChangeVisible(const std::shared_ptr &creature, bool visible) { + if (!client || !creature) { + return; } - std::ostringstream os; - if (!blessingNames.empty()) { - // Join all elements but the last with ", " and add the last one with " and " - for (size_t i = 0; i < blessingNames.size() - 1; ++i) { - os << blessingNames[i] << ", "; + if (creature->getPlayer()) { + if (visible) { + client->sendCreatureOutfit(creature, creature->getCurrentOutfit()); + } else { + static Outfit_t outfit; + client->sendCreatureOutfit(creature, outfit); } - if (blessingNames.size() > 1) { - os << "and "; + } else if (canSeeInvisibility()) { + client->sendCreatureOutfit(creature, creature->getCurrentOutfit()); + } else { + auto tile = creature->getTile(); + if (!tile) { + return; + } + int32_t stackpos = tile->getStackposOfCreature(static_self_cast(), creature); + if (stackpos == -1) { + return; } - os << blessingNames.back() << "."; - } - return os.str(); + if (visible) { + client->sendAddCreature(creature, creature->getPosition(), stackpos, false); + } else { + client->sendRemoveTileThing(creature->getPosition(), stackpos); + } + } } -bool Player::isCreatureUnlockedOnTaskHunting(const std::shared_ptr &mtype) const { - if (!mtype) { - return false; +void Player::sendCreatureLight(const std::shared_ptr &creature) const { + if (client) { + client->sendCreatureLight(creature); } - - return getBestiaryKillCount(mtype->info.raceid) >= mtype->info.bestiaryToUnlock; } -void Player::triggerMomentum() { - double_t chance = 0; - if (const auto &item = getInventoryItem(CONST_SLOT_HEAD)) { - chance += item->getMomentumChance(); +void Player::sendCreatureIcon(const std::shared_ptr &creature) const { + if (client && !client->oldProtocol) { + client->sendCreatureIcon(creature); } +} - chance += m_wheelPlayer->getBonusData().momentum; - double_t randomChance = uniform_random(0, 10000) / 100.; - if (getZoneType() != ZONE_PROTECTION && hasCondition(CONDITION_INFIGHT) && ((OTSYS_TIME() / 1000) % 2) == 0 && chance > 0 && randomChance < chance) { - bool triggered = false; - auto it = conditions.begin(); - while (it != conditions.end()) { - const auto condItem = *it; - const ConditionType_t type = condItem->getType(); - constexpr auto maxu16 = std::numeric_limits::max(); - const auto checkSpellId = condItem->getSubId(); - auto spellId = checkSpellId > maxu16 ? 0u : static_cast(checkSpellId); - const int32_t ticks = condItem->getTicks(); - const int32_t newTicks = (ticks <= 2000) ? 0 : ticks - 2000; - triggered = true; - if (type == CONDITION_SPELLCOOLDOWN || (type == CONDITION_SPELLGROUPCOOLDOWN && spellId > SPELLGROUP_SUPPORT)) { - condItem->setTicks(newTicks); - type == CONDITION_SPELLGROUPCOOLDOWN ? sendSpellGroupCooldown(static_cast(spellId), newTicks) : sendSpellCooldown(spellId, newTicks); - } - ++it; - } - if (triggered) { - g_game().addMagicEffect(getPosition(), CONST_ME_HOURGLASS); - sendTextMessage(MESSAGE_ATTENTION, "Momentum was triggered."); - } +void Player::sendUpdateCreature(const std::shared_ptr &creature) const { + if (client) { + client->sendUpdateCreature(creature); } } -void Player::clearCooldowns() { - auto it = conditions.begin(); - while (it != conditions.end()) { - const auto &condItem = *it; - if (!condItem) { - ++it; - continue; - } - - const ConditionType_t type = condItem->getType(); - constexpr auto maxu16 = std::numeric_limits::max(); - const auto checkSpellId = condItem->getSubId(); - auto spellId = checkSpellId > maxu16 ? 0u : static_cast(checkSpellId); - if (type == CONDITION_SPELLCOOLDOWN || type == CONDITION_SPELLGROUPCOOLDOWN) { - condItem->setTicks(0); - type == CONDITION_SPELLGROUPCOOLDOWN ? sendSpellGroupCooldown(static_cast(spellId), 0) : sendSpellCooldown(spellId, 0); - } - ++it; +void Player::sendCreatureWalkthrough(const std::shared_ptr &creature, bool walkthrough) const { + if (client) { + client->sendCreatureWalkthrough(creature, walkthrough); } } -void Player::triggerTranscendance() { - if (wheel()->getOnThinkTimer(WheelOnThink_t::AVATAR_FORGE) > OTSYS_TIME()) { - return; +void Player::sendCreatureShield(const std::shared_ptr &creature) const { + if (client) { + client->sendCreatureShield(creature); } +} - const auto &item = getInventoryItem(CONST_SLOT_LEGS); - if (item == nullptr) { - return; +void Player::sendCreatureType(const std::shared_ptr &creature, uint8_t creatureType) const { + if (client) { + client->sendCreatureType(creature, creatureType); } +} - const double_t chance = item->getTranscendenceChance(); - const double_t randomChance = uniform_random(0, 10000) / 100.; - if (getZoneType() != ZONE_PROTECTION && checkLastAggressiveActionWithin(2000) && ((OTSYS_TIME() / 1000) % 2) == 0 && chance > 0 && randomChance < chance) { - int64_t duration = g_configManager().getNumber(TRANSCENDANCE_AVATAR_DURATION); - const auto &outfitCondition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_OUTFIT, duration, 0)->static_self_cast(); - Outfit_t outfit; - outfit.lookType = getVocation()->getAvatarLookType(); - outfitCondition->setOutfit(outfit); - addCondition(outfitCondition); - wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR_FORGE, OTSYS_TIME() + duration); - g_game().addMagicEffect(getPosition(), CONST_ME_AVATAR_APPEAR); - - sendSkills(); - sendStats(); - sendBasicData(); +void Player::sendSpellCooldown(uint16_t spellId, uint32_t time) const { + if (client) { + client->sendSpellCooldown(spellId, time); + } +} - sendTextMessage(MESSAGE_ATTENTION, "Transcendance was triggered."); +void Player::sendSpellGroupCooldown(SpellGroup_t groupId, uint32_t time) const { + if (client) { + client->sendSpellGroupCooldown(groupId, time); + } +} - // Send player data after transcendance timer expire - const auto &task = createPlayerTask( - std::max(SCHEDULER_MINTICKS, duration), - [playerId = getID()] { - const auto &player = g_game().getPlayerByID(playerId); - if (player) { - player->sendSkills(); - player->sendStats(); - player->sendBasicData(); - } - }, - __FUNCTION__ - ); - g_dispatcher().scheduleEvent(task); +void Player::sendUseItemCooldown(uint32_t time) const { + if (client) { + client->sendUseItemCooldown(time); + } +} - wheel()->sendGiftOfLifeCooldown(); - g_game().reloadCreature(getPlayer()); +void Player::reloadCreature(const std::shared_ptr &creature) const { + if (client) { + client->reloadCreature(creature); } } -/******************************************************************************* - * Depot search system - ******************************************************************************/ -void Player::requestDepotItems() { - ItemsTierCountList itemMap; - uint16_t count = 0; - const auto &depotLocker = getDepotLocker(getLastDepotId()); - if (!depotLocker) { +void Player::sendModalWindow(const ModalWindow &modalWindow) { + if (!client) { return; } - for (const auto &locker : depotLocker->getItemList()) { - const auto &c = locker->getContainer(); - if (!c || c->empty()) { - continue; - } + modalWindows.emplace_back(modalWindow.id); + client->sendModalWindow(modalWindow); +} - for (ContainerIterator it = c->iterator(); it.hasNext(); it.advance()) { - auto itemMap_it = itemMap.find((*it)->getID()); +// container - uint8_t itemTier = Item::items[(*it)->getID()].upgradeClassification > 0 ? (*it)->getTier() + 1 : 0; - if (itemMap_it == itemMap.end()) { - std::map itemTierMap; - itemTierMap[itemTier] = Item::countByType((*it), -1); - itemMap[(*it)->getID()] = itemTierMap; - count++; - } else if (auto itemTier_it = itemMap[(*it)->getID()].find(itemTier); itemTier_it == itemMap[(*it)->getID()].end()) { - itemMap[(*it)->getID()][itemTier] = Item::countByType((*it), -1); - count++; - } else { - itemMap[(*it)->getID()][itemTier] += Item::countByType((*it), -1); - } - } +void Player::closeAllExternalContainers() { + if (openContainers.empty()) { + return; } - for (const auto &[itemId, itemCount] : getStashItems()) { - auto itemMap_it = itemMap.find(itemId); - // Stackable items not have upgrade classification - if (Item::items[itemId].upgradeClassification > 0) { - g_logger().error("{} - Player {} have wrong item with id {} on stash with upgrade classification", __FUNCTION__, getName(), itemId); + std::vector> containerToClose; + for (const auto &[containerId, containerInfo] : openContainers) { + const auto &container = containerInfo.container; + if (!container) { continue; } - if (itemMap_it == itemMap.end()) { - std::map itemTierMap; - itemTierMap[0] = itemCount; - itemMap[itemId] = itemTierMap; - count++; - } else if (auto itemTier_it = itemMap[itemId].find(0); itemTier_it == itemMap[itemId].end()) { - itemMap[itemId][0] = itemCount; - count++; - } else { - itemMap[itemId][0] += itemCount; + if (container->getHoldingPlayer() != getPlayer()) { + containerToClose.emplace_back(container); } } - setDepotSearchIsOpen(1, 0); - sendDepotItems(itemMap, count); + for (const auto &container : containerToClose) { + autoCloseContainers(container); + } } -void Player::requestDepotSearchItem(uint16_t itemId, uint8_t tier) { - ItemVector depotItems; - ItemVector inboxItems; - uint32_t depotCount = 0; - uint32_t inboxCount = 0; - uint32_t stashCount = 0; +// container - if (const ItemType &iType = Item::items[itemId]; - iType.stackable && iType.wareId > 0) { - stashCount = getStashItemCount(itemId); +void Player::sendAddContainerItem(const std::shared_ptr &container, std::shared_ptr item) { + if (!client) { + return; } - const auto &depotLocker = getDepotLocker(getLastDepotId()); - if (!depotLocker) { + if (!container) { return; } - for (const auto &locker : depotLocker->getItemList()) { - const auto &c = locker->getContainer(); - if (!c || c->empty()) { + for (const auto &[containerId, containerInfo] : openContainers) { + if (containerInfo.container != container) { continue; } - inboxItems.reserve(inboxItems.size()); - depotItems.reserve(depotItems.size()); - - for (ContainerIterator it = c->iterator(); it.hasNext(); it.advance()) { - const auto item = *it; - if (!item || item->getID() != itemId || item->getTier() != tier) { - continue; - } - - if (c->isInbox()) { - if (inboxItems.size() < 255) { - inboxItems.emplace_back(item); - } - inboxCount += Item::countByType(item, -1); + uint16_t slot = containerInfo.index; + if (container->getID() == ITEM_BROWSEFIELD) { + const uint16_t containerSize = container->size() - 1; + const uint16_t pageEnd = containerInfo.index + container->capacity() - 1; + if (containerSize > pageEnd) { + slot = pageEnd; + item = container->getItemByIndex(pageEnd); } else { - if (depotItems.size() < 255) { - depotItems.emplace_back(item); - } - depotCount += Item::countByType(item, -1); + slot = containerSize; } + } else if (containerInfo.index >= container->capacity()) { + item = container->getItemByIndex(containerInfo.index - 1); } + client->sendAddContainerItem(containerId, slot, item); } - - setDepotSearchIsOpen(itemId, tier); - sendDepotSearchResultDetail(itemId, tier, depotCount, depotItems, inboxCount, inboxItems, stashCount); } -void Player::retrieveAllItemsFromDepotSearch(uint16_t itemId, uint8_t tier, bool isDepot) { - const auto &depotLocker = getDepotLocker(getLastDepotId()); - if (!depotLocker) { +void Player::sendUpdateContainerItem(const std::shared_ptr &container, uint16_t slot, const std::shared_ptr &newItem) { + if (!client) { return; } - std::vector> itemsVector; - for (const auto &locker : depotLocker->getItemList()) { - const auto &c = locker->getContainer(); - if (!c || c->empty() || - // Retrieve from inbox. - (c->isInbox() && isDepot) || - // Retrieve from depot. - (!c->isInbox() && !isDepot)) { - continue; - } - - for (ContainerIterator it = c->iterator(); it.hasNext(); it.advance()) { - const auto &item = *it; - if (!item) { - continue; - } - - if (item->getID() == itemId && item->getTier() == depotSearchOnItem.second) { - itemsVector.emplace_back(item); - } - } - } - - ReturnValue ret = RETURNVALUE_NOERROR; - for (const auto &item : itemsVector) { - if (!item) { + for (const auto &[containerId, containerInfo] : openContainers) { + if (containerInfo.container != container) { continue; } - // First lets try to retrieve the item to the stash retrieve container. - if (g_game().tryRetrieveStashItems(static_self_cast(), item)) { + if (slot < containerInfo.index) { continue; } - // If the retrieve fails to move the item to the stash retrieve container, let's add the item anywhere. - if (ret = g_game().internalMoveItem(item->getParent(), getPlayer(), INDEX_WHEREEVER, item, item->getItemCount(), nullptr); ret == RETURNVALUE_NOERROR) { + const uint16_t pageEnd = containerInfo.index + container->capacity(); + if (slot >= pageEnd) { continue; } - sendCancelMessage(ret); - return; + client->sendUpdateContainerItem(containerId, slot, newItem); } - - requestDepotSearchItem(itemId, tier); } -void Player::openContainerFromDepotSearch(const Position &pos) { - if (!isDepotSearchOpen()) { - sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); - return; - } - - const auto &item = getItemFromDepotSearch(depotSearchOnItem.first, pos); - if (!item) { - sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); +void Player::sendRemoveContainerItem(const std::shared_ptr &container, uint16_t slot) { + if (!client) { return; } - const auto &container = item->getParent() ? item->getParent()->getContainer() : nullptr; if (!container) { - sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); return; } - g_actions().useItem(static_self_cast(), pos, 0, container, false); -} - -std::shared_ptr Player::getItemFromDepotSearch(uint16_t itemId, const Position &pos) { - const auto &depotLocker = getDepotLocker(getLastDepotId()); - if (!depotLocker) { - return nullptr; - } - - uint8_t index = 0; - for (const auto &locker : depotLocker->getItemList()) { - const auto &c = locker->getContainer(); - if (!c || c->empty() || (c->isInbox() && pos.y != 0x21) || // From inbox. - (!c->isInbox() && pos.y != 0x20)) { // From depot. + for (auto &[containerId, containerInfo] : openContainers) { + if (containerInfo.container != container) { continue; } - for (ContainerIterator it = c->iterator(); it.hasNext(); it.advance()) { - const auto &item = *it; - if (!item || item->getID() != itemId || item->getTier() != depotSearchOnItem.second) { - continue; - } - - if (pos.z == index) { - return item; - } - index++; + uint16_t &firstIndex = containerInfo.index; + if (firstIndex > 0 && firstIndex >= container->size() - 1) { + firstIndex -= container->capacity(); + sendContainer(containerId, container, false, firstIndex); } - } - return nullptr; + client->sendRemoveContainerItem(containerId, std::max(slot, firstIndex), container->getItemByIndex(container->capacity() + firstIndex)); + } } -std::pair>, std::map>> -Player::requestLockerItems(const std::shared_ptr &depotLocker, bool sendToClient /*= false*/, uint8_t tier /*= 0*/) const { - if (!depotLocker) { - g_logger().error("{} - Depot locker is nullptr", __FUNCTION__); - return {}; +void Player::sendContainer(uint8_t cid, const std::shared_ptr &container, bool hasParent, uint16_t firstIndex) const { + if (client) { + client->sendContainer(cid, container, hasParent, firstIndex); } +} - std::map> lockerItems; - std::vector> itemVector; - std::vector> containers { depotLocker }; - - for (size_t i = 0; i < containers.size(); ++i) { - const auto &container = containers[i]; - - for (const auto &item : container->getItemList()) { - const auto &lockerContainers = item->getContainer(); - if (lockerContainers && !lockerContainers->empty()) { - containers.emplace_back(lockerContainers); - continue; - } +// inventory - const ItemType &itemType = Item::items[item->getID()]; - if (item->isStoreItem() || itemType.wareId == 0) { - continue; - } +void Player::sendDepotItems(const ItemsTierCountList &itemMap, uint16_t count) const { + if (client) { + client->sendDepotItems(itemMap, count); + } +} - if (lockerContainers && (!itemType.isContainer() || lockerContainers->capacity() != itemType.maxItems)) { - continue; - } +void Player::sendCloseDepotSearch() const { + if (client) { + client->sendCloseDepotSearch(); + } +} - if (!item->hasMarketAttributes() || (!sendToClient && item->getTier() != tier)) { - continue; - } +void Player::sendDepotSearchResultDetail(uint16_t itemId, uint8_t tier, uint32_t depotCount, const ItemVector &depotItems, uint32_t inboxCount, const ItemVector &inboxItems, uint32_t stashCount) const { + if (client) { + client->sendDepotSearchResultDetail(itemId, tier, depotCount, depotItems, inboxCount, inboxItems, stashCount); + } +} - lockerItems[itemType.wareId][item->getTier()] += Item::countByType(item, -1); - itemVector.emplace_back(item); - } +void Player::sendCoinBalance() const { + if (client) { + client->sendCoinBalance(); } +} - StashItemList stashToSend = getStashItems(); - for (const auto &[itemId, itemCount] : stashToSend) { - const ItemType &itemType = Item::items[itemId]; - if (itemType.wareId != 0) { - lockerItems[itemType.wareId][0] += itemCount; - } +void Player::sendInventoryItem(Slots_t slot, const std::shared_ptr &item) const { + if (client) { + client->sendInventoryItem(slot, item); } +} - return { itemVector, lockerItems }; +void Player::sendInventoryIds() const { + if (client) { + client->sendInventoryIds(); + } } -std::pair>, uint16_t> Player::getLockerItemsAndCountById(const std::shared_ptr &depotLocker, uint8_t tier, uint16_t itemId) const { - std::vector> lockerItems; - const auto &[itemVector, itemMap] = requestLockerItems(depotLocker, false, tier); - uint16_t totalCount = 0; - for (const auto &item : itemVector) { - if (!item || item->getID() != itemId) { +std::vector> Player::getMuteConditions() const { + std::vector> muteConditions; + muteConditions.reserve(conditions.size()); + + for (const auto &condition : conditions) { + if (condition->getTicks() <= 0) { continue; } - totalCount++; - lockerItems.emplace_back(item); + const auto &type = condition->getType(); + if (type != CONDITION_MUTED && type != CONDITION_CHANNELMUTEDTICKS && type != CONDITION_YELLTICKS) { + continue; + } + + muteConditions.emplace_back(condition); } + return muteConditions; +} - return std::make_pair(lockerItems, totalCount); +[[nodiscard]] std::shared_ptr Player::getGuild() const { + return guild; } -bool Player::saySpell( - SpeakClasses type, - const std::string &text, - bool ghostMode, - const Spectators* spectatorsPtr /* = nullptr*/, - const Position* pos /* = nullptr*/ -) { - if (text.empty()) { - g_logger().debug("{} - Spell text is empty for player {}", __FUNCTION__, getName()); - return false; +void Player::setGuild(const std::shared_ptr &newGuild) { + if (newGuild == guild) { + return; } - if (!pos) { - pos = &getPosition(); + if (guild) { + guild->removeMember(static_self_cast()); + guild = nullptr; } - Spectators spectators; + guildNick.clear(); + guildRank = nullptr; - if (!spectatorsPtr || spectatorsPtr->empty()) { - // This somewhat complex construct ensures that the cached Spectators - // is used if available and if it can be used, else a local vector is - // used (hopefully the compiler will optimize away the construction of - // the temporary when it's not used). - if (type != TALKTYPE_YELL && type != TALKTYPE_MONSTER_YELL) { - spectators.find(*pos, false, MAP_MAX_CLIENT_VIEW_PORT_X, MAP_MAX_CLIENT_VIEW_PORT_X, MAP_MAX_CLIENT_VIEW_PORT_Y, MAP_MAX_CLIENT_VIEW_PORT_Y); - } else { - spectators.find(*pos, true, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2); + if (newGuild) { + const auto &rank = newGuild->getRankByLevel(1); + if (!rank) { + return; } - } else { - spectators = (*spectatorsPtr); + + guild = newGuild; + guildRank = rank; + newGuild->addMember(static_self_cast()); } +} - int32_t valueEmote = 0; - // Send to client - for (const auto &spectator : spectators) { - if (const auto &tmpPlayer = spectator->getPlayer()) { - if (g_configManager().getBoolean(EMOTE_SPELLS)) { - valueEmote = tmpPlayer->getStorageValue(STORAGEVALUE_EMOTE); - } - if (!ghostMode || tmpPlayer->canSeeCreature(static_self_cast())) { - if (valueEmote == 1) { - tmpPlayer->sendCreatureSay(static_self_cast(), TALKTYPE_MONSTER_SAY, text, pos); - } else { - tmpPlayer->sendCreatureSay(static_self_cast(), TALKTYPE_SPELL_USE, text, pos); - } - } - } +[[nodiscard]] GuildRank_ptr Player::getGuildRank() const { + return guildRank; +} + +void Player::setGuildRank(GuildRank_ptr newGuildRank) { + guildRank = std::move(newGuildRank); +} + +bool Player::isGuildMate(const std::shared_ptr &player) const { + if (!player || !guild) { + return false; } + return guild == player->guild; +} - // Execute lua event method - for (const auto &spectator : spectators) { - const auto &tmpPlayer = spectator->getPlayer(); - if (!tmpPlayer) { - continue; - } +bool Player::addItemFromStash(uint16_t itemId, uint32_t itemCount) { + const uint32_t stackCount = 100u; - tmpPlayer->onCreatureSay(static_self_cast(), type, text); - if (static_self_cast() != tmpPlayer) { - g_events().eventCreatureOnHear(tmpPlayer, getPlayer(), text, type); - g_callbacks().executeCallback(EventCallback_t::creatureOnHear, &EventCallback::creatureOnHear, tmpPlayer, getPlayer(), text, type); + while (itemCount > 0) { + const auto addValue = itemCount > stackCount ? stackCount : itemCount; + itemCount -= addValue; + const auto &newItem = Item::CreateItem(itemId, addValue); + + if (!g_game().tryRetrieveStashItems(static_self_cast(), newItem)) { + g_game().internalPlayerAddItem(static_self_cast(), newItem, true); } } + + // This check is necessary because we need to block it when we retrieve an item from depot search. + if (!isDepotSearchOpenOnItem(itemId)) { + sendOpenStash(); + } + return true; } -// Forge system -void Player::forgeFuseItems(ForgeAction_t actionType, uint16_t firstItemId, uint8_t tier, uint16_t secondItemId, bool success, bool reduceTierLoss, bool convergence, uint8_t bonus, uint8_t coreCount) { - if (getFreeBackpackSlots() == 0) { - sendCancelMessage(RETURNVALUE_NOTENOUGHROOM); - return; +void sendStowItems(const std::shared_ptr &item, const std::shared_ptr &stowItem, StashContainerList &itemDict) { + if (stowItem->getID() == item->getID()) { + itemDict.emplace_back(stowItem, stowItem->getItemCount()); } - ForgeHistory history; - history.actionType = actionType; - history.tier = tier; - history.success = success; - history.tierLoss = reduceTierLoss; - - const auto &firstForgingItem = getForgeItemFromId(firstItemId, tier); - if (!firstForgingItem) { - g_logger().error("[Log 1] Player with name {} failed to fuse item with id {}", getName(), firstItemId); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; + if (const auto &container = stowItem->getContainer()) { + std::ranges::copy_if(container->getStowableItems(), std::back_inserter(itemDict), [&item](const auto &stowable_it) { + return stowable_it.first->getID() == item->getID(); + }); } - auto returnValue = g_game().internalRemoveItem(firstForgingItem, 1); - if (returnValue != RETURNVALUE_NOERROR) { - g_logger().error("[Log 1] Failed to remove forge item {} from player with name {}", firstItemId, getName()); - sendCancelMessage(getReturnMessage(returnValue)); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); +} + +void Player::stowItem(const std::shared_ptr &item, uint32_t count, bool allItems) { + if (!item || !item->isItemStorable()) { + sendCancelMessage("This item cannot be stowed here."); return; } - const auto &secondForgingItem = getForgeItemFromId(secondItemId, tier); - if (!secondForgingItem) { - g_logger().error("[Log 2] Player with name {} failed to fuse item with id {}", getName(), secondItemId); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; + + StashContainerList itemDict; + if (allItems) { + if (!item->isInsideDepot(true)) { + // Stow "all items" from player backpack + if (const auto &backpack = getInventoryItem(CONST_SLOT_BACKPACK)) { + sendStowItems(item, backpack, itemDict); + } + + // Stow "all items" from loot pouch + const auto &itemParent = item->getParent(); + const auto &lootPouch = itemParent->getItem(); + if (itemParent && lootPouch && lootPouch->getID() == ITEM_GOLD_POUCH) { + sendStowItems(item, lootPouch, itemDict); + } + } + + // Stow locker items + const auto &depotLocker = getDepotLocker(getLastDepotId()); + const auto &[itemVector, itemMap] = requestLockerItems(depotLocker); + for (const auto &lockerItem : itemVector) { + if (lockerItem == nullptr) { + break; + } + + if (item->isInsideDepot(true)) { + sendStowItems(item, lockerItem, itemDict); + } + } + } else if (item->getContainer()) { + itemDict = item->getContainer()->getStowableItems(); + for (const std::shared_ptr &containerItem : item->getContainer()->getItems(true)) { + uint32_t depotChest = g_configManager().getNumber(DEPOTCHEST); + bool validDepot = depotChest > 0 && depotChest < 21; + if (g_configManager().getBoolean(STASH_MOVING) && containerItem && !containerItem->isStackable() && validDepot) { + g_game().internalMoveItem(containerItem->getParent(), getDepotChest(depotChest, true), INDEX_WHEREEVER, containerItem, containerItem->getItemCount(), nullptr); + movedItems++; + moved = true; + } + } + } else { + itemDict.emplace_back(item, count); } - if (returnValue = g_game().internalRemoveItem(secondForgingItem, 1); - returnValue != RETURNVALUE_NOERROR) { - g_logger().error("[Log 2] Failed to remove forge item {} from player with name {}", secondItemId, getName()); - sendCancelMessage(getReturnMessage(returnValue)); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + + if (itemDict.empty()) { + sendCancelMessage("There is no stowable items on this container."); return; } - const auto &exaltationChest = Item::CreateItem(ITEM_EXALTATION_CHEST, 1); - if (!exaltationChest) { - g_logger().error("Failed to create exaltation chest"); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; + stashContainer(itemDict); +} + +void Player::sendPreyData() const { + if (client) { + for (const std::unique_ptr &slot : preys) { + client->sendPreyData(slot); + } + + client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards()); } - const auto &exaltationContainer = exaltationChest->getContainer(); - if (!exaltationContainer) { - g_logger().error("Failed to create exaltation container"); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; +} + +void Player::sendPreyTimeLeft(const std::unique_ptr &slot) const { + if (g_configManager().getBoolean(PREY_ENABLED) && client) { + client->sendPreyTimeLeft(slot); + } +} + +void Player::reloadPreySlot(PreySlot_t slotid) { + if (g_configManager().getBoolean(PREY_ENABLED) && client) { + client->sendPreyData(getPreySlotById(slotid)); + client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints()); + } +} + +const std::unique_ptr &Player::getPreySlotById(PreySlot_t slotid) { + if (auto it = std::ranges::find_if(preys, [slotid](const std::unique_ptr &preyIt) { + return preyIt->id == slotid; + }); + it != preys.end()) { + return *it; + } + + return PreySlotNull; +} + +bool Player::setPreySlotClass(std::unique_ptr &slot) { + if (getPreySlotById(slot->id)) { + return false; + } + + preys.emplace_back(std::move(slot)); + return true; +} + +bool Player::usePreyCards(uint16_t amount) { + if (preyCards < amount) { + return false; + } + + preyCards -= amount; + if (client) { + client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints()); + } + return true; +} + +void Player::addPreyCards(uint64_t amount) { + preyCards += amount; + if (client) { + client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints()); + } +} + +uint64_t Player::getPreyCards() const { + return preyCards; +} + +uint32_t Player::getPreyRerollPrice() const { + return getLevel() * g_configManager().getNumber(PREY_REROLL_PRICE_LEVEL); +} + +std::vector Player::getPreyBlackList() const { + std::vector rt; + for (const std::unique_ptr &slot : preys) { + if (slot) { + if (slot->isOccupied()) { + rt.push_back(slot->selectedRaceId); + } + for (uint16_t raceId : slot->raceIdList) { + rt.push_back(raceId); + } + } } - const auto &firstForgedItem = Item::CreateItem(firstItemId, 1); - if (!firstForgedItem) { - g_logger().error("[Log 3] Player with name {} failed to fuse item with id {}", getName(), firstItemId); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; + return rt; +} + +const std::unique_ptr &Player::getPreyWithMonster(uint16_t raceId) const { + if (!g_configManager().getBoolean(PREY_ENABLED)) { + return PreySlotNull; + } + + if (auto it = std::ranges::find_if(preys, [raceId](const std::unique_ptr &preyPtr) { + return preyPtr->selectedRaceId == raceId; + }); + it != preys.end()) { + return *it; + } + + return PreySlotNull; +} + +// Task hunting system + +void Player::initializeTaskHunting() { + if (taskHunting.empty()) { + for (uint8_t slotId = PreySlot_First; slotId <= PreySlot_Last; slotId++) { + auto slot = std::make_unique(static_cast(slotId)); + if (!g_configManager().getBoolean(TASK_HUNTING_ENABLED)) { + slot->state = PreyTaskDataState_Inactive; + } else if (slot->id == PreySlot_Three && !g_configManager().getBoolean(TASK_HUNTING_FREE_THIRD_SLOT)) { + slot->state = PreyTaskDataState_Locked; + } else if (slot->id == PreySlot_Two && !isPremium()) { + slot->state = PreyTaskDataState_Locked; + } else { + slot->state = PreyTaskDataState_Selection; + slot->reloadMonsterGrid(getTaskHuntingBlackList(), getLevel()); + } + + setTaskHuntingSlotClass(slot); + } + } + + if (client && g_configManager().getBoolean(TASK_HUNTING_ENABLED) && !client->oldProtocol) { + client->writeToOutputBuffer(g_ioprey().getTaskHuntingBaseDate()); + } +} + +bool Player::isCreatureUnlockedOnTaskHunting(const std::shared_ptr &mtype) const { + if (!mtype) { + return false; + } + + return getBestiaryKillCount(mtype->info.raceid) >= mtype->info.bestiaryToUnlock; +} + +bool Player::setTaskHuntingSlotClass(std::unique_ptr &slot) { + if (getTaskHuntingSlotById(slot->id)) { + return false; + } + + taskHunting.emplace_back(std::move(slot)); + return true; +} + +uint8_t Player::getBlessingCount(uint8_t index, bool storeCount) const { + if (!storeCount) { + if (index > 0 && index <= blessings.size()) { + return blessings[index - 1]; + } else { + g_logger().error("[{}] - index outside range 0-10.", __FUNCTION__); + return 0; + } + } + auto amount = kv()->scoped("summary")->scoped("blessings")->scoped(fmt::format("{}", index))->get("amount"); + return amount ? static_cast(amount->getNumber()) : 0; +} + +std::string Player::getBlessingsName() const { + std::vector blessingNames; + for (const auto &bless : magic_enum::enum_values()) { + if (hasBlessing(enumToValue(bless))) { + std::string name = toStartCaseWithSpace(magic_enum::enum_name(bless).data()); + blessingNames.emplace_back(name); + } + } + + std::ostringstream os; + if (!blessingNames.empty()) { + // Join all elements but the last with ", " and add the last one with " and " + for (size_t i = 0; i < blessingNames.size() - 1; ++i) { + os << blessingNames[i] << ", "; + } + if (blessingNames.size() > 1) { + os << "and "; + } + os << blessingNames.back() << "."; + } + + return os.str(); +} + +void Player::disconnect() const { + if (client) { + client->disconnect(); + } +} + +uint32_t Player::getIP() const { + return client ? client->getIP() : 0; +} + +void Player::reloadTaskSlot(PreySlot_t slotid) { + if (g_configManager().getBoolean(TASK_HUNTING_ENABLED) && client) { + client->sendTaskHuntingData(getTaskHuntingSlotById(slotid)); + client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints()); + } +} + +const std::unique_ptr &Player::getTaskHuntingSlotById(PreySlot_t slotid) { + if (auto it = std::ranges::find_if(taskHunting, [slotid](const std::unique_ptr &itTask) { + return itTask->id == slotid; + }); + it != taskHunting.end()) { + return *it; + } + + return TaskHuntingSlotNull; +} + +std::vector Player::getTaskHuntingBlackList() const { + std::vector rt; + + std::ranges::for_each(taskHunting, [&rt](const std::unique_ptr &slot) { + if (slot->isOccupied()) { + rt.push_back(slot->selectedRaceId); + } else { + std::ranges::for_each(slot->raceIdList, [&rt](uint16_t raceId) { + rt.push_back(raceId); + }); + } + }); + + return rt; +} + +void Player::sendTaskHuntingData() const { + if (client) { + client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints()); + for (const std::unique_ptr &slot : taskHunting) { + if (slot) { + client->sendTaskHuntingData(slot); + } + } + } +} + +void Player::addTaskHuntingPoints(uint64_t amount) { + taskHuntingPoints += amount; + if (client) { + client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints()); + } +} + +bool Player::useTaskHuntingPoints(uint64_t amount) { + if (taskHuntingPoints < amount) { + return false; + } + + taskHuntingPoints -= amount; + if (client) { + client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints()); + } + return true; +} + +uint64_t Player::getTaskHuntingPoints() const { + return taskHuntingPoints; +} + +uint32_t Player::getTaskHuntingRerollPrice() const { + return getLevel() * g_configManager().getNumber(TASK_HUNTING_REROLL_PRICE_LEVEL); +} + +const std::unique_ptr &Player::getTaskHuntingWithCreature(uint16_t raceId) const { + if (!g_configManager().getBoolean(TASK_HUNTING_ENABLED)) { + return TaskHuntingSlotNull; + } + + if (auto it = std::ranges::find_if(taskHunting, [raceId](const std::unique_ptr &itTask) { + return itTask->selectedRaceId == raceId; + }); + it != taskHunting.end()) { + return *it; + } + + return TaskHuntingSlotNull; +} + +uint32_t Player::getLoyaltyPoints() const { + return loyaltyPoints; +} + +void Player::setLoyaltyBonus(uint16_t bonus) { + loyaltyBonusPercent = bonus; + sendSkills(); +} + +void Player::setLoyaltyTitle(std::string title) { + loyaltyTitle = std::move(title); +} + +std::string Player::getLoyaltyTitle() const { + return loyaltyTitle; +} + +uint16_t Player::getLoyaltyBonus() const { + return loyaltyBonusPercent; +} + +// Depot search system +/******************************************************************************* + * Depot search system + ******************************************************************************/ + +void Player::requestDepotItems() { + ItemsTierCountList itemMap; + uint16_t count = 0; + const auto &depotLocker = getDepotLocker(getLastDepotId()); + if (!depotLocker) { + return; + } + + for (const auto &locker : depotLocker->getItemList()) { + const auto &c = locker->getContainer(); + if (!c || c->empty()) { + continue; + } + + for (ContainerIterator it = c->iterator(); it.hasNext(); it.advance()) { + auto itemMap_it = itemMap.find((*it)->getID()); + + uint8_t itemTier = Item::items[(*it)->getID()].upgradeClassification > 0 ? (*it)->getTier() + 1 : 0; + if (itemMap_it == itemMap.end()) { + std::map itemTierMap; + itemTierMap[itemTier] = Item::countByType((*it), -1); + itemMap[(*it)->getID()] = itemTierMap; + count++; + } else if (auto itemTier_it = itemMap[(*it)->getID()].find(itemTier); itemTier_it == itemMap[(*it)->getID()].end()) { + itemMap[(*it)->getID()][itemTier] = Item::countByType((*it), -1); + count++; + } else { + itemMap[(*it)->getID()][itemTier] += Item::countByType((*it), -1); + } + } + } + + for (const auto &[itemId, itemCount] : getStashItems()) { + auto itemMap_it = itemMap.find(itemId); + // Stackable items not have upgrade classification + if (Item::items[itemId].upgradeClassification > 0) { + g_logger().error("{} - Player {} have wrong item with id {} on stash with upgrade classification", __FUNCTION__, getName(), itemId); + continue; + } + + if (itemMap_it == itemMap.end()) { + std::map itemTierMap; + itemTierMap[0] = itemCount; + itemMap[itemId] = itemTierMap; + count++; + } else if (auto itemTier_it = itemMap[itemId].find(0); itemTier_it == itemMap[itemId].end()) { + itemMap[itemId][0] = itemCount; + count++; + } else { + itemMap[itemId][0] += itemCount; + } + } + + setDepotSearchIsOpen(1, 0); + sendDepotItems(itemMap, count); +} + +void Player::requestDepotSearchItem(uint16_t itemId, uint8_t tier) { + ItemVector depotItems; + ItemVector inboxItems; + uint32_t depotCount = 0; + uint32_t inboxCount = 0; + uint32_t stashCount = 0; + + if (const ItemType &iType = Item::items[itemId]; + iType.stackable && iType.wareId > 0) { + stashCount = getStashItemCount(itemId); + } + + const auto &depotLocker = getDepotLocker(getLastDepotId()); + if (!depotLocker) { + return; + } + + for (const auto &locker : depotLocker->getItemList()) { + const auto &c = locker->getContainer(); + if (!c || c->empty()) { + continue; + } + + inboxItems.reserve(inboxItems.size()); + depotItems.reserve(depotItems.size()); + + for (ContainerIterator it = c->iterator(); it.hasNext(); it.advance()) { + const auto item = *it; + if (!item || item->getID() != itemId || item->getTier() != tier) { + continue; + } + + if (c->isInbox()) { + if (inboxItems.size() < 255) { + inboxItems.emplace_back(item); + } + inboxCount += Item::countByType(item, -1); + } else { + if (depotItems.size() < 255) { + depotItems.emplace_back(item); + } + depotCount += Item::countByType(item, -1); + } + } + } + + setDepotSearchIsOpen(itemId, tier); + sendDepotSearchResultDetail(itemId, tier, depotCount, depotItems, inboxCount, inboxItems, stashCount); +} + +void Player::retrieveAllItemsFromDepotSearch(uint16_t itemId, uint8_t tier, bool isDepot) { + const auto &depotLocker = getDepotLocker(getLastDepotId()); + if (!depotLocker) { + return; + } + + std::vector> itemsVector; + for (const auto &locker : depotLocker->getItemList()) { + const auto &c = locker->getContainer(); + if (!c || c->empty() || + // Retrieve from inbox. + (c->isInbox() && isDepot) || + // Retrieve from depot. + (!c->isInbox() && !isDepot)) { + continue; + } + + for (ContainerIterator it = c->iterator(); it.hasNext(); it.advance()) { + const auto &item = *it; + if (!item) { + continue; + } + + if (item->getID() == itemId && item->getTier() == depotSearchOnItem.second) { + itemsVector.emplace_back(item); + } + } + } + + ReturnValue ret = RETURNVALUE_NOERROR; + for (const auto &item : itemsVector) { + if (!item) { + continue; + } + + // First lets try to retrieve the item to the stash retrieve container. + if (g_game().tryRetrieveStashItems(static_self_cast(), item)) { + continue; + } + + // If the retrieve fails to move the item to the stash retrieve container, let's add the item anywhere. + if (ret = g_game().internalMoveItem(item->getParent(), getPlayer(), INDEX_WHEREEVER, item, item->getItemCount(), nullptr); ret == RETURNVALUE_NOERROR) { + continue; + } + + sendCancelMessage(ret); + return; + } + + requestDepotSearchItem(itemId, tier); +} + +void Player::openContainerFromDepotSearch(const Position &pos) { + if (!isDepotSearchOpen()) { + sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + return; + } + + const auto &item = getItemFromDepotSearch(depotSearchOnItem.first, pos); + if (!item) { + sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + return; + } + + const auto &container = item->getParent() ? item->getParent()->getContainer() : nullptr; + if (!container) { + sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + return; + } + + g_actions().useItem(static_self_cast(), pos, 0, container, false); +} + +std::shared_ptr Player::getItemFromDepotSearch(uint16_t itemId, const Position &pos) { + const auto &depotLocker = getDepotLocker(getLastDepotId()); + if (!depotLocker) { + return nullptr; + } + + uint8_t index = 0; + for (const auto &locker : depotLocker->getItemList()) { + const auto &c = locker->getContainer(); + if (!c || c->empty() || (c->isInbox() && pos.y != 0x21) || // From inbox. + (!c->isInbox() && pos.y != 0x20)) { // From depot. + continue; + } + + for (ContainerIterator it = c->iterator(); it.hasNext(); it.advance()) { + const auto &item = *it; + if (!item || item->getID() != itemId || item->getTier() != depotSearchOnItem.second) { + continue; + } + + if (pos.z == index) { + return item; + } + index++; + } + } + + return nullptr; +} + +std::pair>, std::map>> Player::requestLockerItems(const std::shared_ptr &depotLocker, bool sendToClient, uint8_t tier) const { + if (!depotLocker) { + g_logger().error("{} - Depot locker is nullptr", __FUNCTION__); + return {}; + } + + std::map> lockerItems; + std::vector> itemVector; + std::vector> containers { depotLocker }; + + for (size_t i = 0; i < containers.size(); ++i) { + const auto &container = containers[i]; + + for (const auto &item : container->getItemList()) { + const auto &lockerContainers = item->getContainer(); + if (lockerContainers && !lockerContainers->empty()) { + containers.emplace_back(lockerContainers); + continue; + } + + const ItemType &itemType = Item::items[item->getID()]; + if (item->isStoreItem() || itemType.wareId == 0) { + continue; + } + + if (lockerContainers && (!itemType.isContainer() || lockerContainers->capacity() != itemType.maxItems)) { + continue; + } + + if (!item->hasMarketAttributes() || (!sendToClient && item->getTier() != tier)) { + continue; + } + + lockerItems[itemType.wareId][item->getTier()] += Item::countByType(item, -1); + itemVector.emplace_back(item); + } + } + + StashItemList stashToSend = getStashItems(); + for (const auto &[itemId, itemCount] : stashToSend) { + const ItemType &itemType = Item::items[itemId]; + if (itemType.wareId != 0) { + lockerItems[itemType.wareId][0] += itemCount; + } + } + + return { itemVector, lockerItems }; +} + +/** + This function returns a pair of an array of items and a 16-bit integer from a DepotLocker instance, a 8-bit byte and a 16-bit integer. + @param depotLocker The instance of DepotLocker from which to retrieve items. + @param tier The 8-bit byte that specifies the level of the tier to search. + @param itemId The 16-bit integer that specifies the ID of the item to search for. + @return A pair of an array of items and a 16-bit integer, where the array of items is filled with all items from the + locker with the specified id and the 16-bit integer is the total items found. + */ + +std::pair>, uint16_t> Player::getLockerItemsAndCountById(const std::shared_ptr &depotLocker, uint8_t tier, uint16_t itemId) const { + std::vector> lockerItems; + const auto &[itemVector, itemMap] = requestLockerItems(depotLocker, false, tier); + uint16_t totalCount = 0; + for (const auto &item : itemVector) { + if (!item || item->getID() != itemId) { + continue; + } + + totalCount++; + lockerItems.emplace_back(item); + } + + return std::make_pair(lockerItems, totalCount); +} + +bool Player::saySpell(SpeakClasses type, const std::string &text, bool isGhostMode, const Spectators* spectatorsPtr, const Position* pos) { + if (text.empty()) { + g_logger().debug("{} - Spell text is empty for player {}", __FUNCTION__, getName()); + return false; + } + + if (!pos) { + pos = &getPosition(); + } + + Spectators spectators; + + if (!spectatorsPtr || spectatorsPtr->empty()) { + // This somewhat complex construct ensures that the cached Spectators + // is used if available and if it can be used, else a local vector is + // used (hopefully the compiler will optimize away the construction of + // the temporary when it's not used). + if (type != TALKTYPE_YELL && type != TALKTYPE_MONSTER_YELL) { + spectators.find(*pos, false, MAP_MAX_CLIENT_VIEW_PORT_X, MAP_MAX_CLIENT_VIEW_PORT_X, MAP_MAX_CLIENT_VIEW_PORT_Y, MAP_MAX_CLIENT_VIEW_PORT_Y); + } else { + spectators.find(*pos, true, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_X + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2, (MAP_MAX_CLIENT_VIEW_PORT_Y + 1) * 2); + } + } else { + spectators = (*spectatorsPtr); + } + + int32_t valueEmote = 0; + // Send to client + for (const auto &spectator : spectators) { + if (const auto &tmpPlayer = spectator->getPlayer()) { + if (g_configManager().getBoolean(EMOTE_SPELLS)) { + valueEmote = tmpPlayer->getStorageValue(STORAGEVALUE_EMOTE); + } + if (!isGhostMode || tmpPlayer->canSeeCreature(static_self_cast())) { + if (valueEmote == 1) { + tmpPlayer->sendCreatureSay(static_self_cast(), TALKTYPE_MONSTER_SAY, text, pos); + } else { + tmpPlayer->sendCreatureSay(static_self_cast(), TALKTYPE_SPELL_USE, text, pos); + } + } + } + } + + // Execute lua event method + for (const auto &spectator : spectators) { + const auto &tmpPlayer = spectator->getPlayer(); + if (!tmpPlayer) { + continue; + } + + tmpPlayer->onCreatureSay(static_self_cast(), type, text); + if (static_self_cast() != tmpPlayer) { + g_events().eventCreatureOnHear(tmpPlayer, getPlayer(), text, type); + g_callbacks().executeCallback(EventCallback_t::creatureOnHear, &EventCallback::creatureOnHear, tmpPlayer, getPlayer(), text, type); + } + } + return true; +} + +void Player::triggerMomentum() { + double_t chance = 0; + if (const auto &item = getInventoryItem(CONST_SLOT_HEAD)) { + chance += item->getMomentumChance(); + } + + chance += m_wheelPlayer->getBonusData().momentum; + double_t randomChance = uniform_random(0, 10000) / 100.; + if (getZoneType() != ZONE_PROTECTION && hasCondition(CONDITION_INFIGHT) && ((OTSYS_TIME() / 1000) % 2) == 0 && chance > 0 && randomChance < chance) { + bool triggered = false; + auto it = conditions.begin(); + while (it != conditions.end()) { + const auto condItem = *it; + const ConditionType_t type = condItem->getType(); + constexpr auto maxu16 = std::numeric_limits::max(); + const auto checkSpellId = condItem->getSubId(); + auto spellId = checkSpellId > maxu16 ? 0u : static_cast(checkSpellId); + const int32_t ticks = condItem->getTicks(); + const int32_t newTicks = (ticks <= 2000) ? 0 : ticks - 2000; + triggered = true; + if (type == CONDITION_SPELLCOOLDOWN || (type == CONDITION_SPELLGROUPCOOLDOWN && spellId > SPELLGROUP_SUPPORT)) { + condItem->setTicks(newTicks); + type == CONDITION_SPELLGROUPCOOLDOWN ? sendSpellGroupCooldown(static_cast(spellId), newTicks) : sendSpellCooldown(spellId, newTicks); + } + ++it; + } + if (triggered) { + g_game().addMagicEffect(getPosition(), CONST_ME_HOURGLASS); + sendTextMessage(MESSAGE_ATTENTION, "Momentum was triggered."); + } + } +} + +void Player::clearCooldowns() { + auto it = conditions.begin(); + while (it != conditions.end()) { + const auto &condItem = *it; + if (!condItem) { + ++it; + continue; + } + + const ConditionType_t type = condItem->getType(); + constexpr auto maxu16 = std::numeric_limits::max(); + const auto checkSpellId = condItem->getSubId(); + auto spellId = checkSpellId > maxu16 ? 0u : static_cast(checkSpellId); + if (type == CONDITION_SPELLCOOLDOWN || type == CONDITION_SPELLGROUPCOOLDOWN) { + condItem->setTicks(0); + type == CONDITION_SPELLGROUPCOOLDOWN ? sendSpellGroupCooldown(static_cast(spellId), 0) : sendSpellCooldown(spellId, 0); + } + ++it; + } +} + +void Player::triggerTranscendance() { + if (wheel()->getOnThinkTimer(WheelOnThink_t::AVATAR_FORGE) > OTSYS_TIME()) { + return; + } + + const auto &item = getInventoryItem(CONST_SLOT_LEGS); + if (item == nullptr) { + return; + } + + const double_t chance = item->getTranscendenceChance(); + const double_t randomChance = uniform_random(0, 10000) / 100.; + if (getZoneType() != ZONE_PROTECTION && checkLastAggressiveActionWithin(2000) && ((OTSYS_TIME() / 1000) % 2) == 0 && chance > 0 && randomChance < chance) { + int64_t duration = g_configManager().getNumber(TRANSCENDANCE_AVATAR_DURATION); + const auto &outfitCondition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_OUTFIT, duration, 0)->static_self_cast(); + Outfit_t outfit; + outfit.lookType = getVocation()->getAvatarLookType(); + outfitCondition->setOutfit(outfit); + addCondition(outfitCondition); + wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR_FORGE, OTSYS_TIME() + duration); + g_game().addMagicEffect(getPosition(), CONST_ME_AVATAR_APPEAR); + + sendSkills(); + sendStats(); + sendBasicData(); + + sendTextMessage(MESSAGE_ATTENTION, "Transcendance was triggered."); + + // Send player data after transcendance timer expire + const auto &task = createPlayerTask( + std::max(SCHEDULER_MINTICKS, duration), + [playerId = getID()] { + const auto &player = g_game().getPlayerByID(playerId); + if (player) { + player->sendSkills(); + player->sendStats(); + player->sendBasicData(); + } + }, + __FUNCTION__ + ); + g_dispatcher().scheduleEvent(task); + + wheel()->sendGiftOfLifeCooldown(); + g_game().reloadCreature(getPlayer()); + } +} + +// Forge system +// Forge system + +void Player::forgeFuseItems(ForgeAction_t actionType, uint16_t firstItemId, uint8_t tier, uint16_t secondItemId, bool success, bool reduceTierLoss, bool convergence, uint8_t bonus, uint8_t coreCount) { + if (getFreeBackpackSlots() == 0) { + sendCancelMessage(RETURNVALUE_NOTENOUGHROOM); + return; + } + + ForgeHistory history; + history.actionType = actionType; + history.tier = tier; + history.success = success; + history.tierLoss = reduceTierLoss; + + const auto &firstForgingItem = getForgeItemFromId(firstItemId, tier); + if (!firstForgingItem) { + g_logger().error("[Log 1] Player with name {} failed to fuse item with id {}", getName(), firstItemId); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + auto returnValue = g_game().internalRemoveItem(firstForgingItem, 1); + if (returnValue != RETURNVALUE_NOERROR) { + g_logger().error("[Log 1] Failed to remove forge item {} from player with name {}", firstItemId, getName()); + sendCancelMessage(getReturnMessage(returnValue)); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + const auto &secondForgingItem = getForgeItemFromId(secondItemId, tier); + if (!secondForgingItem) { + g_logger().error("[Log 2] Player with name {} failed to fuse item with id {}", getName(), secondItemId); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + if (returnValue = g_game().internalRemoveItem(secondForgingItem, 1); + returnValue != RETURNVALUE_NOERROR) { + g_logger().error("[Log 2] Failed to remove forge item {} from player with name {}", secondItemId, getName()); + sendCancelMessage(getReturnMessage(returnValue)); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + const auto &exaltationChest = Item::CreateItem(ITEM_EXALTATION_CHEST, 1); + if (!exaltationChest) { + g_logger().error("Failed to create exaltation chest"); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + const auto &exaltationContainer = exaltationChest->getContainer(); + if (!exaltationContainer) { + g_logger().error("Failed to create exaltation container"); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + const auto &firstForgedItem = Item::CreateItem(firstItemId, 1); + if (!firstForgedItem) { + g_logger().error("[Log 3] Player with name {} failed to fuse item with id {}", getName(), firstItemId); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + returnValue = g_game().internalAddItem(exaltationContainer, firstForgedItem, INDEX_WHEREEVER); + if (returnValue != RETURNVALUE_NOERROR) { + g_logger().error("[Log 1] Failed to add forge item {} from player with name {}", firstItemId, getName()); + sendCancelMessage(getReturnMessage(returnValue)); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + auto configKey = convergence ? FORGE_CONVERGENCE_FUSION_DUST_COST : FORGE_FUSION_DUST_COST; + auto dustCost = static_cast(g_configManager().getNumber(configKey)); + if (convergence) { + firstForgedItem->setTier(tier + 1); + history.dustCost = dustCost; + setForgeDusts(getForgeDusts() - dustCost); + + uint64_t cost = 0; + for (const auto* itemClassification : g_game().getItemsClassifications()) { + if (itemClassification->id != firstForgingItem->getClassification()) { + continue; + } + + for (const auto &[mapTier, mapPrice] : itemClassification->tiers) { + if (mapTier == firstForgingItem->getTier() + 1) { + cost = mapPrice.convergenceFusionPrice; + break; + } + } + break; + } + if (!g_game().removeMoney(static_self_cast(), cost, 0, true)) { + g_logger().error("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName()); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + g_metrics().addCounter("balance_decrease", cost, { { "player", getName() }, { "context", "forge_convergence_fuse" } }); + history.cost = cost; + } else { + firstForgedItem->setTier(tier); + const auto &secondForgedItem = Item::CreateItem(secondItemId, 1); + if (!secondForgedItem) { + g_logger().error("[Log 4] Player with name {} failed to fuse item with id {}", getName(), secondItemId); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + secondForgedItem->setTier(tier); + returnValue = g_game().internalAddItem(exaltationContainer, secondForgedItem, INDEX_WHEREEVER); + if (returnValue != RETURNVALUE_NOERROR) { + g_logger().error("[Log 2] Failed to add forge item {} from player with name {}", secondItemId, getName()); + sendCancelMessage(getReturnMessage(returnValue)); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + if (success) { + firstForgedItem->setTier(tier + 1); + + if (bonus != 1) { + history.dustCost = dustCost; + setForgeDusts(getForgeDusts() - dustCost); + } + if (bonus != 2) { + if (coreCount != 0 && !removeItemCountById(ITEM_FORGE_CORE, coreCount)) { + g_logger().error("[{}][Log 1] Failed to remove item 'id :{} count: {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_CORE), coreCount, getName()); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + history.coresCost = coreCount; + } + if (bonus != 3) { + uint64_t cost = 0; + for (const auto* itemClassification : g_game().getItemsClassifications()) { + if (itemClassification->id != firstForgedItem->getClassification()) { + continue; + } + if (!itemClassification->tiers.contains(firstForgedItem->getTier())) { + g_logger().error("[{}] Failed to find tier {} for item {} in classification {}", __FUNCTION__, firstForgedItem->getTier(), firstForgedItem->getClassification(), itemClassification->id); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + break; + } + cost = itemClassification->tiers.at(firstForgedItem->getTier()).regularPrice; + break; + } + if (!g_game().removeMoney(static_self_cast(), cost, 0, true)) { + g_logger().error("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName()); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + g_metrics().addCounter("balance_decrease", cost, { { "player", getName() }, { "context", "forge_fuse" } }); + history.cost = cost; + } + + if (bonus == 4) { + if (tier > 0) { + secondForgedItem->setTier(tier - 1); + } + } else if (bonus == 6) { + secondForgedItem->setTier(tier + 1); + } else if (bonus == 7 && tier + 2 <= firstForgedItem->getClassification()) { + firstForgedItem->setTier(tier + 2); + } + + if (bonus != 4 && bonus != 5 && bonus != 6 && bonus != 8) { + returnValue = g_game().internalRemoveItem(secondForgedItem, 1); + if (returnValue != RETURNVALUE_NOERROR) { + g_logger().error("[Log 6] Failed to remove forge item {} from player with name {}", secondItemId, getName()); + sendCancelMessage(getReturnMessage(returnValue)); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + } + } else { + auto isTierLost = uniform_random(1, 100) <= (reduceTierLoss ? g_configManager().getNumber(FORGE_TIER_LOSS_REDUCTION) : 100); + if (isTierLost) { + if (secondForgedItem->getTier() >= 1) { + secondForgedItem->setTier(tier - 1); + } else { + returnValue = g_game().internalRemoveItem(secondForgedItem, 1); + if (returnValue != RETURNVALUE_NOERROR) { + g_logger().error("[Log 7] Failed to remove forge item {} from player with name {}", secondItemId, getName()); + sendCancelMessage(getReturnMessage(returnValue)); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + } + } + bonus = (isTierLost ? 0 : 8); + history.coresCost = coreCount; + + if (getForgeDusts() < dustCost) { + g_logger().error("[Log 7] Failed to remove fuse dusts from player with name {}", getName()); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } else { + setForgeDusts(getForgeDusts() - dustCost); + } + + if (coreCount != 0 && !removeItemCountById(ITEM_FORGE_CORE, coreCount)) { + g_logger().error("[{}][Log 2] Failed to remove item 'id: {}, count: {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_CORE), coreCount, getName()); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + uint64_t cost = 0; + for (const auto* itemClassification : g_game().getItemsClassifications()) { + if (itemClassification->id != firstForgingItem->getClassification()) { + continue; + } + if (!itemClassification->tiers.contains(firstForgingItem->getTier() + 1)) { + g_logger().error("[{}] Failed to find tier {} for item {} in classification {}", __FUNCTION__, firstForgingItem->getTier() + 1, firstForgingItem->getClassification(), itemClassification->id); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + break; + } + cost = itemClassification->tiers.at(firstForgingItem->getTier() + 1).regularPrice; + break; + } + if (!g_game().removeMoney(static_self_cast(), cost, 0, true)) { + g_logger().error("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName()); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + g_metrics().addCounter("balance_decrease", cost, { { "player", getName() }, { "context", "forge_fuse" } }); + + history.cost = cost; + } + } + + returnValue = g_game().internalAddItem(static_self_cast(), exaltationContainer, INDEX_WHEREEVER); + if (returnValue != RETURNVALUE_NOERROR) { + g_logger().error("Failed to add exaltation chest to player with name {}", fmt::underlying(ITEM_EXALTATION_CHEST), getName()); + sendCancelMessage(getReturnMessage(returnValue)); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + history.firstItemName = firstForgingItem->getName(); + history.secondItemName = secondForgingItem->getName(); + history.bonus = bonus; + history.createdAt = getTimeNow(); + history.convergence = convergence; + registerForgeHistoryDescription(history); + + sendForgeResult(actionType, firstItemId, tier, secondItemId, tier + 1, success, bonus, coreCount, convergence); +} + +void Player::forgeTransferItemTier(ForgeAction_t actionType, uint16_t donorItemId, uint8_t tier, uint16_t receiveItemId, bool convergence) { + if (getFreeBackpackSlots() == 0) { + sendCancelMessage(RETURNVALUE_NOTENOUGHROOM); + return; + } + + ForgeHistory history; + history.actionType = actionType; + history.tier = tier; + history.success = true; + + const auto &donorItem = getForgeItemFromId(donorItemId, tier); + if (!donorItem) { + g_logger().error("[Log 1] Player with name {} failed to transfer item with id {}", getName(), donorItemId); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + auto returnValue = g_game().internalRemoveItem(donorItem, 1); + if (returnValue != RETURNVALUE_NOERROR) { + g_logger().error("[Log 1] Failed to remove transfer item {} from player with name {}", donorItemId, getName()); + sendCancelMessage(getReturnMessage(returnValue)); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + const auto &receiveItem = getForgeItemFromId(receiveItemId, 0); + if (!receiveItem) { + g_logger().error("[Log 2] Player with name {} failed to transfer item with id {}", getName(), receiveItemId); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + if (returnValue = g_game().internalRemoveItem(receiveItem, 1); + returnValue != RETURNVALUE_NOERROR) { + g_logger().error("[Log 2] Failed to remove transfer item {} from player with name {}", receiveItemId, getName()); + sendCancelMessage(getReturnMessage(returnValue)); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + const auto &exaltationChest = Item::CreateItem(ITEM_EXALTATION_CHEST, 1); + if (!exaltationChest) { + g_logger().error("Exaltation chest is nullptr"); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + const auto &exaltationContainer = exaltationChest->getContainer(); + if (!exaltationContainer) { + g_logger().error("Exaltation container is nullptr"); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + const auto &newReceiveItem = Item::CreateItem(receiveItemId, 1); + if (!newReceiveItem) { + g_logger().error("[Log 6] Player with name {} failed to fuse item with id {}", getName(), receiveItemId); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + auto configKey = convergence ? FORGE_CONVERGENCE_TRANSFER_DUST_COST : FORGE_TRANSFER_DUST_COST; + if (getForgeDusts() < g_configManager().getNumber(configKey)) { + g_logger().error("[Log 8] Failed to remove transfer dusts from player with name {}", getName()); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } else { + setForgeDusts(getForgeDusts() - g_configManager().getNumber(configKey)); + } + + setForgeDusts(getForgeDusts() - g_configManager().getNumber(configKey)); + + if (convergence) { + newReceiveItem->setTier(tier); + } else { + newReceiveItem->setTier(tier - 1); + } + returnValue = g_game().internalAddItem(exaltationContainer, newReceiveItem, INDEX_WHEREEVER); + if (returnValue != RETURNVALUE_NOERROR) { + g_logger().error("[Log 7] Failed to add forge item {} from player with name {}", receiveItemId, getName()); + sendCancelMessage(getReturnMessage(returnValue)); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + uint8_t coresAmount = 0; + uint64_t cost = 0; + for (const auto &itemClassification : g_game().getItemsClassifications()) { + if (itemClassification->id != donorItem->getClassification()) { + continue; + } + if (!itemClassification->tiers.contains(donorItem->getTier())) { + g_logger().error("[{}] Failed to find tier {} for item {} in classification {}", __FUNCTION__, donorItem->getTier(), donorItem->getClassification(), itemClassification->id); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + break; + } + + const uint8_t toTier = convergence ? donorItem->getTier() : donorItem->getTier() - 1; + auto tierPriecs = itemClassification->tiers.at(toTier); + cost = convergence ? tierPriecs.convergenceTransferPrice : tierPriecs.regularPrice; + coresAmount = tierPriecs.corePrice; + break; + } + + if (!removeItemCountById(ITEM_FORGE_CORE, coresAmount)) { + g_logger().error("[{}] Failed to remove item 'id: {}, count: {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_CORE), 1, getName()); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + if (!g_game().removeMoney(static_self_cast(), cost, 0, true)) { + g_logger().error("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName()); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + history.cost = cost; + g_metrics().addCounter("balance_decrease", cost, { { "player", getName() }, { "context", "forge_transfer" } }); + + returnValue = g_game().internalAddItem(static_self_cast(), exaltationContainer, INDEX_WHEREEVER); + if (returnValue != RETURNVALUE_NOERROR) { + g_logger().error("[Log 10] Failed to add forge item {} from player with name {}", fmt::underlying(ITEM_EXALTATION_CHEST), getName()); + sendCancelMessage(getReturnMessage(returnValue)); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + history.firstItemName = Item::items[donorItemId].name; + history.secondItemName = newReceiveItem->getName(); + history.createdAt = getTimeNow(); + history.convergence = convergence; + registerForgeHistoryDescription(history); + + sendForgeResult(actionType, donorItemId, tier, receiveItemId, convergence ? tier : tier - 1, true, 0, 0, convergence); +} + +void Player::forgeResourceConversion(ForgeAction_t actionType) { + ForgeHistory history; + history.actionType = actionType; + history.success = true; + + ReturnValue returnValue = RETURNVALUE_NOERROR; + if (actionType == ForgeAction_t::DUSTTOSLIVERS) { + auto dusts = getForgeDusts(); + auto cost = static_cast(g_configManager().getNumber(FORGE_COST_ONE_SLIVER) * g_configManager().getNumber(FORGE_SLIVER_AMOUNT)); + if (cost > dusts) { + g_logger().error("[{}] Not enough dust", __FUNCTION__); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + auto itemCount = static_cast(g_configManager().getNumber(FORGE_SLIVER_AMOUNT)); + const auto &item = Item::CreateItem(ITEM_FORGE_SLIVER, itemCount); + returnValue = g_game().internalPlayerAddItem(static_self_cast(), item); + if (returnValue != RETURNVALUE_NOERROR) { + g_logger().error("Failed to add {} slivers to player with name {}", itemCount, getName()); + sendCancelMessage(getReturnMessage(returnValue)); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + history.cost = cost; + history.gained = 3; + setForgeDusts(dusts - cost); + } else if (actionType == ForgeAction_t::SLIVERSTOCORES) { + const auto &[sliverCount, coreCount] = getForgeSliversAndCores(); + auto cost = static_cast(g_configManager().getNumber(FORGE_CORE_COST)); + if (cost > sliverCount) { + g_logger().error("[{}] Not enough sliver", __FUNCTION__); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + if (!removeItemCountById(ITEM_FORGE_SLIVER, cost)) { + g_logger().error("[{}] Failed to remove item 'id: {}, count {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_SLIVER), cost, getName()); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + if (const auto &item = Item::CreateItem(ITEM_FORGE_CORE, 1); + item) { + returnValue = g_game().internalPlayerAddItem(static_self_cast(), item); + } + if (returnValue != RETURNVALUE_NOERROR) { + g_logger().error("Failed to add one core to player with name {}", getName()); + sendCancelMessage(getReturnMessage(returnValue)); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + history.cost = cost; + history.gained = 1; + } else { + auto dustLevel = getForgeDustLevel(); + if (dustLevel >= g_configManager().getNumber(FORGE_MAX_DUST)) { + g_logger().error("[{}] Maximum level reached", __FUNCTION__); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + const auto upgradeCost = dustLevel - 75; + if (const auto dusts = getForgeDusts(); + upgradeCost > dusts) { + g_logger().error("[{}] Not enough dust", __FUNCTION__); + sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + return; + } + + history.cost = upgradeCost; + history.gained = dustLevel; + removeForgeDusts(upgradeCost); + addForgeDustLevel(1); + } + + history.createdAt = getTimeNow(); + registerForgeHistoryDescription(history); + sendForgingData(); +} + +void Player::forgeHistory(uint8_t page) const { + sendForgeHistory(page); +} + +void Player::sendOpenForge() const { + if (client) { + client->sendOpenForge(); + } +} + +void Player::sendForgeError(ReturnValue returnValue) const { + if (client) { + client->sendForgeError(returnValue); + } +} + +void Player::sendForgeResult(ForgeAction_t actionType, uint16_t leftItemId, uint8_t leftTier, uint16_t rightItemId, uint8_t rightTier, bool success, uint8_t bonus, uint8_t coreCount, bool convergence) const { + if (client) { + client->sendForgeResult(actionType, leftItemId, leftTier, rightItemId, rightTier, success, bonus, coreCount, convergence); + } +} + +void Player::sendForgeHistory(uint8_t page) const { + if (client) { + client->sendForgeHistory(page); + } +} + +void Player::closeForgeWindow() const { + if (client) { + client->closeForgeWindow(); + } +} + +void Player::setForgeDusts(uint64_t amount) { + forgeDusts = amount; + if (client) { + client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints(), getForgeDusts()); + } +} + +void Player::addForgeDusts(uint64_t amount) { + forgeDusts += amount; + if (client) { + client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints(), getForgeDusts()); + } +} + +void Player::removeForgeDusts(uint64_t amount) { + forgeDusts = std::max(0, forgeDusts - amount); + if (client) { + client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints(), getForgeDusts()); + } +} + +uint64_t Player::getForgeDusts() const { + return forgeDusts; +} + +void Player::addForgeDustLevel(uint64_t amount) { + forgeDustLevel += amount; + if (client) { + client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints(), getForgeDusts()); + } +} + +void Player::removeForgeDustLevel(uint64_t amount) { + forgeDustLevel = std::max(0, forgeDustLevel - amount); + if (client) { + client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints(), getForgeDusts()); + } +} + +uint64_t Player::getForgeDustLevel() const { + return forgeDustLevel; +} + +std::vector &Player::getForgeHistory() { + return forgeHistoryVector; +} + +void Player::setForgeHistory(const ForgeHistory &history) { + forgeHistoryVector.emplace_back(history); +} + +void Player::registerForgeHistoryDescription(ForgeHistory history) { + std::string successfulString = history.success ? "Successful" : "Unsuccessful"; + std::string historyTierString = history.tier > 0 ? "tier - 1" : "consumed"; + std::string price = history.bonus != 3 ? formatPrice(std::to_string(history.cost), true) : "0"; + std::stringstream detailsResponse; + auto itemId = Item::items.getItemIdByName(history.firstItemName); + const ItemType &itemType = Item::items[itemId]; + if (history.actionType == ForgeAction_t::FUSION) { + if (history.success) { + detailsResponse << fmt::format( + "{:s}{:s}

" + "Fusion partners:" + "
    " + "
  • " + "First item: {:s} {:s}, tier {:s}" + "
  • " + "
  • " + "Second item: {:s} {:s}, tier {:s}" + "
  • " + "
" + "
" + "Result:" + "
    " + "
  • " + "First item: tier + 1" + "
  • " + "
  • " + "Second item: {:s}" + "
  • " + "
" + "
" + "Invested:" + "
    " + "
  • " + "{:d} cores" + "
  • " + "
  • " + "{:d} dust" + "
  • " + "
  • " + "{:s} gold" + "
  • " + "
", + successfulString, + history.convergence ? " (convergence)" : "", + itemType.article, itemType.name, std::to_string(history.tier), + itemType.article, itemType.name, std::to_string(history.tier), + history.bonus == 8 ? "unchanged" : "consumed", + history.coresCost, history.dustCost, price + ); + } else { + detailsResponse << fmt::format( + "{:s}{:s}

" + "Fusion partners:" + "
    " + "
  • " + "First item: {:s} {:s}, tier {:s}" + "
  • " + "
  • " + "Second item: {:s} {:s}, tier {:s}" + "
  • " + "
" + "
" + "Result:" + "
    " + "
  • " + "First item: unchanged" + "
  • " + "
  • " + "Second item: {:s}" + "
  • " + "
" + "
" + "Invested:" + "
    " + "
  • " + "{:d} cores" + "
  • " + "
  • " + "100 dust" + "
  • " + "
  • " + "{:s} gold" + "
  • " + "
", + successfulString, + history.convergence ? " (convergence)" : "", + itemType.article, itemType.name, std::to_string(history.tier), + itemType.article, itemType.name, std::to_string(history.tier), + history.bonus == 8 ? "unchanged" : historyTierString, + history.coresCost, price + ); + } + } else if (history.actionType == ForgeAction_t::TRANSFER) { + detailsResponse << fmt::format( + "{:s}{:s}

" + "Transfer partners:" + "
    " + "
  • " + "First item: {:s} {:s}, tier {:s}" + "
  • " + "
  • " + "Second item: {:s} {:s}, tier {:s}" + "
  • " + "
" + "
" + "Result:" + "
    " + "
  • " + "First item: {:s} {:s}, tier {:s}" + "
  • " + "
  • " + "Second item: {:s} {:s}, {:s}" + "
  • " + "
" + "
" + "Invested:" + "
    " + "
  • " + "1 cores" + "
  • " + "
  • " + "100 dust" + "
  • " + "
  • " + "{:s} gold" + "
  • " + "
", + successfulString, + history.convergence ? " (convergence)" : "", + itemType.article, itemType.name, std::to_string(history.tier), + itemType.article, itemType.name, std::to_string(history.tier), + itemType.article, itemType.name, std::to_string(history.tier), + itemType.article, itemType.name, std::to_string(history.tier), + price + ); + } else if (history.actionType == ForgeAction_t::DUSTTOSLIVERS) { + detailsResponse << fmt::format("Converted {:d} dust to {:d} slivers.", history.cost, history.gained); + } else if (history.actionType == ForgeAction_t::SLIVERSTOCORES) { + history.actionType = ForgeAction_t::DUSTTOSLIVERS; + detailsResponse << fmt::format("Converted {:d} slivers to {:d} exalted core.", history.cost, history.gained); + } else if (history.actionType == ForgeAction_t::INCREASELIMIT) { + history.actionType = ForgeAction_t::DUSTTOSLIVERS; + detailsResponse << fmt::format("Spent {:d} dust to increase the dust limit to {:d}.", history.cost, history.gained + 1); + } else { + detailsResponse << "(unknown)"; + } + + history.description = detailsResponse.str(); + + setForgeHistory(history); +} + +// Quickloot + +void Player::openPlayerContainers() { + std::vector>> openContainersList; + + for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; i++) { + const auto &item = inventory[i]; + if (!item) { + continue; + } + + const auto &itemContainer = item->getContainer(); + if (itemContainer) { + const auto &cid = item->getAttribute(ItemAttribute_t::OPENCONTAINER); + if (cid > 0) { + openContainersList.emplace_back(cid, itemContainer); + } + for (ContainerIterator it = itemContainer->iterator(); it.hasNext(); it.advance()) { + const auto &subContainer = (*it)->getContainer(); + if (subContainer) { + const auto &subcid = (*it)->getAttribute(ItemAttribute_t::OPENCONTAINER); + if (subcid > 0) { + openContainersList.emplace_back(subcid, subContainer); + } + } + } + } + } + + std::ranges::sort(openContainersList, [](const std::pair> &left, const std::pair> &right) { + return left.first < right.first; + }); + + for (const auto &[containerId, container] : openContainersList) { + addContainer(containerId - 1, container); + onSendContainer(container); + } +} + +// Quickloot + +void Player::sendLootContainers() const { + if (client) { + client->sendLootContainers(); + } +} + +void Player::sendSingleSoundEffect(const Position &pos, SoundEffect_t id, SourceEffect_t source) const { + if (client) { + client->sendSingleSoundEffect(pos, id, source); + } +} + +void Player::sendDoubleSoundEffect(const Position &pos, SoundEffect_t mainSoundId, SourceEffect_t mainSource, SoundEffect_t secondarySoundId, SourceEffect_t secondarySource) const { + if (client) { + client->sendDoubleSoundEffect(pos, mainSoundId, mainSource, secondarySoundId, secondarySource); + } +} + +SoundEffect_t Player::getAttackSoundEffect() const { + const auto &tool = getWeapon(); + if (tool == nullptr) { + return SoundEffect_t::HUMAN_CLOSE_ATK_FIST; + } + + const ItemType &it = Item::items[tool->getID()]; + if (it.weaponType == WEAPON_NONE || it.weaponType == WEAPON_SHIELD) { + return SoundEffect_t::HUMAN_CLOSE_ATK_FIST; + } + + switch (it.weaponType) { + case WEAPON_AXE: { + return SoundEffect_t::MELEE_ATK_AXE; + } + case WEAPON_SWORD: { + return SoundEffect_t::MELEE_ATK_SWORD; + } + case WEAPON_CLUB: { + return SoundEffect_t::MELEE_ATK_CLUB; + } + case WEAPON_AMMO: + case WEAPON_DISTANCE: { + if (tool->getAmmoType() == AMMO_BOLT) { + return SoundEffect_t::DIST_ATK_CROSSBOW; + } + if (tool->getAmmoType() == AMMO_ARROW) { + return SoundEffect_t::DIST_ATK_BOW; + } + return SoundEffect_t::DIST_ATK_THROW; + + break; + } + case WEAPON_WAND: { + return SoundEffect_t::MAGICAL_RANGE_ATK; + } + default: { + return SoundEffect_t::SILENCE; + } + } + + return SoundEffect_t::SILENCE; +} + +SoundEffect_t Player::getHitSoundEffect() const { + // Distance sound effects + const auto &tool = getWeapon(); + if (tool == nullptr) { + return SoundEffect_t::SILENCE; + } + + switch (const auto &it = Item::items[tool->getID()]; it.weaponType) { + case WEAPON_AMMO: { + if (it.ammoType == AMMO_BOLT) { + return SoundEffect_t::DIST_ATK_CROSSBOW_SHOT; + } + if (it.ammoType == AMMO_ARROW) { + if (it.shootType == CONST_ANI_BURSTARROW) { + return SoundEffect_t::BURST_ARROW_EFFECT; + } + if (it.shootType == CONST_ANI_DIAMONDARROW) { + return SoundEffect_t::DIAMOND_ARROW_EFFECT; + } + } else { + return SoundEffect_t::DIST_ATK_THROW_SHOT; + } + } + case WEAPON_DISTANCE: { + if (tool->getAmmoType() == AMMO_BOLT) { + return SoundEffect_t::DIST_ATK_CROSSBOW_SHOT; + } + if (tool->getAmmoType() == AMMO_ARROW) { + return SoundEffect_t::DIST_ATK_BOW_SHOT; + } + return SoundEffect_t::DIST_ATK_THROW_SHOT; + } + case WEAPON_WAND: { + // Separate between wand and rod here + // return SoundEffect_t::DIST_ATK_ROD_SHOT; + return SoundEffect_t::DIST_ATK_WAND_SHOT; + } + default: { + return SoundEffect_t::SILENCE; + } + } // switch + + return SoundEffect_t::SILENCE; +} + +// event methods + +void Player::onUpdateTileItem(const std::shared_ptr &updateTile, const Position &pos, const std::shared_ptr &oldItem, const ItemType &oldType, const std::shared_ptr &newItem, const ItemType &newType) { + Creature::onUpdateTileItem(updateTile, pos, oldItem, oldType, newItem, newType); + + if (oldItem != newItem) { + onRemoveTileItem(updateTile, pos, oldType, oldItem); } - returnValue = g_game().internalAddItem(exaltationContainer, firstForgedItem, INDEX_WHEREEVER); - if (returnValue != RETURNVALUE_NOERROR) { - g_logger().error("[Log 1] Failed to add forge item {} from player with name {}", firstItemId, getName()); - sendCancelMessage(getReturnMessage(returnValue)); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; + + if (tradeState != TRADE_TRANSFER) { + if (tradeItem && oldItem == tradeItem) { + g_game().internalCloseTrade(getPlayer()); + } } +} - auto configKey = convergence ? FORGE_CONVERGENCE_FUSION_DUST_COST : FORGE_FUSION_DUST_COST; - auto dustCost = static_cast(g_configManager().getNumber(configKey)); - if (convergence) { - firstForgedItem->setTier(tier + 1); - history.dustCost = dustCost; - setForgeDusts(getForgeDusts() - dustCost); +void Player::onRemoveTileItem(const std::shared_ptr &fromTile, const Position &pos, const ItemType &iType, const std::shared_ptr &item) { + Creature::onRemoveTileItem(fromTile, pos, iType, item); - uint64_t cost = 0; - for (const auto* itemClassification : g_game().getItemsClassifications()) { - if (itemClassification->id != firstForgingItem->getClassification()) { - continue; - } + if (tradeState != TRADE_TRANSFER) { + checkTradeState(item); - for (const auto &[mapTier, mapPrice] : itemClassification->tiers) { - if (mapTier == firstForgingItem->getTier() + 1) { - cost = mapPrice.convergenceFusionPrice; - break; - } + if (tradeItem) { + const auto &container = item->getContainer(); + if (container && container->isHoldingItem(tradeItem)) { + g_game().internalCloseTrade(static_self_cast()); } - break; - } - if (!g_game().removeMoney(static_self_cast(), cost, 0, true)) { - g_logger().error("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName()); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; - } - g_metrics().addCounter("balance_decrease", cost, { { "player", getName() }, { "context", "forge_convergence_fuse" } }); - history.cost = cost; - } else { - firstForgedItem->setTier(tier); - const auto &secondForgedItem = Item::CreateItem(secondItemId, 1); - if (!secondForgedItem) { - g_logger().error("[Log 4] Player with name {} failed to fuse item with id {}", getName(), secondItemId); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; } + } - secondForgedItem->setTier(tier); - returnValue = g_game().internalAddItem(exaltationContainer, secondForgedItem, INDEX_WHEREEVER); - if (returnValue != RETURNVALUE_NOERROR) { - g_logger().error("[Log 2] Failed to add forge item {} from player with name {}", secondItemId, getName()); - sendCancelMessage(getReturnMessage(returnValue)); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; + checkLootContainers(item->getContainer()); +} + +void Player::onCreatureAppear(const std::shared_ptr &creature, bool isLogin) { + Creature::onCreatureAppear(creature, isLogin); + + if (isLogin && creature == getPlayer()) { + onEquipInventory(); + + // Refresh bosstiary tracker onLogin + refreshCyclopediaMonsterTracker(true); + // Refresh bestiary tracker onLogin + refreshCyclopediaMonsterTracker(false); + + for (const auto &condition : storedConditionList) { + addCondition(condition); } + storedConditionList.clear(); - if (success) { - firstForgedItem->setTier(tier + 1); + updateRegeneration(); - if (bonus != 1) { - history.dustCost = dustCost; - setForgeDusts(getForgeDusts() - dustCost); - } - if (bonus != 2) { - if (coreCount != 0 && !removeItemCountById(ITEM_FORGE_CORE, coreCount)) { - g_logger().error("[{}][Log 1] Failed to remove item 'id :{} count: {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_CORE), coreCount, getName()); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; - } - history.coresCost = coreCount; - } - if (bonus != 3) { - uint64_t cost = 0; - for (const auto* itemClassification : g_game().getItemsClassifications()) { - if (itemClassification->id != firstForgedItem->getClassification()) { - continue; - } - if (!itemClassification->tiers.contains(firstForgedItem->getTier())) { - g_logger().error("[{}] Failed to find tier {} for item {} in classification {}", __FUNCTION__, firstForgedItem->getTier(), firstForgedItem->getClassification(), itemClassification->id); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - break; - } - cost = itemClassification->tiers.at(firstForgedItem->getTier()).regularPrice; - break; - } - if (!g_game().removeMoney(static_self_cast(), cost, 0, true)) { - g_logger().error("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName()); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; - } - g_metrics().addCounter("balance_decrease", cost, { { "player", getName() }, { "context", "forge_fuse" } }); - history.cost = cost; - } + const auto &bed = g_game().getBedBySleeper(guid); + if (bed) { + bed->wakeUp(static_self_cast()); + } - if (bonus == 4) { - if (tier > 0) { - secondForgedItem->setTier(tier - 1); - } - } else if (bonus == 6) { - secondForgedItem->setTier(tier + 1); - } else if (bonus == 7 && tier + 2 <= firstForgedItem->getClassification()) { - firstForgedItem->setTier(tier + 2); - } + auto version = client->oldProtocol ? getProtocolVersion() : CLIENT_VERSION; + g_logger().info("{} has logged in. (Protocol: {})", name, version); - if (bonus != 4 && bonus != 5 && bonus != 6 && bonus != 8) { - returnValue = g_game().internalRemoveItem(secondForgedItem, 1); - if (returnValue != RETURNVALUE_NOERROR) { - g_logger().error("[Log 6] Failed to remove forge item {} from player with name {}", secondItemId, getName()); - sendCancelMessage(getReturnMessage(returnValue)); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; - } - } - } else { - auto isTierLost = uniform_random(1, 100) <= (reduceTierLoss ? g_configManager().getNumber(FORGE_TIER_LOSS_REDUCTION) : 100); - if (isTierLost) { - if (secondForgedItem->getTier() >= 1) { - secondForgedItem->setTier(tier - 1); - } else { - returnValue = g_game().internalRemoveItem(secondForgedItem, 1); - if (returnValue != RETURNVALUE_NOERROR) { - g_logger().error("[Log 7] Failed to remove forge item {} from player with name {}", secondItemId, getName()); - sendCancelMessage(getReturnMessage(returnValue)); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; - } - } - } - bonus = (isTierLost ? 0 : 8); - history.coresCost = coreCount; + if (guild) { + guild->addMember(static_self_cast()); + } - if (getForgeDusts() < dustCost) { - g_logger().error("[Log 7] Failed to remove fuse dusts from player with name {}", getName()); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; - } else { - setForgeDusts(getForgeDusts() - dustCost); - } + int32_t offlineTime; + if (getLastLogout() != 0) { + // Not counting more than 21 days to prevent overflow when multiplying with 1000 (for milliseconds). + offlineTime = std::min(time(nullptr) - getLastLogout(), 86400 * 21); + } else { + offlineTime = 0; + } - if (coreCount != 0 && !removeItemCountById(ITEM_FORGE_CORE, coreCount)) { - g_logger().error("[{}][Log 2] Failed to remove item 'id: {}, count: {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_CORE), coreCount, getName()); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; + for (const auto &condition : getMuteConditions()) { + condition->setTicks(condition->getTicks() - (offlineTime * 1000)); + if (condition->getTicks() <= 0) { + removeCondition(condition); } + } - uint64_t cost = 0; - for (const auto* itemClassification : g_game().getItemsClassifications()) { - if (itemClassification->id != firstForgingItem->getClassification()) { - continue; - } - if (!itemClassification->tiers.contains(firstForgingItem->getTier() + 1)) { - g_logger().error("[{}] Failed to find tier {} for item {} in classification {}", __FUNCTION__, firstForgingItem->getTier() + 1, firstForgingItem->getClassification(), itemClassification->id); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - break; + g_game().checkPlayersRecord(); + if (getLevel() < g_configManager().getNumber(ADVENTURERSBLESSING_LEVEL) && getVocationId() > VOCATION_NONE) { + for (uint8_t i = 2; i <= 6; i++) { + if (!hasBlessing(i)) { + addBlessing(i, 1); } - cost = itemClassification->tiers.at(firstForgingItem->getTier() + 1).regularPrice; - break; - } - if (!g_game().removeMoney(static_self_cast(), cost, 0, true)) { - g_logger().error("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName()); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; } - g_metrics().addCounter("balance_decrease", cost, { { "player", getName() }, { "context", "forge_fuse" } }); + sendBlessStatus(); + } - history.cost = cost; + if (getCurrentMount() != 0) { + toggleMount(true); } - } - returnValue = g_game().internalAddItem(static_self_cast(), exaltationContainer, INDEX_WHEREEVER); - if (returnValue != RETURNVALUE_NOERROR) { - g_logger().error("Failed to add exaltation chest to player with name {}", fmt::underlying(ITEM_EXALTATION_CHEST), getName()); - sendCancelMessage(getReturnMessage(returnValue)); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; + g_game().changePlayerSpeed(static_self_cast(), 0); } +} - history.firstItemName = firstForgingItem->getName(); - history.secondItemName = secondForgingItem->getName(); - history.bonus = bonus; - history.createdAt = getTimeNow(); - history.convergence = convergence; - registerForgeHistoryDescription(history); +void Player::onRemoveCreature(const std::shared_ptr &creature, bool isLogout) { + Creature::onRemoveCreature(creature, isLogout); + + if (const auto &player = getPlayer(); player == creature) { + if (isLogout) { + onDeEquipInventory(); + + if (m_party) { + m_party->leaveParty(player, true); + } + if (guild) { + guild->removeMember(player); + } + + g_game().removePlayerUniqueLogin(player); + loginPosition = getPosition(); + lastLogout = time(nullptr); + g_logger().info("{} has logged out", getName()); + g_chat().removeUserFromAllChannels(player); + clearPartyInvitations(); + } - sendForgeResult(actionType, firstItemId, tier, secondItemId, tier + 1, success, bonus, coreCount, convergence); -} + if (eventWalk != 0) { + setFollowCreature(nullptr); + } -void Player::forgeTransferItemTier(ForgeAction_t actionType, uint16_t donorItemId, uint8_t tier, uint16_t receiveItemId, bool convergence) { - if (getFreeBackpackSlots() == 0) { - sendCancelMessage(RETURNVALUE_NOTENOUGHROOM); - return; - } + if (tradePartner) { + g_game().internalCloseTrade(player); + } - ForgeHistory history; - history.actionType = actionType; - history.tier = tier; - history.success = true; + closeShopWindow(); - const auto &donorItem = getForgeItemFromId(donorItemId, tier); - if (!donorItem) { - g_logger().error("[Log 1] Player with name {} failed to transfer item with id {}", getName(), donorItemId); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; - } - auto returnValue = g_game().internalRemoveItem(donorItem, 1); - if (returnValue != RETURNVALUE_NOERROR) { - g_logger().error("[Log 1] Failed to remove transfer item {} from player with name {}", donorItemId, getName()); - sendCancelMessage(getReturnMessage(returnValue)); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; + g_saveManager().savePlayer(player); } - const auto &receiveItem = getForgeItemFromId(receiveItemId, 0); - if (!receiveItem) { - g_logger().error("[Log 2] Player with name {} failed to transfer item with id {}", getName(), receiveItemId); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; - } - if (returnValue = g_game().internalRemoveItem(receiveItem, 1); - returnValue != RETURNVALUE_NOERROR) { - g_logger().error("[Log 2] Failed to remove transfer item {} from player with name {}", receiveItemId, getName()); - sendCancelMessage(getReturnMessage(returnValue)); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; + if (creature == shopOwner) { + setShopOwner(nullptr); + sendCloseShop(); } +} - const auto &exaltationChest = Item::CreateItem(ITEM_EXALTATION_CHEST, 1); - if (!exaltationChest) { - g_logger().error("Exaltation chest is nullptr"); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; - } - const auto &exaltationContainer = exaltationChest->getContainer(); - if (!exaltationContainer) { - g_logger().error("Exaltation container is nullptr"); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; - } +void Player::onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) { + Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport); - const auto &newReceiveItem = Item::CreateItem(receiveItemId, 1); - if (!newReceiveItem) { - g_logger().error("[Log 6] Player with name {} failed to fuse item with id {}", getName(), receiveItemId); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; + const auto &followCreature = getFollowCreature(); + if (hasFollowPath && (creature == followCreature || (creature.get() == this && followCreature))) { + isUpdatingPath = false; + g_game().updateCreatureWalk(getID()); // internally uses addEventWalk. } - auto configKey = convergence ? FORGE_CONVERGENCE_TRANSFER_DUST_COST : FORGE_TRANSFER_DUST_COST; - if (getForgeDusts() < g_configManager().getNumber(configKey)) { - g_logger().error("[Log 8] Failed to remove transfer dusts from player with name {}", getName()); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); + if (creature != getPlayer()) { return; - } else { - setForgeDusts(getForgeDusts() - g_configManager().getNumber(configKey)); } - setForgeDusts(getForgeDusts() - g_configManager().getNumber(configKey)); + if (tradeState != TRADE_TRANSFER) { + // check if we should close trade + if (tradeItem && !Position::areInRange<1, 1, 0>(tradeItem->getPosition(), getPosition())) { + g_game().internalCloseTrade(getPlayer()); + } - if (convergence) { - newReceiveItem->setTier(tier); - } else { - newReceiveItem->setTier(tier - 1); - } - returnValue = g_game().internalAddItem(exaltationContainer, newReceiveItem, INDEX_WHEREEVER); - if (returnValue != RETURNVALUE_NOERROR) { - g_logger().error("[Log 7] Failed to add forge item {} from player with name {}", receiveItemId, getName()); - sendCancelMessage(getReturnMessage(returnValue)); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; + if (tradePartner && !Position::areInRange<2, 2, 0>(tradePartner->getPosition(), getPosition())) { + g_game().internalCloseTrade(getPlayer()); + } } - uint8_t coresAmount = 0; - uint64_t cost = 0; - for (const auto &itemClassification : g_game().getItemsClassifications()) { - if (itemClassification->id != donorItem->getClassification()) { - continue; - } - if (!itemClassification->tiers.contains(donorItem->getTier())) { - g_logger().error("[{}] Failed to find tier {} for item {} in classification {}", __FUNCTION__, donorItem->getTier(), donorItem->getClassification(), itemClassification->id); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - break; + // close modal windows + if (!modalWindows.empty()) { + // TODO: This shouldn't be hardcoded + for (const uint32_t modalWindowId : modalWindows) { + if (modalWindowId == std::numeric_limits::max()) { + sendTextMessage(MESSAGE_EVENT_ADVANCE, "Offline training aborted."); + break; + } } - - const uint8_t toTier = convergence ? donorItem->getTier() : donorItem->getTier() - 1; - auto tierPriecs = itemClassification->tiers.at(toTier); - cost = convergence ? tierPriecs.convergenceTransferPrice : tierPriecs.regularPrice; - coresAmount = tierPriecs.corePrice; - break; + modalWindows.clear(); } - if (!removeItemCountById(ITEM_FORGE_CORE, coresAmount)) { - g_logger().error("[{}] Failed to remove item 'id: {}, count: {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_CORE), 1, getName()); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; + // leave market + if (inMarket) { + inMarket = false; } - if (!g_game().removeMoney(static_self_cast(), cost, 0, true)) { - g_logger().error("[{}] Failed to remove {} gold from player with name {}", __FUNCTION__, cost, getName()); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; + if (m_party) { + m_party->updateSharedExperience(); + m_party->updatePlayerStatus(getPlayer(), oldPos, newPos); } - history.cost = cost; - g_metrics().addCounter("balance_decrease", cost, { { "player", getName() }, { "context", "forge_transfer" } }); - returnValue = g_game().internalAddItem(static_self_cast(), exaltationContainer, INDEX_WHEREEVER); - if (returnValue != RETURNVALUE_NOERROR) { - g_logger().error("[Log 10] Failed to add forge item {} from player with name {}", fmt::underlying(ITEM_EXALTATION_CHEST), getName()); - sendCancelMessage(getReturnMessage(returnValue)); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; + if (teleport || oldPos.z != newPos.z) { + int32_t ticks = g_configManager().getNumber(STAIRHOP_DELAY); + if (ticks > 0) { + if (const auto &condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_PACIFIED, ticks, 0)) { + addCondition(condition); + } + } } - - history.firstItemName = Item::items[donorItemId].name; - history.secondItemName = newReceiveItem->getName(); - history.createdAt = getTimeNow(); - history.convergence = convergence; - registerForgeHistoryDescription(history); - - sendForgeResult(actionType, donorItemId, tier, receiveItemId, convergence ? tier : tier - 1, true, 0, 0, convergence); } -void Player::forgeResourceConversion(ForgeAction_t actionType) { - ForgeHistory history; - history.actionType = actionType; - history.success = true; - - ReturnValue returnValue = RETURNVALUE_NOERROR; - if (actionType == ForgeAction_t::DUSTTOSLIVERS) { - auto dusts = getForgeDusts(); - auto cost = static_cast(g_configManager().getNumber(FORGE_COST_ONE_SLIVER) * g_configManager().getNumber(FORGE_SLIVER_AMOUNT)); - if (cost > dusts) { - g_logger().error("[{}] Not enough dust", __FUNCTION__); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; - } - - auto itemCount = static_cast(g_configManager().getNumber(FORGE_SLIVER_AMOUNT)); - const auto &item = Item::CreateItem(ITEM_FORGE_SLIVER, itemCount); - returnValue = g_game().internalPlayerAddItem(static_self_cast(), item); - if (returnValue != RETURNVALUE_NOERROR) { - g_logger().error("Failed to add {} slivers to player with name {}", itemCount, getName()); - sendCancelMessage(getReturnMessage(returnValue)); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; - } - history.cost = cost; - history.gained = 3; - setForgeDusts(dusts - cost); - } else if (actionType == ForgeAction_t::SLIVERSTOCORES) { - const auto &[sliverCount, coreCount] = getForgeSliversAndCores(); - auto cost = static_cast(g_configManager().getNumber(FORGE_CORE_COST)); - if (cost > sliverCount) { - g_logger().error("[{}] Not enough sliver", __FUNCTION__); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; - } - - if (!removeItemCountById(ITEM_FORGE_SLIVER, cost)) { - g_logger().error("[{}] Failed to remove item 'id: {}, count {}' from player {}", __FUNCTION__, fmt::underlying(ITEM_FORGE_SLIVER), cost, getName()); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; +void Player::onEquipInventory() { + for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) { + const auto &item = inventory[slot]; + if (item) { + item->startDecaying(); + g_moveEvents().onPlayerEquip(getPlayer(), item, static_cast(slot), false); } + } +} - if (const auto &item = Item::CreateItem(ITEM_FORGE_CORE, 1); - item) { - returnValue = g_game().internalPlayerAddItem(static_self_cast(), item); - } - if (returnValue != RETURNVALUE_NOERROR) { - g_logger().error("Failed to add one core to player with name {}", getName()); - sendCancelMessage(getReturnMessage(returnValue)); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; +void Player::onDeEquipInventory() { + for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) { + const auto &item = inventory[slot]; + if (item) { + g_moveEvents().onPlayerDeEquip(getPlayer(), item, static_cast(slot)); } + } +} - history.cost = cost; - history.gained = 1; - } else { - auto dustLevel = getForgeDustLevel(); - if (dustLevel >= g_configManager().getNumber(FORGE_MAX_DUST)) { - g_logger().error("[{}] Maximum level reached", __FUNCTION__); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; - } +void Player::onAttackedCreatureDisappear(bool isLogout) { + sendCancelTarget(); - const auto upgradeCost = dustLevel - 75; - if (const auto dusts = getForgeDusts(); - upgradeCost > dusts) { - g_logger().error("[{}] Not enough dust", __FUNCTION__); - sendForgeError(RETURNVALUE_CONTACTADMINISTRATOR); - return; - } + if (!isLogout) { + sendTextMessage(MESSAGE_FAILURE, "Target lost."); + } +} - history.cost = upgradeCost; - history.gained = dustLevel; - removeForgeDusts(upgradeCost); - addForgeDustLevel(1); +void Player::onFollowCreatureDisappear(bool isLogout) { + sendCancelTarget(); + + if (!isLogout) { + sendTextMessage(MESSAGE_FAILURE, "Target lost."); } +} - history.createdAt = getTimeNow(); - registerForgeHistoryDescription(history); - sendForgingData(); +// container +// container + +void Player::onAddContainerItem(const std::shared_ptr &item) { + checkTradeState(item); } -void Player::forgeHistory(uint8_t page) const { - sendForgeHistory(page); +void Player::onUpdateContainerItem(const std::shared_ptr &container, const std::shared_ptr &oldItem, const std::shared_ptr &newItem) { + if (oldItem != newItem) { + onRemoveContainerItem(container, oldItem); + } + + if (tradeState != TRADE_TRANSFER) { + checkTradeState(oldItem); + } } -void Player::registerForgeHistoryDescription(ForgeHistory history) { - std::string successfulString = history.success ? "Successful" : "Unsuccessful"; - std::string historyTierString = history.tier > 0 ? "tier - 1" : "consumed"; - std::string price = history.bonus != 3 ? formatPrice(std::to_string(history.cost), true) : "0"; - std::stringstream detailsResponse; - auto itemId = Item::items.getItemIdByName(history.firstItemName); - const ItemType &itemType = Item::items[itemId]; - if (history.actionType == ForgeAction_t::FUSION) { - if (history.success) { - detailsResponse << fmt::format( - "{:s}{:s}

" - "Fusion partners:" - "
    " - "
  • " - "First item: {:s} {:s}, tier {:s}" - "
  • " - "
  • " - "Second item: {:s} {:s}, tier {:s}" - "
  • " - "
" - "
" - "Result:" - "
    " - "
  • " - "First item: tier + 1" - "
  • " - "
  • " - "Second item: {:s}" - "
  • " - "
" - "
" - "Invested:" - "
    " - "
  • " - "{:d} cores" - "
  • " - "
  • " - "{:d} dust" - "
  • " - "
  • " - "{:s} gold" - "
  • " - "
", - successfulString, - history.convergence ? " (convergence)" : "", - itemType.article, itemType.name, std::to_string(history.tier), - itemType.article, itemType.name, std::to_string(history.tier), - history.bonus == 8 ? "unchanged" : "consumed", - history.coresCost, history.dustCost, price - ); - } else { - detailsResponse << fmt::format( - "{:s}{:s}

" - "Fusion partners:" - "
    " - "
  • " - "First item: {:s} {:s}, tier {:s}" - "
  • " - "
  • " - "Second item: {:s} {:s}, tier {:s}" - "
  • " - "
" - "
" - "Result:" - "
    " - "
  • " - "First item: unchanged" - "
  • " - "
  • " - "Second item: {:s}" - "
  • " - "
" - "
" - "Invested:" - "
    " - "
  • " - "{:d} cores" - "
  • " - "
  • " - "100 dust" - "
  • " - "
  • " - "{:s} gold" - "
  • " - "
", - successfulString, - history.convergence ? " (convergence)" : "", - itemType.article, itemType.name, std::to_string(history.tier), - itemType.article, itemType.name, std::to_string(history.tier), - history.bonus == 8 ? "unchanged" : historyTierString, - history.coresCost, price - ); +void Player::onRemoveContainerItem(const std::shared_ptr &container, const std::shared_ptr &item) { + if (tradeState != TRADE_TRANSFER) { + checkTradeState(item); + + if (tradeItem) { + if (tradeItem->getParent() != container && container->isHoldingItem(tradeItem)) { + g_game().internalCloseTrade(static_self_cast()); + } } - } else if (history.actionType == ForgeAction_t::TRANSFER) { - detailsResponse << fmt::format( - "{:s}{:s}

" - "Transfer partners:" - "
    " - "
  • " - "First item: {:s} {:s}, tier {:s}" - "
  • " - "
  • " - "Second item: {:s} {:s}, tier {:s}" - "
  • " - "
" - "
" - "Result:" - "
    " - "
  • " - "First item: {:s} {:s}, tier {:s}" - "
  • " - "
  • " - "Second item: {:s} {:s}, {:s}" - "
  • " - "
" - "
" - "Invested:" - "
    " - "
  • " - "1 cores" - "
  • " - "
  • " - "100 dust" - "
  • " - "
  • " - "{:s} gold" - "
  • " - "
", - successfulString, - history.convergence ? " (convergence)" : "", - itemType.article, itemType.name, std::to_string(history.tier), - itemType.article, itemType.name, std::to_string(history.tier), - itemType.article, itemType.name, std::to_string(history.tier), - itemType.article, itemType.name, std::to_string(history.tier), - price - ); - } else if (history.actionType == ForgeAction_t::DUSTTOSLIVERS) { - detailsResponse << fmt::format("Converted {:d} dust to {:d} slivers.", history.cost, history.gained); - } else if (history.actionType == ForgeAction_t::SLIVERSTOCORES) { - history.actionType = ForgeAction_t::DUSTTOSLIVERS; - detailsResponse << fmt::format("Converted {:d} slivers to {:d} exalted core.", history.cost, history.gained); - } else if (history.actionType == ForgeAction_t::INCREASELIMIT) { - history.actionType = ForgeAction_t::DUSTTOSLIVERS; - detailsResponse << fmt::format("Spent {:d} dust to increase the dust limit to {:d}.", history.cost, history.gained + 1); - } else { - detailsResponse << "(unknown)"; } - history.description = detailsResponse.str(); - - setForgeHistory(history); + checkLootContainers(item->getContainer()); } -void Player::closeAllExternalContainers() { - if (openContainers.empty()) { +void Player::onCloseContainer(const std::shared_ptr &container) { + if (!client) { return; } - std::vector> containerToClose; for (const auto &[containerId, containerInfo] : openContainers) { - const auto &container = containerInfo.container; - if (!container) { - continue; + if (containerInfo.container == container) { + client->sendCloseContainer(containerId); } + } +} - if (container->getHoldingPlayer() != getPlayer()) { - containerToClose.emplace_back(container); - } +void Player::onSendContainer(const std::shared_ptr &container) { + if (!client || !container) { + return; } - for (const auto &container : containerToClose) { - autoCloseContainers(container); + const bool hasParent = container->hasParent(); + for (const auto &[containerId, containerInfo] : openContainers) { + if (containerInfo.container == container) { + client->sendContainer(containerId, container, hasParent, containerInfo.index); + } } } -SoundEffect_t Player::getHitSoundEffect() const { - // Distance sound effects - const auto &tool = getWeapon(); - if (tool == nullptr) { - return SoundEffect_t::SILENCE; - } +// close container and its child containers - switch (const auto &it = Item::items[tool->getID()]; it.weaponType) { - case WEAPON_AMMO: { - if (it.ammoType == AMMO_BOLT) { - return SoundEffect_t::DIST_ATK_CROSSBOW_SHOT; - } - if (it.ammoType == AMMO_ARROW) { - if (it.shootType == CONST_ANI_BURSTARROW) { - return SoundEffect_t::BURST_ARROW_EFFECT; - } - if (it.shootType == CONST_ANI_DIAMONDARROW) { - return SoundEffect_t::DIAMOND_ARROW_EFFECT; - } - } else { - return SoundEffect_t::DIST_ATK_THROW_SHOT; - } - } - case WEAPON_DISTANCE: { - if (tool->getAmmoType() == AMMO_BOLT) { - return SoundEffect_t::DIST_ATK_CROSSBOW_SHOT; - } - if (tool->getAmmoType() == AMMO_ARROW) { - return SoundEffect_t::DIST_ATK_BOW_SHOT; +void Player::autoCloseContainers(const std::shared_ptr &container) { + std::vector closeList; + for (const auto &[containerId, containerInfo] : openContainers) { + auto tmpContainer = containerInfo.container; + while (tmpContainer) { + if (tmpContainer->isRemoved() || tmpContainer == container) { + closeList.emplace_back(containerId); + break; } - return SoundEffect_t::DIST_ATK_THROW_SHOT; - } - case WEAPON_WAND: { - // Separate between wand and rod here - // return SoundEffect_t::DIST_ATK_ROD_SHOT; - return SoundEffect_t::DIST_ATK_WAND_SHOT; + + tmpContainer = std::dynamic_pointer_cast(tmpContainer->getParent()); } - default: { - return SoundEffect_t::SILENCE; + } + + for (const uint32_t containerId : closeList) { + closeContainer(containerId); + if (client) { + client->sendCloseContainer(containerId); } - } // switch - - return SoundEffect_t::SILENCE; + } } -SoundEffect_t Player::getAttackSoundEffect() const { - const auto &tool = getWeapon(); - if (tool == nullptr) { - return SoundEffect_t::HUMAN_CLOSE_ATK_FIST; +// inventory +// inventory + +void Player::onUpdateInventoryItem(const std::shared_ptr &oldItem, const std::shared_ptr &newItem) { + if (oldItem != newItem) { + onRemoveInventoryItem(oldItem); } - const ItemType &it = Item::items[tool->getID()]; - if (it.weaponType == WEAPON_NONE || it.weaponType == WEAPON_SHIELD) { - return SoundEffect_t::HUMAN_CLOSE_ATK_FIST; + if (tradeState != TRADE_TRANSFER) { + checkTradeState(oldItem); } +} - switch (it.weaponType) { - case WEAPON_AXE: { - return SoundEffect_t::MELEE_ATK_AXE; - } - case WEAPON_SWORD: { - return SoundEffect_t::MELEE_ATK_SWORD; - } - case WEAPON_CLUB: { - return SoundEffect_t::MELEE_ATK_CLUB; - } - case WEAPON_AMMO: - case WEAPON_DISTANCE: { - if (tool->getAmmoType() == AMMO_BOLT) { - return SoundEffect_t::DIST_ATK_CROSSBOW; - } - if (tool->getAmmoType() == AMMO_ARROW) { - return SoundEffect_t::DIST_ATK_BOW; - } - return SoundEffect_t::DIST_ATK_THROW; +void Player::onRemoveInventoryItem(const std::shared_ptr &item) { + if (tradeState != TRADE_TRANSFER) { + checkTradeState(item); - break; - } - case WEAPON_WAND: { - return SoundEffect_t::MAGICAL_RANGE_ATK; - } - default: { - return SoundEffect_t::SILENCE; + if (tradeItem) { + const auto &container = item->getContainer(); + if (container && container->isHoldingItem(tradeItem)) { + g_game().internalCloseTrade(static_self_cast()); + } } } - return SoundEffect_t::SILENCE; + checkLootContainers(item->getContainer()); +} + +uint64_t Player::getItemCustomPrice(uint16_t itemId, bool buyPrice) const { + auto it = itemPriceMap.find(itemId); + if (it != itemPriceMap.end()) { + return it->second; + } + + const std::map itemMap { { itemId, 1 } }; + return g_game().getItemMarketPrice(itemMap, buyPrice); +} + +uint16_t Player::getFreeBackpackSlots() const { + const auto &thing = getThing(CONST_SLOT_BACKPACK); + if (!thing) { + return 0; + } + + const auto &backpack = thing->getContainer(); + if (!backpack) { + return 0; + } + + const uint16_t counter = std::max(0, backpack->getFreeSlots()); + + return counter; } -bool Player::canAutoWalk(const Position &toPosition, const std::function &function, uint32_t delay /* = 500*/) { +bool Player::canAutoWalk(const Position &toPosition, const std::function &function, uint32_t delay) { if (!Position::areInRange<1, 1>(getPosition(), toPosition)) { // Check if can walk to the toPosition and send event to use function std::vector listDir; @@ -7921,7 +10053,15 @@ bool Player::canAutoWalk(const Position &toPosition, const std::function return false; } +void Player::sendMessageDialog(const std::string &message) const { + if (client) { + client->sendMessageDialog(message); + } +} + // Account +// Account + bool Player::setAccount(uint32_t accountId) { if (account) { g_logger().warn("Account was already set!"); @@ -7944,132 +10084,108 @@ std::shared_ptr Player::getAccount() const { return account; } -/******************************************************************************* - * Hazard system - ******************************************************************************/ +// Prey system -void Player::setHazardSystemPoints(int32_t count) { - if (!g_configManager().getBoolean(TOGGLE_HAZARDSYSTEM)) { - return; - } - addStorageValue(STORAGEVALUE_HAZARDCOUNT, std::max(0, std::min(0xFFFF, count)), true); - reloadHazardSystemPointsCounter = true; - if (count > 0) { - setIcon("hazard", CreatureIcon(CreatureIconQuests_t::Hazard, count)); - } else { - removeIcon("hazard"); +void Player::initializePrey() { + if (preys.empty()) { + for (uint8_t slotId = PreySlot_First; slotId <= PreySlot_Last; slotId++) { + auto slot = std::make_unique(static_cast(slotId)); + if (!g_configManager().getBoolean(PREY_ENABLED)) { + slot->state = PreyDataState_Inactive; + } else if (slot->id == PreySlot_Three && !g_configManager().getBoolean(PREY_FREE_THIRD_SLOT)) { + slot->state = PreyDataState_Locked; + } else if (slot->id == PreySlot_Two && !isPremium()) { + slot->state = PreyDataState_Locked; + } else { + slot->state = PreyDataState_Selection; + slot->reloadMonsterGrid(getPreyBlackList(), getLevel()); + } + + setPreySlotClass(slot); + } } } -void Player::parseAttackRecvHazardSystem(CombatDamage &damage, const std::shared_ptr &monster) { - if (!monster || !monster->getHazard()) { - return; - } +void Player::removePreySlotById(PreySlot_t slotid) { + const auto it = std::ranges::remove_if(preys, [slotid](const auto &preyIt) { + return preyIt->id == slotid; + }).begin(); - if (!g_configManager().getBoolean(TOGGLE_HAZARDSYSTEM)) { - return; - } + preys.erase(it, preys.end()); +} - if (damage.primary.type == COMBAT_HEALING) { - return; - } +/******************************************************************************* + * Hazard system + ******************************************************************************/ - auto points = getHazardSystemPoints(); - if (m_party) { - for (const auto &partyMember : m_party->getMembers()) { - if (partyMember && partyMember->getHazardSystemPoints() < points) { - points = partyMember->getHazardSystemPoints(); - } - } +void Player::setBossPoints(uint32_t amount) { + bossPoints = amount; +} - if (m_party->getLeader() && m_party->getLeader()->getHazardSystemPoints() < points) { - points = m_party->getLeader()->getHazardSystemPoints(); - } - } +void Player::addBossPoints(uint32_t amount) { + bossPoints += amount; +} - if (points == 0) { - return; - } +void Player::removeBossPoints(uint32_t amount) { + bossPoints = std::max(0, bossPoints - amount); +} - uint16_t stage = 0; - auto chance = static_cast(normal_random(1, 10000)); - auto critChance = g_configManager().getNumber(HAZARD_CRITICAL_CHANCE); - // Critical chance - if (monster->getHazardSystemCrit() && (lastHazardSystemCriticalHit + g_configManager().getNumber(HAZARD_CRITICAL_INTERVAL)) <= OTSYS_TIME() && chance <= critChance && !damage.critical) { - damage.critical = true; - damage.extension = true; - damage.exString = "(Hazard)"; +uint32_t Player::getBossPoints() const { + return bossPoints; +} - stage = (points - 1) * static_cast(g_configManager().getNumber(HAZARD_CRITICAL_MULTIPLIER)); - damage.primary.value += static_cast(std::ceil((static_cast(damage.primary.value) * (5000 + stage)) / 10000)); - damage.secondary.value += static_cast(std::ceil((static_cast(damage.secondary.value) * (5000 + stage)) / 10000)); - lastHazardSystemCriticalHit = OTSYS_TIME(); +void Player::sendBosstiaryCooldownTimer() const { + if (client) { + client->sendBosstiaryCooldownTimer(); } +} - // To prevent from punish the player twice with critical + damage boost, just uncomment code from the if - if (monster->getHazardSystemDamageBoost() /* && !damage.critical*/) { - stage = points * static_cast(g_configManager().getNumber(HAZARD_DAMAGE_MULTIPLIER)); - if (stage != 0) { - damage.extension = true; - damage.exString = "(Hazard)"; - damage.primary.value += static_cast(std::ceil((static_cast(damage.primary.value) * stage) / 10000)); - if (damage.secondary.value != 0) { - damage.secondary.value += static_cast(std::ceil((static_cast(damage.secondary.value) * stage) / 10000)); - } - } +void Player::setSlotBossId(uint8_t slotId, uint32_t bossId) { + if (slotId == 1) { + bossIdSlotOne = bossId; + } else { + bossIdSlotTwo = bossId; + } + if (client) { + client->parseSendBosstiarySlots(); } } -void Player::parseAttackDealtHazardSystem(CombatDamage &damage, const std::shared_ptr &monster) const { - if (!g_configManager().getBoolean(TOGGLE_HAZARDSYSTEM)) { - return; +uint32_t Player::getSlotBossId(uint8_t slotId) const { + if (slotId == 1) { + return bossIdSlotOne; + } else { + return bossIdSlotTwo; } +} - if (!monster || !monster->getHazard()) { - return; - } +void Player::addRemoveTime() { + bossRemoveTimes = bossRemoveTimes + 1; +} - if (damage.primary.type == COMBAT_HEALING) { - return; - } +void Player::setRemoveBossTime(uint8_t newRemoveTimes) { + bossRemoveTimes = newRemoveTimes; +} - auto points = getHazardSystemPoints(); - if (m_party) { - for (const auto &partyMember : m_party->getMembers()) { - if (partyMember && partyMember->getHazardSystemPoints() < points) { - points = partyMember->getHazardSystemPoints(); - } - } +uint8_t Player::getRemoveTimes() const { + return bossRemoveTimes; +} - if (m_party->getLeader() && m_party->getLeader()->getHazardSystemPoints() < points) { - points = m_party->getLeader()->getHazardSystemPoints(); - } +void Player::sendMonsterPodiumWindow(const std::shared_ptr &podium, const Position &position, uint16_t itemId, uint8_t stackpos) const { + if (client) { + client->sendMonsterPodiumWindow(podium, position, itemId, stackpos); } +} - if (points == 0) { - return; +void Player::sendBosstiaryEntryChanged(uint32_t bossid) const { + if (client) { + client->sendBosstiaryEntryChanged(bossid); } +} - // Dodge chance - uint16_t stage; - if (monster->getHazardSystemDodge()) { - stage = points * g_configManager().getNumber(HAZARD_DODGE_MULTIPLIER); - auto chance = static_cast(normal_random(1, 10000)); - if (chance <= stage) { - damage.primary.value = 0; - damage.secondary.value = 0; - return; - } - } - if (monster->getHazardSystemDefenseBoost()) { - stage = points * static_cast(g_configManager().getNumber(HAZARD_DEFENSE_MULTIPLIER)); - if (stage != 0) { - damage.exString = fmt::format("(hazard -{}%)", stage / 100.); - damage.primary.value -= static_cast(std::ceil((static_cast(damage.primary.value) * stage) / 10000)); - if (damage.secondary.value != 0) { - damage.secondary.value -= static_cast(std::ceil((static_cast(damage.secondary.value) * stage) / 10000)); - } - } +void Player::sendInventoryImbuements(const std::map> &items) const { + if (client) { + client->sendInventoryImbuements(items); } } @@ -8218,6 +10334,50 @@ uint16_t Player::getDodgeChance() const { return chance; } +uint8_t Player::isRandomMounted() const { + return randomMount; +} + +void Player::setRandomMount(uint8_t isMountRandomized) { + randomMount = isMountRandomized; +} + +void Player::sendFYIBox(const std::string &message) const { + if (client) { + client->sendFYIBox(message); + } +} + +void Player::BestiarysendCharms() const { + if (client) { + client->BestiarysendCharms(); + } +} + +void Player::addBestiaryKillCount(uint16_t raceid, uint32_t amount) { + const uint32_t oldCount = getBestiaryKillCount(raceid); + const uint32_t key = STORAGEVALUE_BESTIARYKILLCOUNT + raceid; + addStorageValue(key, static_cast(oldCount + amount), true); +} + +uint32_t Player::getBestiaryKillCount(uint16_t raceid) const { + const uint32_t key = STORAGEVALUE_BESTIARYKILLCOUNT + raceid; + const auto value = getStorageValue(key); + return value > 0 ? static_cast(value) : 0; +} + +void Player::setGUID(uint32_t newGuid) { + this->guid = newGuid; +} + +uint32_t Player::getGUID() const { + return guid; +} + +bool Player::canSeeInvisibility() const { + return hasFlag(PlayerFlags_t::CanSenseInvisibility) || group->access; +} + void Player::checkAndShowBlessingMessage() { auto adventurerBlessingLevel = g_configManager().getNumber(ADVENTURERSBLESSING_LEVEL); auto willNotLoseBless = getLevel() < adventurerBlessingLevel && getVocationId() > VOCATION_NONE; diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 8be3d7c7a99..03f248cfa2f 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -9,36 +9,13 @@ #pragma once -#include "items/containers/container.hpp" #include "creatures/creature.hpp" -#include "items/cylinder.hpp" -#include "declarations.hpp" -#include "items/containers/depot/depotchest.hpp" -#include "items/containers/depot/depotlocker.hpp" -#include "grouping/familiars.hpp" #include "enums/forge_conversion.hpp" -#include "grouping/groups.hpp" -#include "grouping/guild.hpp" -#include "imbuements/imbuements.hpp" -#include "items/containers/inbox/inbox.hpp" -#include "io/ioguild.hpp" -#include "io/ioprey.hpp" -#include "creatures/appearance/mounts/mounts.hpp" -#include "creatures/appearance/outfit/outfit.hpp" -#include "grouping/party.hpp" -#include "server/network/protocol/protocolgame.hpp" -#include "items/containers/rewards/reward.hpp" -#include "items/containers/rewards/rewardchest.hpp" -#include "vocations/vocation.hpp" -#include "creatures/npcs/npc.hpp" #include "game/bank/bank.hpp" -#include "enums/object_category.hpp" -#include "enums/player_cyclopedia.hpp" -#include "enums/player_icons.hpp" -#include "creatures/players/cyclopedia/player_badge.hpp" -#include "creatures/players/cyclopedia/player_cyclopedia.hpp" -#include "creatures/players/cyclopedia/player_title.hpp" -#include "creatures/players/vip/player_vip.hpp" +#include "grouping/guild.hpp" +#include "items/cylinder.hpp" +#include "game/movement/position.hpp" +#include "creatures/creatures_definitions.hpp" class House; class NetworkMessage; @@ -59,12 +36,45 @@ class PlayerTitle; class PlayerVIP; class Spectators; class Account; +class RewardChest; +class Cylinder; class Town; +class Reward; +class DepotChest; +class DepotLocker; +class Inbox; +class Vocation; +class Container; +class KV; +class BedItem; +class Npc; struct ModalWindow; struct Achievement; -struct Badge; -struct Title; +struct VIPGroup; +struct Mount; +struct OutfitEntry; +struct Outfit; +struct FamiliarEntry; +struct Familiar; +struct Group; +struct Outfit_t; +struct TextMessage; +struct HighscoreCharacter; + +enum class PlayerIcon : uint8_t; +enum class IconBakragore : uint8_t; +enum ObjectCategory_t : uint8_t; +enum PreySlot_t : uint8_t; +enum SpeakClasses : uint8_t; +enum ChannelEvent_t : uint8_t; +enum SquareColor_t : uint8_t; + +using GuildWarVector = std::vector; +using StashContainerList = std::vector, uint32_t>>; +using ItemVector = std::vector>; +using UsersMap = std::map>; +using InvitedMap = std::map>; struct ForgeHistory { ForgeAction_t actionType = ForgeAction_t::FUSION; @@ -121,7 +131,7 @@ class Player final : public Creature, public Cylinder, public Bankable { const std::shared_ptr &player; }; - explicit Player(ProtocolGame_ptr p); + explicit Player(std::shared_ptr p); ~Player() override; // non-copyable @@ -184,133 +194,63 @@ class Player final : public Creature, public Cylinder, public Bankable { void dismount(); uint16_t getDodgeChance() const; - uint8_t isRandomMounted() const { - return randomMount; - } - void setRandomMount(uint8_t isMountRandomized) { - randomMount = isMountRandomized; - } + uint8_t isRandomMounted() const; + void setRandomMount(uint8_t isMountRandomized); - void sendFYIBox(const std::string &message) const { - if (client) { - client->sendFYIBox(message); - } - } + void sendFYIBox(const std::string &message) const; - void BestiarysendCharms() const { - if (client) { - client->BestiarysendCharms(); - } - } - void addBestiaryKillCount(uint16_t raceid, uint32_t amount) { - const uint32_t oldCount = getBestiaryKillCount(raceid); - const uint32_t key = STORAGEVALUE_BESTIARYKILLCOUNT + raceid; - addStorageValue(key, static_cast(oldCount + amount), true); - } - uint32_t getBestiaryKillCount(uint16_t raceid) const { - const uint32_t key = STORAGEVALUE_BESTIARYKILLCOUNT + raceid; - const auto value = getStorageValue(key); - return value > 0 ? static_cast(value) : 0; - } + void BestiarysendCharms() const; + void addBestiaryKillCount(uint16_t raceid, uint32_t amount); + uint32_t getBestiaryKillCount(uint16_t raceid) const; - void setGUID(uint32_t newGuid) { - this->guid = newGuid; - } - uint32_t getGUID() const { - return guid; - } - bool canSeeInvisibility() const override { - return hasFlag(PlayerFlags_t::CanSenseInvisibility) || group->access; - } + void setGUID(uint32_t newGuid); + uint32_t getGUID() const; + bool canSeeInvisibility() const override; - void setDailyReward(uint8_t reward) { - this->isDailyReward = reward; - } + void setDailyReward(uint8_t reward); void removeList() override; void addList() override; void removePlayer(bool displayEffect, bool forced = true); - static uint64_t getExpForLevel(const uint32_t level) { - return (((level - 6ULL) * level + 17ULL) * level - 12ULL) / 6ULL * 100ULL; - } + static uint64_t getExpForLevel(const uint32_t level); - uint16_t getStaminaMinutes() const { - return staminaMinutes; - } + uint16_t getStaminaMinutes() const; - void sendItemsPrice() const { - if (client) { - client->sendItemsPrice(); - } - } + void sendItemsPrice() const; - void sendForgingData() const { - if (client) { - client->sendForgingData(); - } - } + void sendForgingData() const; bool addOfflineTrainingTries(skills_t skill, uint64_t tries); - void addOfflineTrainingTime(int32_t addTime) { - offlineTrainingTime = std::min(12 * 3600 * 1000, offlineTrainingTime + addTime); - } - void removeOfflineTrainingTime(int32_t removeTime) { - offlineTrainingTime = std::max(0, offlineTrainingTime - removeTime); - } - int32_t getOfflineTrainingTime() const { - return offlineTrainingTime; - } + void addOfflineTrainingTime(int32_t addTime); + void removeOfflineTrainingTime(int32_t removeTime); + int32_t getOfflineTrainingTime() const; - int8_t getOfflineTrainingSkill() const { - return offlineTrainingSkill; - } - void setOfflineTrainingSkill(int8_t skill) { - offlineTrainingSkill = skill; - } + int8_t getOfflineTrainingSkill() const; + void setOfflineTrainingSkill(int8_t skill); - uint64_t getBankBalance() const override { - return bankBalance; - } - void setBankBalance(uint64_t balance) override { - bankBalance = balance; - } + uint64_t getBankBalance() const override; + void setBankBalance(uint64_t balance) override; - [[nodiscard]] std::shared_ptr getGuild() const { - return guild; - } + [[nodiscard]] std::shared_ptr getGuild() const; void setGuild(const std::shared_ptr &guild); - [[nodiscard]] GuildRank_ptr getGuildRank() const { - return guildRank; - } - void setGuildRank(GuildRank_ptr newGuildRank) { - guildRank = std::move(newGuildRank); - } + [[nodiscard]] GuildRank_ptr getGuildRank() const; + void setGuildRank(GuildRank_ptr newGuildRank); bool isGuildMate(const std::shared_ptr &player) const; - [[nodiscard]] const std::string &getGuildNick() const { - return guildNick; - } - void setGuildNick(std::string nick) { - guildNick = std::move(nick); - } + [[nodiscard]] const std::string &getGuildNick() const; + void setGuildNick(std::string nick); bool isInWar(const std::shared_ptr &player) const; bool isInWarList(uint32_t guild_id) const; - void setLastWalkthroughAttempt(int64_t walkthroughAttempt) { - lastWalkthroughAttempt = walkthroughAttempt; - } - void setLastWalkthroughPosition(Position walkthroughPosition) { - lastWalkthroughPosition = walkthroughPosition; - } + void setLastWalkthroughAttempt(int64_t walkthroughAttempt); + void setLastWalkthroughPosition(Position walkthroughPosition); - std::shared_ptr getInbox() const { - return inbox; - } + std::shared_ptr getInbox() const; std::unordered_set getClientIcons(); @@ -318,102 +258,55 @@ class Player final : public Creature, public Cylinder, public Bankable { return guildWarVector; } - const std::unordered_set> &getCyclopediaMonsterTrackerSet(bool isBoss) const { - return isBoss ? m_bosstiaryMonsterTracker : m_bestiaryMonsterTracker; - } + const std::unordered_set> &getCyclopediaMonsterTrackerSet(bool isBoss) const; void addMonsterToCyclopediaTrackerList(const std::shared_ptr &mtype, bool isBoss, bool reloadClient = false); void removeMonsterFromCyclopediaTrackerList(const std::shared_ptr &mtype, bool isBoss, bool reloadClient = false); - void sendBestiaryEntryChanged(uint16_t raceid) const { - if (client) { - client->sendBestiaryEntryChanged(raceid); - } - } + void sendBestiaryEntryChanged(uint16_t raceid) const; void refreshCyclopediaMonsterTracker(bool isBoss = false) const { refreshCyclopediaMonsterTracker(getCyclopediaMonsterTrackerSet(isBoss), isBoss); } - void refreshCyclopediaMonsterTracker(const std::unordered_set> &trackerList, bool isBoss) const { - if (client) { - client->refreshCyclopediaMonsterTracker(trackerList, isBoss); - } - } + void refreshCyclopediaMonsterTracker(const std::unordered_set> &trackerList, bool isBoss) const; bool isBossOnBosstiaryTracker(const std::shared_ptr &monsterType) const; - std::shared_ptr getVocation() const { - return vocation; - } - - OperatingSystem_t getOperatingSystem() const { - return operatingSystem; - } - void setOperatingSystem(OperatingSystem_t clientos) { - operatingSystem = clientos; - } + std::shared_ptr getVocation() const; - bool isOldProtocol() const { - return client && client->oldProtocol; - } + OperatingSystem_t getOperatingSystem() const; + void setOperatingSystem(OperatingSystem_t clientos); - uint32_t getProtocolVersion() const { - if (!client) { - return 0; - } + bool isOldProtocol() const; - return client->getVersion(); - } + uint32_t getProtocolVersion() const; - bool hasSecureMode() const { - return secureMode; - } + bool hasSecureMode() const; - void setParty(std::shared_ptr newParty) { - m_party = std::move(newParty); - } - std::shared_ptr getParty() const { - return m_party; - } + void setParty(std::shared_ptr newParty); + std::shared_ptr getParty() const; int32_t getCleavePercent(bool useCharges = false) const; - void setCleavePercent(int32_t value) { - cleavePercent = std::max(0, cleavePercent + value); - } + void setCleavePercent(int32_t value); int32_t getPerfectShotDamage(uint8_t range, bool useCharges = false) const; - void setPerfectShotDamage(uint8_t range, int32_t damage) { - int32_t actualDamage = getPerfectShotDamage(range); - const bool aboveZero = (actualDamage != 0); - actualDamage += damage; - if (actualDamage == 0 && aboveZero) { - perfectShot.erase(range); - } else { - perfectShot[range] = actualDamage; - } - } + void setPerfectShotDamage(uint8_t range, int32_t damage); int32_t getSpecializedMagicLevel(CombatType_t combat, bool useCharges = false) const; - void setSpecializedMagicLevel(CombatType_t combat, int32_t value) { - specializedMagicLevel[combatTypeToIndex(combat)] = std::max(0, specializedMagicLevel[combatTypeToIndex(combat)] + value); - } + void setSpecializedMagicLevel(CombatType_t combat, int32_t value); int32_t getMagicShieldCapacityFlat(bool useCharges = false) const; - void setMagicShieldCapacityFlat(int32_t value) { - magicShieldCapacityFlat += value; - } + void setMagicShieldCapacityFlat(int32_t value); int32_t getMagicShieldCapacityPercent(bool useCharges = false) const; - void setMagicShieldCapacityPercent(int32_t value) { - magicShieldCapacityPercent += value; - } + void setMagicShieldCapacityPercent(int32_t value); double_t getReflectPercent(CombatType_t combat, bool useCharges = false) const override; @@ -431,78 +324,33 @@ class Player final : public Creature, public Cylinder, public Bankable { GuildEmblems_t getGuildEmblem(const std::shared_ptr &player) const; - uint64_t getSpentMana() const { - return manaSpent; - } + uint64_t getSpentMana() const; - bool hasFlag(PlayerFlags_t flag) const { - return group->flags[static_cast(flag)]; - } + bool hasFlag(PlayerFlags_t flag) const; - void setFlag(PlayerFlags_t flag) const { - group->flags[static_cast(flag)] = true; - } + void setFlag(PlayerFlags_t flag) const; - void removeFlag(PlayerFlags_t flag) const { - group->flags[static_cast(flag)] = false; - } + void removeFlag(PlayerFlags_t flag) const; - std::shared_ptr getBedItem() { - return bedItem; - } - void setBedItem(std::shared_ptr b) { - bedItem = std::move(b); - } + std::shared_ptr getBedItem(); + void setBedItem(std::shared_ptr b); - bool hasImbuingItem() const { - return imbuingItem != nullptr; - } + bool hasImbuingItem() const; void setImbuingItem(const std::shared_ptr &item); - void addBlessing(uint8_t index, uint8_t count) { - if (blessings[index - 1] == 255) { - return; - } - - blessings[index - 1] += count; - } - void removeBlessing(uint8_t index, uint8_t count) { - if (blessings[index - 1] == 0) { - return; - } - - blessings[index - 1] -= count; - } - bool hasBlessing(uint8_t index) const { - return blessings[index - 1] != 0; - } + void addBlessing(uint8_t index, uint8_t count); + void removeBlessing(uint8_t index, uint8_t count); + bool hasBlessing(uint8_t index) const; - uint8_t getBlessingCount(uint8_t index, bool storeCount = false) const { - if (!storeCount) { - if (index > 0 && index <= blessings.size()) { - return blessings[index - 1]; - } else { - g_logger().error("[{}] - index outside range 0-10.", __FUNCTION__); - return 0; - } - } - const auto amount = kv()->scoped("summary")->scoped("blessings")->scoped(fmt::format("{}", index))->get("amount"); - return amount ? static_cast(amount->getNumber()) : 0; - } + uint8_t getBlessingCount(uint8_t index, bool storeCount = false) const; std::string getBlessingsName() const; bool isOffline() const { return (getID() == 0); } - void disconnect() const { - if (client) { - client->disconnect(); - } - } + void disconnect() const; - uint32_t getIP() const { - return client ? client->getIP() : 0; - } + uint32_t getIP() const; bool isDisconnected() const { return getIP() == 0; @@ -524,9 +372,7 @@ class Player final : public Creature, public Cylinder, public Bankable { int32_t getStorageValueByName(const std::string &storageName) const; void addStorageValueByName(const std::string &storageName, int32_t value, bool isLogin = false); - std::shared_ptr kv() const { - return g_kv().scoped("player")->scoped(fmt::format("{}", getGUID())); - } + std::shared_ptr kv() const; void genReservedStorageRange(); @@ -543,23 +389,7 @@ class Player final : public Creature, public Cylinder, public Bankable { bool isInMarket() const { return inMarket; } - void setSpecialMenuAvailable(bool supplyStashBool, bool marketMenuBool, bool depotSearchBool) { - // Closing depot search when player have special container disabled and it's still open. - if (isDepotSearchOpen() && !depotSearchBool && depotSearch) { - depotSearchOnItem = { 0, 0 }; - sendCloseDepotSearch(); - } - - // Menu option 'stow, stow container ...' - // Menu option 'show in market' - // Menu option to open depot search - supplyStash = supplyStashBool; - marketMenu = marketMenuBool; - depotSearch = depotSearchBool; - if (client) { - client->sendSpecialContainersAvailable(); - } - } + void setSpecialMenuAvailable(bool supplyStashBool, bool marketMenuBool, bool depotSearchBool); bool isDepotSearchOpen() const { return depotSearchOnItem.first != 0; } @@ -627,9 +457,7 @@ class Player final : public Creature, public Cylinder, public Bankable { uint32_t getPremiumDays() const; time_t getPremiumLastDay() const; - bool isVip() const { - return g_configManager().getBoolean(VIP_SYSTEM_ENABLED) && (getPremiumDays() > 0 || getPremiumLastDay() > getTimeNow()); - } + bool isVip() const; void setTibiaCoins(int32_t v); void setTransferableTibiaCoins(int32_t v); @@ -637,9 +465,7 @@ class Player final : public Creature, public Cylinder, public Bankable { uint16_t getHelpers() const; bool setVocation(uint16_t vocId); - uint16_t getVocationId() const { - return vocation->getId(); - } + uint16_t getVocationId() const; PlayerSex_t getSex() const { return sex; @@ -647,21 +473,11 @@ class Player final : public Creature, public Cylinder, public Bankable { PlayerPronoun_t getPronoun() const { return pronoun; } - std::string getObjectPronoun() const { - return getPlayerObjectPronoun(pronoun, sex, name); - } - std::string getSubjectPronoun() const { - return getPlayerSubjectPronoun(pronoun, sex, name); - } - std::string getPossessivePronoun() const { - return getPlayerPossessivePronoun(pronoun, sex, name); - } - std::string getReflexivePronoun() const { - return getPlayerReflexivePronoun(pronoun, sex, name); - } - std::string getSubjectVerb(bool past = false) const { - return getVerbForPronoun(pronoun, past); - } + std::string getObjectPronoun() const; + std::string getSubjectPronoun() const; + std::string getPossessivePronoun() const; + std::string getReflexivePronoun() const; + std::string getSubjectVerb(bool past = false) const; void setSex(PlayerSex_t); void setPronoun(PlayerPronoun_t); uint64_t getExperience() const { @@ -679,14 +495,7 @@ class Player final : public Creature, public Cylinder, public Bankable { const Position &getLoginPosition() const { return loginPosition; } - const Position &getTemplePosition() const { - if (!town) { - static auto emptyPosition = Position(); - return emptyPosition; - } - - return town->getTemplePosition(); - } + const Position &getTemplePosition() const; std::shared_ptr getTown() const; void setTown(const std::shared_ptr &newTown); @@ -709,84 +518,28 @@ class Player final : public Creature, public Cylinder, public Bankable { */ bool removeItemCountById(uint16_t itemId, uint32_t itemAmount, bool removeFromStash = true); - void addItemOnStash(uint16_t itemId, uint32_t amount) { - const auto it = stashItems.find(itemId); - if (it != stashItems.end()) { - stashItems[itemId] += amount; - return; - } - - stashItems[itemId] = amount; - } - uint32_t getStashItemCount(uint16_t itemId) const { - const auto it = stashItems.find(itemId); - if (it != stashItems.end()) { - return it->second; - } - return 0; - } - bool withdrawItem(uint16_t itemId, uint32_t amount) { - const auto it = stashItems.find(itemId); - if (it != stashItems.end()) { - if (it->second > amount) { - stashItems[itemId] -= amount; - } else if (it->second == amount) { - stashItems.erase(itemId); - } else { - return false; - } - return true; - } - return false; - } - StashItemList getStashItems() const { - return stashItems; - } + void addItemOnStash(uint16_t itemId, uint32_t amount); + uint32_t getStashItemCount(uint16_t itemId) const; + bool withdrawItem(uint16_t itemId, uint32_t amount); + StashItemList getStashItems() const; - uint32_t getBaseCapacity() const { - if (hasFlag(PlayerFlags_t::CannotPickupItem)) { - return 0; - } - if (hasFlag(PlayerFlags_t::HasInfiniteCapacity)) { - return std::numeric_limits::max(); - } - return capacity; - } + uint32_t getBaseCapacity() const; uint32_t getCapacity() const; - uint32_t getBonusCapacity() const { - if (hasFlag(PlayerFlags_t::CannotPickupItem) || hasFlag(PlayerFlags_t::HasInfiniteCapacity)) { - return std::numeric_limits::max(); - } - return bonusCapacity; - } + uint32_t getBonusCapacity() const; - uint32_t getFreeCapacity() const { - if (hasFlag(PlayerFlags_t::CannotPickupItem)) { - return 0; - } else if (hasFlag(PlayerFlags_t::HasInfiniteCapacity)) { - return std::numeric_limits::max(); - } else { - return std::max(0, getCapacity() - inventoryWeight); - } - } + uint32_t getFreeCapacity() const; int32_t getMaxHealth() const override; uint32_t getMaxMana() const override; std::shared_ptr getInventoryItem(Slots_t slot) const; - bool isItemAbilityEnabled(Slots_t slot) const { - return inventoryAbilities[slot]; - } - void setItemAbility(Slots_t slot, bool enabled) { - inventoryAbilities[slot] = enabled; - } + bool isItemAbilityEnabled(Slots_t slot) const; + void setItemAbility(Slots_t slot, bool enabled); - void setVarSkill(skills_t skill, int32_t modifier) { - varSkills[skill] += modifier; - } + void setVarSkill(skills_t skill, int32_t modifier); void setVarStats(stats_t stat, int32_t modifier); int32_t getDefaultStats(stats_t stat) const; @@ -816,32 +569,20 @@ class Player final : public Creature, public Cylinder, public Bankable { bool canWalkthrough(const std::shared_ptr &creature); bool canWalkthroughEx(const std::shared_ptr &creature) const; - RaceType_t getRace() const override { - return RACE_BLOOD; - } + RaceType_t getRace() const override; uint64_t getMoney() const; std::pair getForgeSliversAndCores() const; // safe-trade functions - void setTradeState(TradeState_t state) { - tradeState = state; - } - TradeState_t getTradeState() const { - return tradeState; - } - std::shared_ptr getTradeItem() { - return tradeItem; - } + void setTradeState(TradeState_t state); + TradeState_t getTradeState() const; + std::shared_ptr getTradeItem(); // shop functions - void setShopOwner(std::shared_ptr owner) { - shopOwner = std::move(owner); - } + void setShopOwner(std::shared_ptr owner); - std::shared_ptr getShopOwner() const { - return shopOwner; - } + std::shared_ptr getShopOwner() const; // follow functions bool setFollowCreature(const std::shared_ptr &creature) override; @@ -862,20 +603,12 @@ class Player final : public Creature, public Cylinder, public Bankable { bool hasShopItemForSale(uint16_t itemId, uint8_t subType) const; void setChaseMode(bool mode); - void setFightMode(FightMode_t mode) { - fightMode = mode; - } - void setSecureMode(bool mode) { - secureMode = mode; - } + void setFightMode(FightMode_t mode); + void setSecureMode(bool mode); - Faction_t getFaction() const override { - return faction; - } + Faction_t getFaction() const override; - void setFaction(Faction_t factionId) { - faction = factionId; - } + void setFaction(Faction_t factionId); // combat functions bool setAttackedCreature(const std::shared_ptr &creature) override; bool isImmune(CombatType_t type) const override; @@ -892,78 +625,37 @@ class Player final : public Creature, public Cylinder, public Bankable { void changeMana(int32_t manaChange) override; void changeSoul(int32_t soulChange); - bool isPzLocked() const { - return pzLocked; - } + bool isPzLocked() const; BlockType_t blockHit(const std::shared_ptr &attacker, const CombatType_t &combatType, int32_t &damage, bool checkDefense = false, bool checkArmor = false, bool field = false) override; void doAttacking(uint32_t interval) override; - bool hasExtraSwing() override { - return lastAttack > 0 && !checkLastAttackWithin(getAttackSpeed()); - } + bool hasExtraSwing() override; uint16_t getSkillLevel(skills_t skill) const; uint16_t getLoyaltySkill(skills_t skill) const; - uint16_t getBaseSkill(uint8_t skill) const { - return skills[skill].level; - } - double_t getSkillPercent(skills_t skill) const { - return skills[skill].percent; - } + uint16_t getBaseSkill(uint8_t skill) const; + double_t getSkillPercent(skills_t skill) const; - bool getAddAttackSkill() const { - return addAttackSkillPoint; - } + bool getAddAttackSkill() const; - BlockType_t getLastAttackBlockType() const { - return lastAttackBlockType; - } + BlockType_t getLastAttackBlockType() const; - uint64_t getLastConditionTime(ConditionType_t type) const { - if (!lastConditionTime.contains(static_cast(type))) { - return 0; - } - return lastConditionTime.at(static_cast(type)); - } + uint64_t getLastConditionTime(ConditionType_t type) const; - void updateLastConditionTime(ConditionType_t type) { - lastConditionTime[static_cast(type)] = OTSYS_TIME(); - } + void updateLastConditionTime(ConditionType_t type); - bool checkLastConditionTimeWithin(ConditionType_t type, uint32_t interval) const { - if (!lastConditionTime.contains(static_cast(type))) { - return false; - } - const auto last = lastConditionTime.at(static_cast(type)); - return last > 0 && ((OTSYS_TIME() - last) < interval); - } + bool checkLastConditionTimeWithin(ConditionType_t type, uint32_t interval) const; - uint64_t getLastAttack() const { - return lastAttack; - } + uint64_t getLastAttack() const; - bool checkLastAttackWithin(uint32_t interval) const { - return lastAttack > 0 && ((OTSYS_TIME() - lastAttack) < interval); - } + bool checkLastAttackWithin(uint32_t interval) const; - void updateLastAttack() { - if (lastAttack == 0) { - lastAttack = OTSYS_TIME() - getAttackSpeed() - 1; - return; - } - lastAttack = OTSYS_TIME(); - } + void updateLastAttack(); - uint64_t getLastAggressiveAction() const { - return lastAggressiveAction; - } + uint64_t getLastAggressiveAction() const; - bool checkLastAggressiveActionWithin(uint32_t interval) const { - return lastAggressiveAction > 0 && ((OTSYS_TIME() - lastAggressiveAction) < interval); - } + bool checkLastAggressiveActionWithin(uint32_t interval) const; - void updateLastAggressiveAction() { - lastAggressiveAction = OTSYS_TIME(); - } + void updateLastAggressiveAction(); std::shared_ptr getWeapon(Slots_t slot, bool ignoreAmmo) const; std::shared_ptr getWeapon(bool ignoreAmmo = false) const; @@ -1011,28 +703,16 @@ class Player final : public Creature, public Cylinder, public Bankable { Skulls_t getSkull() const override; Skulls_t getSkullClient(const std::shared_ptr &creature) override; - int64_t getSkullTicks() const { - return skullTicks; - } - void setSkullTicks(int64_t ticks) { - skullTicks = ticks; - } + int64_t getSkullTicks() const; + void setSkullTicks(int64_t ticks); bool hasAttacked(const std::shared_ptr &attacked) const; void addAttacked(const std::shared_ptr &attacked); void removeAttacked(const std::shared_ptr &attacked); void clearAttacked(); void addUnjustifiedDead(const std::shared_ptr &attacked); - void sendCreatureEmblem(const std::shared_ptr &creature) const { - if (client) { - client->sendCreatureEmblem(creature); - } - } - void sendCreatureSkull(const std::shared_ptr &creature) const { - if (client) { - client->sendCreatureSkull(creature); - } - } + void sendCreatureEmblem(const std::shared_ptr &creature) const; + void sendCreatureSkull(const std::shared_ptr &creature) const; void checkSkullTicks(int64_t ticks); bool canWear(uint16_t lookType, uint8_t addons) const; @@ -1045,9 +725,7 @@ class Player final : public Creature, public Cylinder, public Bankable { void addFamiliar(uint16_t lookType); bool removeFamiliar(uint16_t lookType); bool getFamiliar(const std::shared_ptr &familiar) const; - void setFamiliarLooktype(uint16_t familiarLooktype) { - this->defaultOutfit.lookFamiliarsType = familiarLooktype; - } + void setFamiliarLooktype(uint16_t familiarLooktype); bool canLogout(); @@ -1057,255 +735,61 @@ class Player final : public Creature, public Cylinder, public Bankable { // tile // send methods - void sendAddTileItem(const std::shared_ptr &itemTile, const Position &pos, const std::shared_ptr &item) { - if (client) { - const int32_t stackpos = itemTile->getStackposOfItem(static_self_cast(), item); - if (stackpos != -1) { - client->sendAddTileItem(pos, stackpos, item); - } - } - } - void sendUpdateTileItem(const std::shared_ptr &updateTile, const Position &pos, const std::shared_ptr &item) { - if (client) { - const int32_t stackpos = updateTile->getStackposOfItem(static_self_cast(), item); - if (stackpos != -1) { - client->sendUpdateTileItem(pos, stackpos, item); - } - } - } - void sendRemoveTileThing(const Position &pos, int32_t stackpos) const { - if (stackpos != -1 && client) { - client->sendRemoveTileThing(pos, stackpos); - } - } - void sendUpdateTileCreature(const std::shared_ptr &creature) { - if (client) { - client->sendUpdateTileCreature(creature->getPosition(), creature->getTile()->getClientIndexOfCreature(static_self_cast(), creature), creature); - } - } - void sendUpdateTile(const std::shared_ptr &updateTile, const Position &pos) const { - if (client) { - client->sendUpdateTile(updateTile, pos); - } - } - - void sendChannelMessage(const std::string &author, const std::string &text, SpeakClasses type, uint16_t channel) const { - if (client) { - client->sendChannelMessage(author, text, type, channel); - } - } - void sendChannelEvent(uint16_t channelId, const std::string &playerName, ChannelEvent_t channelEvent) const { - if (client) { - client->sendChannelEvent(channelId, playerName, channelEvent); - } - } - void sendCreatureAppear(const std::shared_ptr &creature, const Position &pos, bool isLogin) { - if (!creature) { - return; - } - - const auto &tile = creature->getTile(); - if (!tile) { - return; - } - - if (client) { - client->sendAddCreature(creature, pos, tile->getStackposOfCreature(static_self_cast(), creature), isLogin); - } - } - void sendCreatureMove(const std::shared_ptr &creature, const Position &newPos, int32_t newStackPos, const Position &oldPos, int32_t oldStackPos, bool teleport) const { - if (client) { - client->sendMoveCreature(creature, newPos, newStackPos, oldPos, oldStackPos, teleport); - } - } - void sendCreatureTurn(const std::shared_ptr &creature) { - if (!creature) { - return; - } - - const auto &tile = creature->getTile(); - if (!tile) { - return; - } - - if (client && canSeeCreature(creature)) { - const int32_t stackpos = tile->getStackposOfCreature(static_self_cast(), creature); - if (stackpos != -1) { - client->sendCreatureTurn(creature, stackpos); - } - } - } - void sendCreatureSay(const std::shared_ptr &creature, SpeakClasses type, const std::string &text, const Position* pos = nullptr) const { - if (client) { - client->sendCreatureSay(creature, type, text, pos); - } - } - void sendCreatureReload(const std::shared_ptr &creature) const { - if (client) { - client->reloadCreature(creature); - } - } - void sendPrivateMessage(const std::shared_ptr &speaker, SpeakClasses type, const std::string &text) const { - if (client) { - client->sendPrivateMessage(speaker, type, text); - } - } - void sendCreatureSquare(const std::shared_ptr &creature, SquareColor_t color) const { - if (client) { - client->sendCreatureSquare(creature, color); - } - } - void sendCreatureChangeOutfit(const std::shared_ptr &creature, const Outfit_t &outfit) const { - if (client) { - client->sendCreatureOutfit(creature, outfit); - } - } - void sendCreatureChangeVisible(const std::shared_ptr &creature, bool visible) { - if (!client || !creature) { - return; - } - - if (creature->getPlayer()) { - if (visible) { - client->sendCreatureOutfit(creature, creature->getCurrentOutfit()); - } else { - static Outfit_t outfit; - client->sendCreatureOutfit(creature, outfit); - } - } else if (canSeeInvisibility()) { - client->sendCreatureOutfit(creature, creature->getCurrentOutfit()); - } else { - const auto tile = creature->getTile(); - if (!tile) { - return; - } - const int32_t stackpos = tile->getStackposOfCreature(static_self_cast(), creature); - if (stackpos == -1) { - return; - } - - if (visible) { - client->sendAddCreature(creature, creature->getPosition(), stackpos, false); - } else { - client->sendRemoveTileThing(creature->getPosition(), stackpos); - } - } - } - void sendCreatureLight(const std::shared_ptr &creature) const { - if (client) { - client->sendCreatureLight(creature); - } - } - void sendCreatureIcon(const std::shared_ptr &creature) const { - if (client && !client->oldProtocol) { - client->sendCreatureIcon(creature); - } - } - void sendUpdateCreature(const std::shared_ptr &creature) const { - if (client) { - client->sendUpdateCreature(creature); - } - } - void sendCreatureWalkthrough(const std::shared_ptr &creature, bool walkthrough) const { - if (client) { - client->sendCreatureWalkthrough(creature, walkthrough); - } - } - void sendCreatureShield(const std::shared_ptr &creature) const { - if (client) { - client->sendCreatureShield(creature); - } - } - void sendCreatureType(const std::shared_ptr &creature, uint8_t creatureType) const { - if (client) { - client->sendCreatureType(creature, creatureType); - } - } - void sendSpellCooldown(uint16_t spellId, uint32_t time) const { - if (client) { - client->sendSpellCooldown(spellId, time); - } - } - void sendSpellGroupCooldown(SpellGroup_t groupId, uint32_t time) const { - if (client) { - client->sendSpellGroupCooldown(groupId, time); - } - } - void sendUseItemCooldown(uint32_t time) const { - if (client) { - client->sendUseItemCooldown(time); - } - } - void reloadCreature(const std::shared_ptr &creature) const { - if (client) { - client->reloadCreature(creature); - } - } + // tile + // send methods + void sendAddTileItem(const std::shared_ptr &itemTile, const Position &pos, const std::shared_ptr &item); + void sendUpdateTileItem(const std::shared_ptr &updateTile, const Position &pos, const std::shared_ptr &item); + void sendRemoveTileThing(const Position &pos, int32_t stackpos) const; + void sendUpdateTileCreature(const std::shared_ptr &creature); + void sendUpdateTile(const std::shared_ptr &updateTile, const Position &pos) const; + + void sendChannelMessage(const std::string &author, const std::string &text, SpeakClasses type, uint16_t channel) const; + void sendChannelEvent(uint16_t channelId, const std::string &playerName, ChannelEvent_t channelEvent) const; + void sendCreatureAppear(const std::shared_ptr &creature, const Position &pos, bool isLogin); + void sendCreatureMove(const std::shared_ptr &creature, const Position &newPos, int32_t newStackPos, const Position &oldPos, int32_t oldStackPos, bool teleport) const; + void sendCreatureTurn(const std::shared_ptr &creature); + void sendCreatureSay(const std::shared_ptr &creature, SpeakClasses type, const std::string &text, const Position* pos = nullptr) const; + void sendCreatureReload(const std::shared_ptr &creature) const; + void sendPrivateMessage(const std::shared_ptr &speaker, SpeakClasses type, const std::string &text) const; + void sendCreatureSquare(const std::shared_ptr &creature, SquareColor_t color) const; + void sendCreatureChangeOutfit(const std::shared_ptr &creature, const Outfit_t &outfit) const; + void sendCreatureChangeVisible(const std::shared_ptr &creature, bool visible); + void sendCreatureLight(const std::shared_ptr &creature) const; + void sendCreatureIcon(const std::shared_ptr &creature) const; + void sendUpdateCreature(const std::shared_ptr &creature) const; + void sendCreatureWalkthrough(const std::shared_ptr &creature, bool walkthrough) const; + void sendCreatureShield(const std::shared_ptr &creature) const; + void sendCreatureType(const std::shared_ptr &creature, uint8_t creatureType) const; + void sendSpellCooldown(uint16_t spellId, uint32_t time) const; + void sendSpellGroupCooldown(SpellGroup_t groupId, uint32_t time) const; + void sendUseItemCooldown(uint32_t time) const; + void reloadCreature(const std::shared_ptr &creature) const; void sendModalWindow(const ModalWindow &modalWindow); // container void closeAllExternalContainers(); + // container void sendAddContainerItem(const std::shared_ptr &container, std::shared_ptr item); void sendUpdateContainerItem(const std::shared_ptr &container, uint16_t slot, const std::shared_ptr &newItem); void sendRemoveContainerItem(const std::shared_ptr &container, uint16_t slot); - void sendContainer(uint8_t cid, const std::shared_ptr &container, bool hasParent, uint16_t firstIndex) const { - if (client) { - client->sendContainer(cid, container, hasParent, firstIndex); - } - } + void sendContainer(uint8_t cid, const std::shared_ptr &container, bool hasParent, uint16_t firstIndex) const; // inventory - void sendDepotItems(const ItemsTierCountList &itemMap, uint16_t count) const { - if (client) { - client->sendDepotItems(itemMap, count); - } - } - void sendCloseDepotSearch() const { - if (client) { - client->sendCloseDepotSearch(); - } - } - void sendDepotSearchResultDetail(uint16_t itemId, uint8_t tier, uint32_t depotCount, const ItemVector &depotItems, uint32_t inboxCount, const ItemVector &inboxItems, uint32_t stashCount) const { - if (client) { - client->sendDepotSearchResultDetail(itemId, tier, depotCount, depotItems, inboxCount, inboxItems, stashCount); - } - } - void sendCoinBalance() const { - if (client) { - client->sendCoinBalance(); - } - } - void sendInventoryItem(Slots_t slot, const std::shared_ptr &item) const { - if (client) { - client->sendInventoryItem(slot, item); - } - } - void sendInventoryIds() const { - if (client) { - client->sendInventoryIds(); - } - } + void sendDepotItems(const ItemsTierCountList &itemMap, uint16_t count) const; + void sendCloseDepotSearch() const; + void sendDepotSearchResultDetail(uint16_t itemId, uint8_t tier, uint32_t depotCount, const ItemVector &depotItems, uint32_t inboxCount, const ItemVector &inboxItems, uint32_t stashCount) const; + void sendCoinBalance() const; + void sendInventoryItem(Slots_t slot, const std::shared_ptr &item) const; + void sendInventoryIds() const; void openPlayerContainers(); // Quickloot - void sendLootContainers() const { - if (client) { - client->sendLootContainers(); - } - } + void sendLootContainers() const; - void sendSingleSoundEffect(const Position &pos, SoundEffect_t id, SourceEffect_t source) const { - if (client) { - client->sendSingleSoundEffect(pos, id, source); - } - } + void sendSingleSoundEffect(const Position &pos, SoundEffect_t id, SourceEffect_t source) const; - void sendDoubleSoundEffect(const Position &pos, SoundEffect_t mainSoundId, SourceEffect_t mainSource, SoundEffect_t secondarySoundId, SourceEffect_t secondarySource) const { - if (client) { - client->sendDoubleSoundEffect(pos, mainSoundId, mainSource, secondarySoundId, secondarySource); - } - } + void sendDoubleSoundEffect(const Position &pos, SoundEffect_t mainSoundId, SourceEffect_t mainSource, SoundEffect_t secondarySoundId, SourceEffect_t secondarySource) const; SoundEffect_t getAttackSoundEffect() const; SoundEffect_t getHitSoundEffect() const; @@ -1324,6 +808,7 @@ class Player final : public Creature, public Cylinder, public Bankable { void onAttackedCreatureDisappear(bool isLogout) override; void onFollowCreatureDisappear(bool isLogout) override; + // container // container void onAddContainerItem(const std::shared_ptr &item); void onUpdateContainerItem(const std::shared_ptr &container, const std::shared_ptr &oldItem, const std::shared_ptr &newItem); @@ -1331,441 +816,127 @@ class Player final : public Creature, public Cylinder, public Bankable { void onCloseContainer(const std::shared_ptr &container); void onSendContainer(const std::shared_ptr &container); + // close container and its child containers void autoCloseContainers(const std::shared_ptr &container); + // inventory // inventory void onUpdateInventoryItem(const std::shared_ptr &oldItem, const std::shared_ptr &newItem); void onRemoveInventoryItem(const std::shared_ptr &item); - void sendCancelMessage(const std::string &msg) const { - if (client) { - client->sendTextMessage(TextMessage(MESSAGE_FAILURE, msg)); - } - } + void sendCancelMessage(const std::string &msg) const; void sendCancelMessage(ReturnValue message) const; - void sendCancelTarget() const { - if (client) { - client->sendCancelTarget(); - } - } - void sendCancelWalk() const { - if (client) { - client->sendCancelWalk(); - } - } - void sendChangeSpeed(const std::shared_ptr &creature, uint16_t newSpeed) const { - if (client) { - client->sendChangeSpeed(creature, newSpeed); - } - } - void sendCreatureHealth(const std::shared_ptr &creature) const { - if (client) { - client->sendCreatureHealth(creature); - } - } - void sendPartyCreatureUpdate(const std::shared_ptr &creature) const { - if (client) { - client->sendPartyCreatureUpdate(creature); - } - } - void sendPartyCreatureShield(const std::shared_ptr &creature) const { - if (client) { - client->sendPartyCreatureShield(creature); - } - } - void sendPartyCreatureSkull(const std::shared_ptr &creature) const { - if (client) { - client->sendPartyCreatureSkull(creature); - } - } - void sendPartyCreatureHealth(const std::shared_ptr &creature, uint8_t healthPercent) const { - if (client) { - client->sendPartyCreatureHealth(creature, healthPercent); - } - } - void sendPartyPlayerMana(const std::shared_ptr &player, uint8_t manaPercent) const { - if (client) { - client->sendPartyPlayerMana(player, manaPercent); - } - } - void sendPartyCreatureShowStatus(const std::shared_ptr &creature, bool showStatus) const { - if (client) { - client->sendPartyCreatureShowStatus(creature, showStatus); - } - } - void sendPartyPlayerVocation(const std::shared_ptr &player) const { - if (client) { - client->sendPartyPlayerVocation(player); - } - } - void sendPlayerVocation(const std::shared_ptr &player) const { - if (client) { - client->sendPlayerVocation(player); - } - } - void sendDistanceShoot(const Position &from, const Position &to, uint16_t type) const { - if (client) { - client->sendDistanceShoot(from, to, type); - } - } + void sendCancelTarget() const; + void sendCancelWalk() const; + void sendChangeSpeed(const std::shared_ptr &creature, uint16_t newSpeed) const; + void sendCreatureHealth(const std::shared_ptr &creature) const; + void sendPartyCreatureUpdate(const std::shared_ptr &creature) const; + void sendPartyCreatureShield(const std::shared_ptr &creature) const; + void sendPartyCreatureSkull(const std::shared_ptr &creature) const; + void sendPartyCreatureHealth(const std::shared_ptr &creature, uint8_t healthPercent) const; + void sendPartyPlayerMana(const std::shared_ptr &player, uint8_t manaPercent) const; + void sendPartyCreatureShowStatus(const std::shared_ptr &creature, bool showStatus) const; + void sendPartyPlayerVocation(const std::shared_ptr &player) const; + void sendPlayerVocation(const std::shared_ptr &player) const; + void sendDistanceShoot(const Position &from, const Position &to, uint16_t type) const; void sendHouseWindow(const std::shared_ptr &house, uint32_t listId) const; - void sendCreatePrivateChannel(uint16_t channelId, const std::string &channelName) const { - if (client) { - client->sendCreatePrivateChannel(channelId, channelName); - } - } + void sendCreatePrivateChannel(uint16_t channelId, const std::string &channelName) const; void sendClosePrivate(uint16_t channelId); void sendIcons(); void sendIconBakragore(IconBakragore icon) const; void removeBakragoreIcons(); void removeBakragoreIcon(const IconBakragore icon); - void sendClientCheck() const { - if (client) { - client->sendClientCheck(); - } - } - void sendGameNews() const { - if (client) { - client->sendGameNews(); - } - } - void sendMagicEffect(const Position &pos, uint16_t type) const { - if (client) { - client->sendMagicEffect(pos, type); - } - } - void removeMagicEffect(const Position &pos, uint16_t type) const { - if (client) { - client->removeMagicEffect(pos, type); - } - } + void sendClientCheck() const; + void sendGameNews() const; + void sendMagicEffect(const Position &pos, uint16_t type) const; + void removeMagicEffect(const Position &pos, uint16_t type) const; void sendPing(); - void sendPingBack() const { - if (client) { - client->sendPingBack(); - } - } + void sendPingBack() const; void sendStats(); - void sendBasicData() const { - if (client) { - client->sendBasicData(); - } - } - void sendBlessStatus() const { - if (client) { - client->sendBlessStatus(); - } - } - void sendSkills() const { - if (client) { - client->sendSkills(); - } - } - void sendTextMessage(MessageClasses mclass, const std::string &message) const { - if (client) { - client->sendTextMessage(TextMessage(mclass, message)); - } - } - void sendTextMessage(const TextMessage &message) const { - if (client) { - client->sendTextMessage(message); - } - } - void sendReLoginWindow(uint8_t unfairFightReduction) const { - if (client) { - client->sendReLoginWindow(unfairFightReduction); - } - } - void sendTextWindow(const std::shared_ptr &item, uint16_t maxlen, bool canWrite) const { - if (client) { - client->sendTextWindow(windowTextId, item, maxlen, canWrite); - } - } - void sendToChannel(const std::shared_ptr &creature, SpeakClasses type, const std::string &text, uint16_t channelId) const { - if (client) { - client->sendToChannel(creature, type, text, channelId); - } - } - void sendShop(const std::shared_ptr &npc) const { - if (client) { - client->sendShop(npc); - } - } + void sendBasicData() const; + void sendBlessStatus() const; + void sendSkills() const; + void sendTextMessage(MessageClasses mclass, const std::string &message) const; + void sendTextMessage(const TextMessage &message) const; + void sendReLoginWindow(uint8_t unfairFightReduction) const; + void sendTextWindow(const std::shared_ptr &item, uint16_t maxlen, bool canWrite) const; + void sendToChannel(const std::shared_ptr &creature, SpeakClasses type, const std::string &text, uint16_t channelId) const; + void sendShop(const std::shared_ptr &npc) const; void sendSaleItemList(const std::map &inventoryMap) const; - void sendCloseShop() const { - if (client) { - client->sendCloseShop(); - } - } + void sendCloseShop() const; void sendMarketEnter(uint32_t depotId) const; - void sendMarketLeave() { - inMarket = false; - if (client) { - client->sendMarketLeave(); - } - } - void sendMarketBrowseItem(uint16_t itemId, const MarketOfferList &buyOffers, const MarketOfferList &sellOffers, uint8_t tier) const { - if (client) { - client->sendMarketBrowseItem(itemId, buyOffers, sellOffers, tier); - } - } - void sendMarketBrowseOwnOffers(const MarketOfferList &buyOffers, const MarketOfferList &sellOffers) const { - if (client) { - client->sendMarketBrowseOwnOffers(buyOffers, sellOffers); - } - } - void sendMarketBrowseOwnHistory(const HistoryMarketOfferList &buyOffers, const HistoryMarketOfferList &sellOffers) const { - if (client) { - client->sendMarketBrowseOwnHistory(buyOffers, sellOffers); - } - } - void sendMarketDetail(uint16_t itemId, uint8_t tier) const { - if (client) { - client->sendMarketDetail(itemId, tier); - } - } - void sendMarketAcceptOffer(const MarketOfferEx &offer) const { - if (client) { - client->sendMarketAcceptOffer(offer); - } - } - void sendMarketCancelOffer(const MarketOfferEx &offer) const { - if (client) { - client->sendMarketCancelOffer(offer); - } - } - void sendTradeItemRequest(const std::string &traderName, const std::shared_ptr &item, bool ack) const { - if (client) { - client->sendTradeItemRequest(traderName, item, ack); - } - } - void sendTradeClose() const { - if (client) { - client->sendCloseTrade(); - } - } - void sendWorldLight(LightInfo lightInfo) const { - if (client) { - client->sendWorldLight(lightInfo); - } - } - void sendTibiaTime(int32_t time) const { - if (client) { - client->sendTibiaTime(time); - } - } - void sendChannelsDialog() const { - if (client) { - client->sendChannelsDialog(); - } - } - void sendOpenPrivateChannel(const std::string &receiver) const { - if (client) { - client->sendOpenPrivateChannel(receiver); - } - } - void sendExperienceTracker(int64_t rawExp, int64_t finalExp) const { - if (client) { - client->sendExperienceTracker(rawExp, finalExp); - } - } - void sendOutfitWindow() const { - if (client) { - client->sendOutfitWindow(); - } - } + void sendMarketLeave(); + void sendMarketBrowseItem(uint16_t itemId, const MarketOfferList &buyOffers, const MarketOfferList &sellOffers, uint8_t tier) const; + void sendMarketBrowseOwnOffers(const MarketOfferList &buyOffers, const MarketOfferList &sellOffers) const; + void sendMarketBrowseOwnHistory(const HistoryMarketOfferList &buyOffers, const HistoryMarketOfferList &sellOffers) const; + void sendMarketDetail(uint16_t itemId, uint8_t tier) const; + void sendMarketAcceptOffer(const MarketOfferEx &offer) const; + void sendMarketCancelOffer(const MarketOfferEx &offer) const; + void sendTradeItemRequest(const std::string &traderName, const std::shared_ptr &item, bool ack) const; + void sendTradeClose() const; + void sendWorldLight(LightInfo lightInfo) const; + void sendTibiaTime(int32_t time) const; + void sendChannelsDialog() const; + void sendOpenPrivateChannel(const std::string &receiver) const; + void sendExperienceTracker(int64_t rawExp, int64_t finalExp) const; + void sendOutfitWindow() const; // Imbuements void onApplyImbuement(const Imbuement* imbuement, const std::shared_ptr &item, uint8_t slot, bool protectionCharm); void onClearImbuement(const std::shared_ptr &item, uint8_t slot); void openImbuementWindow(const std::shared_ptr &item); - void sendImbuementResult(const std::string &message) const { - if (client) { - client->sendImbuementResult(message); - } - } - void closeImbuementWindow() const { - if (client) { - client->closeImbuementWindow(); - } - } - void sendPodiumWindow(const std::shared_ptr &podium, const Position &position, uint16_t itemId, uint8_t stackpos) const { - if (client) { - client->sendPodiumWindow(podium, position, itemId, stackpos); - } - } - void sendCloseContainer(uint8_t cid) const { - if (client) { - client->sendCloseContainer(cid); - } - } - - void sendChannel(uint16_t channelId, const std::string &channelName, const UsersMap* channelUsers, const InvitedMap* invitedUsers) const { - if (client) { - client->sendChannel(channelId, channelName, channelUsers, invitedUsers); - } - } - void sendTutorial(uint8_t tutorialId) const { - if (client) { - client->sendTutorial(tutorialId); - } - } - void sendAddMarker(const Position &pos, uint8_t markType, const std::string &desc) const { - if (client) { - client->sendAddMarker(pos, markType, desc); - } - } - void sendItemInspection(uint16_t itemId, uint8_t itemCount, const std::shared_ptr &item, bool cyclopedia) const { - if (client) { - client->sendItemInspection(itemId, itemCount, item, cyclopedia); - } - } - void sendCyclopediaCharacterNoData(CyclopediaCharacterInfoType_t characterInfoType, uint8_t errorCode) const { - if (client) { - client->sendCyclopediaCharacterNoData(characterInfoType, errorCode); - } - } - void sendCyclopediaCharacterBaseInformation() const { - if (client) { - client->sendCyclopediaCharacterBaseInformation(); - } - } - void sendCyclopediaCharacterGeneralStats() const { - if (client) { - client->sendCyclopediaCharacterGeneralStats(); - } - } - void sendCyclopediaCharacterCombatStats() const { - if (client) { - client->sendCyclopediaCharacterCombatStats(); - } - } - void sendCyclopediaCharacterRecentDeaths(uint16_t page, uint16_t pages, const std::vector &entries) const { - if (client) { - client->sendCyclopediaCharacterRecentDeaths(page, pages, entries); - } - } - void sendCyclopediaCharacterRecentPvPKills(uint16_t page, uint16_t pages, const std::vector &entries) const { - if (client) { - client->sendCyclopediaCharacterRecentPvPKills(page, pages, entries); - } - } + void sendImbuementResult(const std::string &message) const; + void closeImbuementWindow() const; + void sendPodiumWindow(const std::shared_ptr &podium, const Position &position, uint16_t itemId, uint8_t stackpos) const; + void sendCloseContainer(uint8_t cid) const; + + void sendChannel(uint16_t channelId, const std::string &channelName, const UsersMap* channelUsers, const InvitedMap* invitedUsers) const; + void sendTutorial(uint8_t tutorialId) const; + void sendAddMarker(const Position &pos, uint8_t markType, const std::string &desc) const; + void sendItemInspection(uint16_t itemId, uint8_t itemCount, const std::shared_ptr &item, bool cyclopedia) const; + void sendCyclopediaCharacterNoData(CyclopediaCharacterInfoType_t characterInfoType, uint8_t errorCode) const; + void sendCyclopediaCharacterBaseInformation() const; + void sendCyclopediaCharacterGeneralStats() const; + void sendCyclopediaCharacterCombatStats() const; + void sendCyclopediaCharacterRecentDeaths(uint16_t page, uint16_t pages, const std::vector &entries) const; + void sendCyclopediaCharacterRecentPvPKills(uint16_t page, uint16_t pages, const std::vector &entries) const; void sendCyclopediaCharacterAchievements(uint16_t secretsUnlocked, const std::vector> &achievementsUnlocked) const; - void sendCyclopediaCharacterItemSummary(const ItemsTierCountList &inventoryItems, const ItemsTierCountList &storeInboxItems, const StashItemList &supplyStashItems, const ItemsTierCountList &depotBoxItems, const ItemsTierCountList &inboxItems) const { - if (client) { - client->sendCyclopediaCharacterItemSummary(inventoryItems, storeInboxItems, supplyStashItems, depotBoxItems, inboxItems); - } - } - void sendCyclopediaCharacterOutfitsMounts() const { - if (client) { - client->sendCyclopediaCharacterOutfitsMounts(); - } - } - void sendCyclopediaCharacterStoreSummary() const { - if (client) { - client->sendCyclopediaCharacterStoreSummary(); - } - } - void sendCyclopediaCharacterInspection() const { - if (client) { - client->sendCyclopediaCharacterInspection(); - } - } - void sendCyclopediaCharacterBadges() const { - if (client) { - client->sendCyclopediaCharacterBadges(); - } - } - void sendCyclopediaCharacterTitles() const { - if (client) { - client->sendCyclopediaCharacterTitles(); - } - } - void sendHighscoresNoData() const { - if (client) { - client->sendHighscoresNoData(); - } - } - void sendHighscores(const std::vector &characters, uint8_t categoryId, uint32_t vocationId, uint16_t page, uint16_t pages, uint32_t updateTimer) const { - if (client) { - client->sendHighscores(characters, categoryId, vocationId, page, pages, updateTimer); - } - } - void addAsyncOngoingTask(uint64_t flags) { - asyncOngoingTasks |= flags; - } - bool hasAsyncOngoingTask(uint64_t flags) const { - return (asyncOngoingTasks & flags); - } - void resetAsyncOngoingTask(uint64_t flags) { - asyncOngoingTasks &= ~(flags); - } - void sendEnterWorld() const { - if (client) { - client->sendEnterWorld(); - } - } - void sendFightModes() const { - if (client) { - client->sendFightModes(); - } - } - void sendNetworkMessage(const NetworkMessage &message) const { - if (client) { - client->writeToOutputBuffer(message); - } - } - - void receivePing() { - lastPong = OTSYS_TIME(); - } - - void sendOpenStash(bool isNpc = false) const { - if (client && ((getLastDepotId() != -1) || isNpc)) { - client->sendOpenStash(); - } - } - - void sendTakeScreenshot(Screenshot_t screenshotType) const { - if (client) { - client->sendTakeScreenshot(screenshotType); - } - } + void sendCyclopediaCharacterItemSummary(const ItemsTierCountList &inventoryItems, const ItemsTierCountList &storeInboxItems, const StashItemList &supplyStashItems, const ItemsTierCountList &depotBoxItems, const ItemsTierCountList &inboxItems) const; + void sendCyclopediaCharacterOutfitsMounts() const; + void sendCyclopediaCharacterStoreSummary() const; + void sendCyclopediaCharacterInspection() const; + void sendCyclopediaCharacterBadges() const; + void sendCyclopediaCharacterTitles() const; + void sendHighscoresNoData() const; + void sendHighscores(const std::vector &characters, uint8_t categoryId, uint32_t vocationId, uint16_t page, uint16_t pages, uint32_t updateTimer) const; + void addAsyncOngoingTask(uint64_t flags); + bool hasAsyncOngoingTask(uint64_t flags) const; + void resetAsyncOngoingTask(uint64_t flags); + void sendEnterWorld() const; + void sendFightModes() const; + void sendNetworkMessage(const NetworkMessage &message) const; + + void receivePing(); + + void sendOpenStash(bool isNpc = false) const; + + void sendTakeScreenshot(Screenshot_t screenshotType) const; void onThink(uint32_t interval) override; void postAddNotification(const std::shared_ptr &thing, const std::shared_ptr &oldParent, int32_t index, CylinderLink_t link = LINK_OWNER) override; void postRemoveNotification(const std::shared_ptr &thing, const std::shared_ptr &newParent, int32_t index, CylinderLink_t link = LINK_OWNER) override; - void setNextAction(int64_t time) { - if (time > nextAction) { - nextAction = time; - } - } - bool canDoAction() const { - return nextAction <= OTSYS_TIME(); - } + void setNextAction(int64_t time); + bool canDoAction() const; - void setNextPotionAction(int64_t time) { - if (time > nextPotionAction) { - nextPotionAction = time; - } - } - bool canDoPotionAction() const { - return nextPotionAction <= OTSYS_TIME(); - } + void setNextPotionAction(int64_t time); + bool canDoPotionAction() const; void cancelPush(); - void setModuleDelay(uint8_t byteortype, int16_t delay) { - moduleDelayMap[byteortype] = OTSYS_TIME() + delay; - } + void setModuleDelay(uint8_t byteortype, int16_t delay); - bool canRunModule(uint8_t byteortype) { - if (!moduleDelayMap[byteortype]) { - return true; - } - return moduleDelayMap[byteortype] <= OTSYS_TIME(); - } + bool canRunModule(uint8_t byteortype); uint32_t getNextActionTime() const; uint32_t getNextPotionActionTime() const; @@ -1782,85 +953,36 @@ class Player final : public Creature, public Cylinder, public Bankable { void updateRegeneration() const; - void setScheduledSaleUpdate(bool scheduled) { - scheduledSaleUpdate = scheduled; - } + void setScheduledSaleUpdate(bool scheduled); - bool getScheduledSaleUpdate() const { - return scheduledSaleUpdate; - } + bool getScheduledSaleUpdate() const; - bool inPushEvent() const { - return inEventMovePush; - } - - void pushEvent(bool b) { - inEventMovePush = b; - } + bool inPushEvent() const; - bool walkExhausted() const { - if (hasCondition(CONDITION_PARALYZE)) { - return lastWalking > OTSYS_TIME(); - } + void pushEvent(bool b); - return false; - } + bool walkExhausted() const; - void setWalkExhaust(int64_t value) { - lastWalking = OTSYS_TIME() + value; - } + void setWalkExhaust(int64_t value); - const std::map &getOpenContainers() const { - return openContainers; - } + const std::map &getOpenContainers() const; - uint16_t getBaseXpGain() const { - return baseXpGain; - } - void setBaseXpGain(uint16_t value) { - baseXpGain = std::min(std::numeric_limits::max(), value); - } - uint16_t getVoucherXpBoost() const { - return voucherXpBoost; - } - void setVoucherXpBoost(uint16_t value) { - voucherXpBoost = std::min(std::numeric_limits::max(), value); - } - uint16_t getGrindingXpBoost() const { - return grindingXpBoost; - } - void setGrindingXpBoost(uint16_t value) { - grindingXpBoost = std::min(std::numeric_limits::max(), value); - } - uint16_t getXpBoostPercent() const { - return xpBoostPercent; - } - void setXpBoostPercent(uint16_t percent) { - xpBoostPercent = percent; - } - uint16_t getStaminaXpBoost() const { - return staminaXpBoost; - } - void setStaminaXpBoost(uint16_t value) { - staminaXpBoost = std::min(std::numeric_limits::max(), value); - } + uint16_t getBaseXpGain() const; + void setBaseXpGain(uint16_t value); + uint16_t getVoucherXpBoost() const; + void setVoucherXpBoost(uint16_t value); + uint16_t getGrindingXpBoost() const; + void setGrindingXpBoost(uint16_t value); + uint16_t getXpBoostPercent() const; + void setXpBoostPercent(uint16_t percent); + uint16_t getStaminaXpBoost() const; + void setStaminaXpBoost(uint16_t value); - void setXpBoostTime(uint16_t timeLeft) { - // only allow time boosts of 12 hours or less - if (timeLeft > 12 * 3600) { - xpBoostTime = 12 * 3600; - return; - } - xpBoostTime = timeLeft; - } + void setXpBoostTime(uint16_t timeLeft); - uint16_t getXpBoostTime() const { - return xpBoostTime; - } + uint16_t getXpBoostTime() const; - int32_t getIdleTime() const { - return idleTime; - } + int32_t getIdleTime() const; void setTraining(bool value); @@ -1868,258 +990,48 @@ class Player final : public Creature, public Cylinder, public Bankable { void removeItemImbuementStats(const Imbuement* imbuement); void updateImbuementTrackerStats() const; + // User Interface action exhaustion bool isUIExhausted(uint32_t exhaustionTime = 250) const; void updateUIExhausted(); - bool isQuickLootListedItem(const std::shared_ptr &item) const { - if (!item) { - return false; - } + bool isQuickLootListedItem(const std::shared_ptr &item) const; - auto it = std::ranges::find(quickLootListItemIds, item->getID()); - return it != quickLootListItemIds.end(); - } + bool updateKillTracker(const std::shared_ptr &corpse, const std::string &playerName, const Outfit_t &creatureOutfit) const; - bool updateKillTracker(const std::shared_ptr &corpse, const std::string &playerName, const Outfit_t &creatureOutfit) const { - if (client) { - client->sendKillTrackerUpdate(corpse, playerName, creatureOutfit); - return true; - } - - return false; - } - - void updatePartyTrackerAnalyzer() const { - if (client && m_party) { - client->updatePartyTrackerAnalyzer(m_party); - } - } + void updatePartyTrackerAnalyzer() const; void sendLootStats(const std::shared_ptr &item, uint8_t count); void updateSupplyTracker(const std::shared_ptr &item); void updateImpactTracker(CombatType_t type, int32_t amount) const; - void updateInputAnalyzer(CombatType_t type, int32_t amount, const std::string &target) const { - if (client) { - client->sendUpdateInputAnalyzer(type, amount, target); - } - } - - void createLeaderTeamFinder(NetworkMessage &msg) const { - if (client) { - client->createLeaderTeamFinder(msg); - } - } - void sendLeaderTeamFinder(bool reset) const { - if (client) { - client->sendLeaderTeamFinder(reset); - } - } - void sendTeamFinderList() const { - if (client) { - client->sendTeamFinderList(); - } - } - void sendCreatureHelpers(uint32_t creatureId, uint16_t helpers) const { - if (client) { - client->sendCreatureHelpers(creatureId, helpers); - } - } - - void setItemCustomPrice(uint16_t itemId, uint64_t price) { - itemPriceMap[itemId] = price; - } - uint32_t getCharmPoints() const { - return charmPoints; - } - void setCharmPoints(uint32_t points) { - charmPoints = points; - } - bool hasCharmExpansion() const { - return charmExpansion; - } - void setCharmExpansion(bool onOff) { - charmExpansion = onOff; - } - void setUsedRunesBit(int32_t bit) { - UsedRunesBit = bit; - } - int32_t getUsedRunesBit() const { - return UsedRunesBit; - } - void setUnlockedRunesBit(int32_t bit) { - UnlockedRunesBit = bit; - } - int32_t getUnlockedRunesBit() const { - return UnlockedRunesBit; - } - void setImmuneCleanse(ConditionType_t conditiontype) { - cleanseCondition.first = conditiontype; - cleanseCondition.second = OTSYS_TIME() + 10000; - } - bool isImmuneCleanse(ConditionType_t conditiontype) const { - const uint64_t timenow = OTSYS_TIME(); - if ((cleanseCondition.first == conditiontype) - && (timenow <= cleanseCondition.second)) { - return true; - } - return false; - } + void updateInputAnalyzer(CombatType_t type, int32_t amount, const std::string &target) const; + + void createLeaderTeamFinder(NetworkMessage &msg) const; + void sendLeaderTeamFinder(bool reset) const; + void sendTeamFinderList() const; + void sendCreatureHelpers(uint32_t creatureId, uint16_t helpers) const; + + void setItemCustomPrice(uint16_t itemId, uint64_t price); + uint32_t getCharmPoints() const; + void setCharmPoints(uint32_t points); + bool hasCharmExpansion() const; + void setCharmExpansion(bool onOff); + void setUsedRunesBit(int32_t bit); + int32_t getUsedRunesBit() const; + void setUnlockedRunesBit(int32_t bit); + int32_t getUnlockedRunesBit() const; + void setImmuneCleanse(ConditionType_t conditiontype); + bool isImmuneCleanse(ConditionType_t conditiontype) const; void setImmuneFear(); bool isImmuneFear() const; - uint16_t parseRacebyCharm(charmRune_t charmId, bool set, uint16_t newRaceid) { - uint16_t raceid = 0; - switch (charmId) { - case CHARM_WOUND: - if (set) { - charmRuneWound = newRaceid; - } else { - raceid = charmRuneWound; - } - break; - case CHARM_ENFLAME: - if (set) { - charmRuneEnflame = newRaceid; - } else { - raceid = charmRuneEnflame; - } - break; - case CHARM_POISON: - if (set) { - charmRunePoison = newRaceid; - } else { - raceid = charmRunePoison; - } - break; - case CHARM_FREEZE: - if (set) { - charmRuneFreeze = newRaceid; - } else { - raceid = charmRuneFreeze; - } - break; - case CHARM_ZAP: - if (set) { - charmRuneZap = newRaceid; - } else { - raceid = charmRuneZap; - } - break; - case CHARM_CURSE: - if (set) { - charmRuneCurse = newRaceid; - } else { - raceid = charmRuneCurse; - } - break; - case CHARM_CRIPPLE: - if (set) { - charmRuneCripple = newRaceid; - } else { - raceid = charmRuneCripple; - } - break; - case CHARM_PARRY: - if (set) { - charmRuneParry = newRaceid; - } else { - raceid = charmRuneParry; - } - break; - case CHARM_DODGE: - if (set) { - charmRuneDodge = newRaceid; - } else { - raceid = charmRuneDodge; - } - break; - case CHARM_ADRENALINE: - if (set) { - charmRuneAdrenaline = newRaceid; - } else { - raceid = charmRuneAdrenaline; - } - break; - case CHARM_NUMB: - if (set) { - charmRuneNumb = newRaceid; - } else { - raceid = charmRuneNumb; - } - break; - case CHARM_CLEANSE: - if (set) { - charmRuneCleanse = newRaceid; - } else { - raceid = charmRuneCleanse; - } - break; - case CHARM_BLESS: - if (set) { - charmRuneBless = newRaceid; - } else { - raceid = charmRuneBless; - } - break; - case CHARM_SCAVENGE: - if (set) { - charmRuneScavenge = newRaceid; - } else { - raceid = charmRuneScavenge; - } - break; - case CHARM_GUT: - if (set) { - charmRuneGut = newRaceid; - } else { - raceid = charmRuneGut; - } - break; - case CHARM_LOW: - if (set) { - charmRuneLowBlow = newRaceid; - } else { - raceid = charmRuneLowBlow; - } - break; - case CHARM_DIVINE: - if (set) { - charmRuneDivine = newRaceid; - } else { - raceid = charmRuneDivine; - } - break; - case CHARM_VAMP: - if (set) { - charmRuneVamp = newRaceid; - } else { - raceid = charmRuneVamp; - } - break; - case CHARM_VOID: - if (set) { - charmRuneVoid = newRaceid; - } else { - raceid = charmRuneVoid; - } - break; - default: - raceid = 0; - break; - } - return raceid; - } + uint16_t parseRacebyCharm(charmRune_t charmId, bool set, uint16_t newRaceid); uint64_t getItemCustomPrice(uint16_t itemId, bool buyPrice = false) const; uint16_t getFreeBackpackSlots() const; bool canAutoWalk(const Position &toPosition, const std::function &function, uint32_t delay = 500); - void sendMessageDialog(const std::string &message) const { - if (client) { - client->sendMessageDialog(message); - } - } + void sendMessageDialog(const std::string &message) const; // Account bool setAccount(uint32_t accountId); @@ -2131,227 +1043,62 @@ class Player final : public Creature, public Cylinder, public Bankable { void initializePrey(); void removePreySlotById(PreySlot_t slotid); - void sendPreyData() const { - if (client) { - for (const std::unique_ptr &slot : preys) { - client->sendPreyData(slot); - } + void sendPreyData() const; - client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards()); - } - } + void sendPreyTimeLeft(const std::unique_ptr &slot) const; - void sendPreyTimeLeft(const std::unique_ptr &slot) const { - if (g_configManager().getBoolean(PREY_ENABLED) && client) { - client->sendPreyTimeLeft(slot); - } - } + void reloadPreySlot(PreySlot_t slotid); - void reloadPreySlot(PreySlot_t slotid) { - if (g_configManager().getBoolean(PREY_ENABLED) && client) { - client->sendPreyData(getPreySlotById(slotid)); - client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints()); - } - } + const std::unique_ptr &getPreySlotById(PreySlot_t slotid); - const std::unique_ptr &getPreySlotById(PreySlot_t slotid) { - if (auto it = std::ranges::find_if(preys, [slotid](const std::unique_ptr &preyIt) { - return preyIt->id == slotid; - }); - it != preys.end()) { - return *it; - } + bool setPreySlotClass(std::unique_ptr &slot); - return PreySlotNull; - } - - bool setPreySlotClass(std::unique_ptr &slot) { - if (getPreySlotById(slot->id)) { - return false; - } - - preys.emplace_back(std::move(slot)); - return true; - } - - bool usePreyCards(uint16_t amount) { - if (preyCards < amount) { - return false; - } - - preyCards -= amount; - if (client) { - client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints()); - } - return true; - } + bool usePreyCards(uint16_t amount); - void addPreyCards(uint64_t amount) { - preyCards += amount; - if (client) { - client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints()); - } - } - - uint64_t getPreyCards() const { - return preyCards; - } - - uint32_t getPreyRerollPrice() const { - return getLevel() * g_configManager().getNumber(PREY_REROLL_PRICE_LEVEL); - } + void addPreyCards(uint64_t amount); - std::vector getPreyBlackList() const { - std::vector rt; - std::ranges::for_each(preys, [&rt](const std::unique_ptr &slot) { - if (slot) { - if (slot->isOccupied()) { - rt.emplace_back(slot->selectedRaceId); - } - std::ranges::for_each(slot->raceIdList, [&rt](uint16_t raceId) { - rt.emplace_back(raceId); - }); - } - }); - - return rt; - } + uint64_t getPreyCards() const; - const std::unique_ptr &getPreyWithMonster(uint16_t raceId) const { - if (!g_configManager().getBoolean(PREY_ENABLED)) { - return PreySlotNull; - } + uint32_t getPreyRerollPrice() const; - auto it = std::ranges::find_if(preys, [raceId](const std::unique_ptr &preySlot) { - return preySlot->selectedRaceId == raceId; - }); - - if (it != preys.end()) { - return *it; - } + std::vector getPreyBlackList() const; - return PreySlotNull; - } + const std::unique_ptr &getPreyWithMonster(uint16_t raceId) const; // Task hunting system void initializeTaskHunting(); bool isCreatureUnlockedOnTaskHunting(const std::shared_ptr &mtype) const; - bool setTaskHuntingSlotClass(std::unique_ptr &slot) { - if (getTaskHuntingSlotById(slot->id)) { - return false; - } - - taskHunting.emplace_back(std::move(slot)); - return true; - } - - void reloadTaskSlot(PreySlot_t slotid) { - if (g_configManager().getBoolean(TASK_HUNTING_ENABLED) && client) { - client->sendTaskHuntingData(getTaskHuntingSlotById(slotid)); - client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints()); - } - } - - const std::unique_ptr &getTaskHuntingSlotById(PreySlot_t slotid) { - if (auto it = std::ranges::find_if(taskHunting, [slotid](const std::unique_ptr &itTask) { - return itTask->id == slotid; - }); - it != taskHunting.end()) { - return *it; - } - - return TaskHuntingSlotNull; - } - - std::vector getTaskHuntingBlackList() const { - std::vector rt; - - std::ranges::for_each(taskHunting, [&rt](const std::unique_ptr &slot) { - if (slot->isOccupied()) { - rt.emplace_back(slot->selectedRaceId); - } else { - std::ranges::for_each(slot->raceIdList, [&rt](uint16_t raceId) { - rt.emplace_back(raceId); - }); - } - }); - - return rt; - } + bool setTaskHuntingSlotClass(std::unique_ptr &slot); - void sendTaskHuntingData() const { - if (client) { - client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints()); - for (const std::unique_ptr &slot : taskHunting) { - if (slot) { - client->sendTaskHuntingData(slot); - } - } - } - } + void reloadTaskSlot(PreySlot_t slotid); - void addTaskHuntingPoints(uint64_t amount) { - taskHuntingPoints += amount; - if (client) { - client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints()); - } - } + const std::unique_ptr &getTaskHuntingSlotById(PreySlot_t slotid); - bool useTaskHuntingPoints(uint64_t amount) { - if (taskHuntingPoints < amount) { - return false; - } + std::vector getTaskHuntingBlackList() const; - taskHuntingPoints -= amount; - if (client) { - client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints()); - } - return true; - } + void sendTaskHuntingData() const; - uint64_t getTaskHuntingPoints() const { - return taskHuntingPoints; - } + void addTaskHuntingPoints(uint64_t amount); - uint32_t getTaskHuntingRerollPrice() const { - return getLevel() * g_configManager().getNumber(TASK_HUNTING_REROLL_PRICE_LEVEL); - } + bool useTaskHuntingPoints(uint64_t amount); - const std::unique_ptr &getTaskHuntingWithCreature(uint16_t raceId) const { - if (!g_configManager().getBoolean(TASK_HUNTING_ENABLED)) { - return TaskHuntingSlotNull; - } + uint64_t getTaskHuntingPoints() const; - if (auto it = std::ranges::find_if(taskHunting, [raceId](const std::unique_ptr &itTask) { - return itTask->selectedRaceId == raceId; - }); - it != taskHunting.end()) { - return *it; - } + uint32_t getTaskHuntingRerollPrice() const; - return TaskHuntingSlotNull; - } + const std::unique_ptr &getTaskHuntingWithCreature(uint16_t raceId) const; - uint32_t getLoyaltyPoints() const { - return loyaltyPoints; - } + uint32_t getLoyaltyPoints() const; - void setLoyaltyBonus(uint16_t bonus) { - loyaltyBonusPercent = bonus; - sendSkills(); - } - void setLoyaltyTitle(std::string title) { - loyaltyTitle = std::move(title); - } - std::string getLoyaltyTitle() const { - return loyaltyTitle; - } - uint16_t getLoyaltyBonus() const { - return loyaltyBonusPercent; - } + void setLoyaltyBonus(uint16_t bonus); + void setLoyaltyTitle(std::string title); + std::string getLoyaltyTitle() const; + uint16_t getLoyaltyBonus() const; - // Depot search system + /******************************************************************************* + * Depot search system + ******************************************************************************/ void requestDepotItems(); void requestDepotSearchItem(uint16_t itemId, uint8_t tier); void retrieveAllItemsFromDepotSearch(uint16_t itemId, uint8_t tier, bool isDepot); @@ -2388,143 +1135,45 @@ class Player final : public Creature, public Cylinder, public Bankable { void forgeResourceConversion(ForgeAction_t actionType); void forgeHistory(uint8_t page) const; - void sendOpenForge() const { - if (client) { - client->sendOpenForge(); - } - } - void sendForgeError(ReturnValue returnValue) const { - if (client) { - client->sendForgeError(returnValue); - } - } - void sendForgeResult(ForgeAction_t actionType, uint16_t leftItemId, uint8_t leftTier, uint16_t rightItemId, uint8_t rightTier, bool success, uint8_t bonus, uint8_t coreCount, bool convergence) const { - if (client) { - client->sendForgeResult(actionType, leftItemId, leftTier, rightItemId, rightTier, success, bonus, coreCount, convergence); - } - } - void sendForgeHistory(uint8_t page) const { - if (client) { - client->sendForgeHistory(page); - } - } - void closeForgeWindow() const { - if (client) { - client->closeForgeWindow(); - } - } + void sendOpenForge() const; + void sendForgeError(ReturnValue returnValue) const; + void sendForgeResult(ForgeAction_t actionType, uint16_t leftItemId, uint8_t leftTier, uint16_t rightItemId, uint8_t rightTier, bool success, uint8_t bonus, uint8_t coreCount, bool convergence) const; + void sendForgeHistory(uint8_t page) const; + void closeForgeWindow() const; - void setForgeDusts(uint64_t amount) { - forgeDusts = amount; - if (client) { - client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints(), getForgeDusts()); - } - } - void addForgeDusts(uint64_t amount) { - forgeDusts += amount; - if (client) { - client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints(), getForgeDusts()); - } - } - void removeForgeDusts(uint64_t amount) { - forgeDusts = std::max(0, forgeDusts - amount); - if (client) { - client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints(), getForgeDusts()); - } - } - uint64_t getForgeDusts() const { - return forgeDusts; - } + void setForgeDusts(uint64_t amount); + void addForgeDusts(uint64_t amount); + void removeForgeDusts(uint64_t amount); + uint64_t getForgeDusts() const; - void addForgeDustLevel(uint64_t amount) { - forgeDustLevel += amount; - if (client) { - client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints(), getForgeDusts()); - } - } - void removeForgeDustLevel(uint64_t amount) { - forgeDustLevel = std::max(0, forgeDustLevel - amount); - if (client) { - client->sendResourcesBalance(getMoney(), getBankBalance(), getPreyCards(), getTaskHuntingPoints(), getForgeDusts()); - } - } - uint64_t getForgeDustLevel() const { - return forgeDustLevel; - } + void addForgeDustLevel(uint64_t amount); + void removeForgeDustLevel(uint64_t amount); + uint64_t getForgeDustLevel() const; - std::vector &getForgeHistory() { - return forgeHistoryVector; - } + std::vector &getForgeHistory(); - void setForgeHistory(const ForgeHistory &history) { - forgeHistoryVector.emplace_back(history); - } + void setForgeHistory(const ForgeHistory &history); void registerForgeHistoryDescription(ForgeHistory history); - void setBossPoints(uint32_t amount) { - bossPoints = amount; - } - void addBossPoints(uint32_t amount) { - bossPoints += amount; - } - void removeBossPoints(uint32_t amount) { - bossPoints = std::max(0, bossPoints - amount); - } - uint32_t getBossPoints() const { - return bossPoints; - } - void sendBosstiaryCooldownTimer() const { - if (client) { - client->sendBosstiaryCooldownTimer(); - } - } + void setBossPoints(uint32_t amount); + void addBossPoints(uint32_t amount); + void removeBossPoints(uint32_t amount); + uint32_t getBossPoints() const; + void sendBosstiaryCooldownTimer() const; - void setSlotBossId(uint8_t slotId, uint32_t bossId) { - if (slotId == 1) { - bossIdSlotOne = bossId; - } else { - bossIdSlotTwo = bossId; - } - if (client) { - client->parseSendBosstiarySlots(); - } - } - uint32_t getSlotBossId(uint8_t slotId) const { - if (slotId == 1) { - return bossIdSlotOne; - } else { - return bossIdSlotTwo; - } - } + void setSlotBossId(uint8_t slotId, uint32_t bossId); + uint32_t getSlotBossId(uint8_t slotId) const; - void addRemoveTime() { - bossRemoveTimes = bossRemoveTimes + 1; - } - void setRemoveBossTime(uint8_t newRemoveTimes) { - bossRemoveTimes = newRemoveTimes; - } - uint8_t getRemoveTimes() const { - return bossRemoveTimes; - } + void addRemoveTime(); + void setRemoveBossTime(uint8_t newRemoveTimes); + uint8_t getRemoveTimes() const; - void sendMonsterPodiumWindow(const std::shared_ptr &podium, const Position &position, uint16_t itemId, uint8_t stackpos) const { - if (client) { - client->sendMonsterPodiumWindow(podium, position, itemId, stackpos); - } - } + void sendMonsterPodiumWindow(const std::shared_ptr &podium, const Position &position, uint16_t itemId, uint8_t stackpos) const; - void sendBosstiaryEntryChanged(uint32_t bossid) const { - if (client) { - client->sendBosstiaryEntryChanged(bossid); - } - } + void sendBosstiaryEntryChanged(uint32_t bossid) const; - void sendInventoryImbuements(const std::map> &items) const { - if (client) { - client->sendInventoryImbuements(items); - } - } + void sendInventoryImbuements(const std::map> &items) const; /******************************************************************************* * Hazard system @@ -2535,58 +1184,18 @@ class Player final : public Creature, public Cylinder, public Bankable { // Points increase: void setHazardSystemPoints(int32_t amount); // Points get: - uint16_t getHazardSystemPoints() const { - const int32_t points = getStorageValue(STORAGEVALUE_HAZARDCOUNT); - if (points <= 0) { - return 0; - } - return static_cast(std::max(0, std::min(0xFFFF, points))); - } + uint16_t getHazardSystemPoints() const; /*******************************************************************************/ // Concoction system - void updateConcoction(uint16_t itemId, uint16_t timeLeft) { - if (timeLeft == 0) { - activeConcoctions.erase(itemId); - } else { - activeConcoctions[itemId] = timeLeft; - } - } - std::map getActiveConcoctions() const { - return activeConcoctions; - } - bool isConcoctionActive(Concoction_t concotion) const { - const auto itemId = static_cast(concotion); - if (!activeConcoctions.contains(itemId)) { - return false; - } - const auto timeLeft = activeConcoctions.at(itemId); - return timeLeft > 0; - } + void updateConcoction(uint16_t itemId, uint16_t timeLeft); + std::map getActiveConcoctions() const; + bool isConcoctionActive(Concoction_t concotion) const; - bool checkAutoLoot(bool isBoss) const { - if (!g_configManager().getBoolean(AUTOLOOT)) { - return false; - } - if (g_configManager().getBoolean(VIP_SYSTEM_ENABLED) && g_configManager().getBoolean(VIP_AUTOLOOT_VIP_ONLY) && !isVip()) { - return false; - } - - const auto featureKV = kv()->scoped("features")->get("autoloot"); - const auto value = featureKV.has_value() ? featureKV->getNumber() : 0; - if (value == 2) { - return true; - } - if (value == 1) { - return !isBoss; - } - return false; - } + bool checkAutoLoot(bool isBoss) const; - QuickLootFilter_t getQuickLootFilter() const { - return quickLootFilter; - } + QuickLootFilter_t getQuickLootFilter() const; // Get specific inventory item from itemid std::vector> getInventoryItemsFromId(uint16_t itemId, bool ignore = true) const; @@ -2735,12 +1344,6 @@ class Player final : public Creature, public Cylinder, public Bankable { std::map storageMap; std::map itemPriceMap; - std::map maxValuePerSkill = { - { SKILL_LIFE_LEECH_CHANCE, 100 }, - { SKILL_MANA_LEECH_CHANCE, 100 }, - { SKILL_CRITICAL_HIT_CHANCE, 100 * g_configManager().getNumber(CRITICALCHANCE) } - }; - std::map> rewardMap; std::map, std::shared_ptr>> m_managedContainers; @@ -2820,7 +1423,7 @@ class Player final : public Creature, public Cylinder, public Bankable { std::shared_ptr shopOwner = nullptr; std::shared_ptr m_party = nullptr; std::shared_ptr tradePartner = nullptr; - ProtocolGame_ptr client; + std::shared_ptr client = nullptr; std::shared_ptr walkTask; std::shared_ptr town; std::shared_ptr vocation = nullptr; @@ -2964,37 +1567,11 @@ class Player final : public Creature, public Cylinder, public Bankable { uint16_t getStepSpeed() const override { return std::max(PLAYER_MIN_SPEED, std::min(PLAYER_MAX_SPEED, getSpeed())); } - void updateBaseSpeed() { - if (baseSpeed >= PLAYER_MAX_SPEED) { - return; - } - - if (!hasFlag(PlayerFlags_t::SetMaxSpeed)) { - baseSpeed = static_cast(vocation->getBaseSpeed() + (level - 1)); - } else { - baseSpeed = PLAYER_MAX_SPEED; - } - } + void updateBaseSpeed(); bool isPromoted() const; - uint32_t getAttackSpeed() const { - bool onFistAttackSpeed = g_configManager().getBoolean(TOGGLE_ATTACK_SPEED_ONFIST); - uint32_t MAX_ATTACK_SPEED = g_configManager().getNumber(MAX_SPEED_ATTACKONFIST); - if (onFistAttackSpeed) { - uint32_t baseAttackSpeed = vocation->getAttackSpeed(); - uint32_t skillLevel = getSkillLevel(SKILL_FIST); - uint32_t attackSpeed = baseAttackSpeed - (skillLevel * g_configManager().getNumber(MULTIPLIER_ATTACKONFIST)); - - if (attackSpeed < MAX_ATTACK_SPEED) { - attackSpeed = MAX_ATTACK_SPEED; - } - - return static_cast(attackSpeed); - } else { - return vocation->getAttackSpeed(); - } - } + uint32_t getAttackSpeed() const; static double_t getPercentLevel(uint64_t count, uint64_t nextLevelCount); double getLostPercent() const; @@ -3003,7 +1580,6 @@ class Player final : public Creature, public Cylinder, public Bankable { } bool isSuppress(ConditionType_t conditionType, bool attackerPlayer) const override; - void addConditionSuppression(const std::array &addConditions); uint16_t getLookCorpse() const override; void getPathSearchParams(const std::shared_ptr &creature, FindPathParams &fpp) override; diff --git a/src/creatures/players/storages/storages.cpp b/src/creatures/players/storages/storages.cpp index e3fd7e13ced..64aab5e44d5 100644 --- a/src/creatures/players/storages/storages.cpp +++ b/src/creatures/players/storages/storages.cpp @@ -8,8 +8,8 @@ */ #include "creatures/players/storages/storages.hpp" + #include "config/configmanager.hpp" -#include "config/config_enums.hpp" #include "lib/di/container.hpp" Storages &Storages::getInstance() { diff --git a/src/creatures/players/vip/player_vip.cpp b/src/creatures/players/vip/player_vip.cpp index dd49429ba2c..195480ca5fd 100644 --- a/src/creatures/players/vip/player_vip.cpp +++ b/src/creatures/players/vip/player_vip.cpp @@ -9,10 +9,10 @@ #include "creatures/players/vip/player_vip.hpp" -#include "io/iologindata.hpp" - -#include "game/game.hpp" +#include "creatures/players/grouping/groups.hpp" #include "creatures/players/player.hpp" +#include "io/iologindata.hpp" +#include "server/network/protocol/protocolgame.hpp" const uint8_t PlayerVIP::firstID = 1; const uint8_t PlayerVIP::lastID = 8; diff --git a/src/creatures/players/vocations/vocation.cpp b/src/creatures/players/vocations/vocation.cpp index 1e7fb083800..f36494895f5 100644 --- a/src/creatures/players/vocations/vocation.cpp +++ b/src/creatures/players/vocations/vocation.cpp @@ -10,23 +10,21 @@ #include "creatures/players/vocations/vocation.hpp" #include "config/configmanager.hpp" -#include "lib/di/container.hpp" #include "items/item.hpp" +#include "lib/di/container.hpp" #include "utils/pugicast.hpp" #include "utils/tools.hpp" #include "enums/player_wheel.hpp" -#include - -Vocations &Vocations::getInstance() { - return inject(); -} - bool Vocations::reload() { vocationsMap.clear(); return loadFromXml(); } +Vocations &Vocations::getInstance() { + return inject(); +} + bool Vocations::loadFromXml() { pugi::xml_document doc; auto folder = g_configManager().getString(CORE_DIRECTORY) + "/XML/vocations.xml"; @@ -212,6 +210,11 @@ std::shared_ptr Vocations::getVocation(uint16_t id) { } return it->second; } + +const std::map> &Vocations::getVocations() const { + return vocationsMap; +} + uint16_t Vocations::getVocationId(const std::string &name) const { for (const auto &[vocationId, vocationPtr] : vocationsMap) { if (caseInsensitiveCompare(vocationPtr->name, name)) { @@ -233,6 +236,14 @@ uint16_t Vocations::getPromotedVocation(uint16_t vocationId) const { uint32_t Vocation::skillBase[SKILL_LAST + 1] = { 50, 50, 50, 50, 30, 100, 20 }; constexpr uint16_t minSkillLevel = 10; +const std::string &Vocation::getVocName() const { + return name; +} + +const std::string &Vocation::getVocDescription() const { + return description; +} + absl::uint128 Vocation::getTotalSkillTries(uint8_t skill, uint16_t level) { if (skill > SKILL_LAST) { return 0; @@ -295,6 +306,34 @@ uint64_t Vocation::getReqMana(uint32_t magLevel) { return reqMana; } +uint16_t Vocation::getId() const { + return id; +} + +uint8_t Vocation::getClientId() const { + return clientId; +} + +uint8_t Vocation::getBaseId() const { + return baseId; +} + +uint16_t Vocation::getAvatarLookType() const { + return avatarLookType; +} + +uint32_t Vocation::getHPGain() const { + return gainHP; +} + +uint32_t Vocation::getManaGain() const { + return gainMana; +} + +uint32_t Vocation::getCapGain() const { + return gainCap; +} + uint32_t Vocation::getManaGainTicks() const { return gainManaTicks / g_configManager().getFloat(RATE_MANA_REGEN_SPEED); } @@ -311,19 +350,37 @@ uint32_t Vocation::getHealthGainAmount() const { return gainHealthAmount * g_configManager().getFloat(RATE_HEALTH_REGEN); } +uint8_t Vocation::getSoulMax() const { + return soulMax; +} + uint32_t Vocation::getSoulGainTicks() const { return gainSoulTicks / g_configManager().getFloat(RATE_SOUL_REGEN_SPEED); } +uint32_t Vocation::getBaseAttackSpeed() const { + return attackSpeed; +} + uint32_t Vocation::getAttackSpeed() const { return attackSpeed / g_configManager().getFloat(RATE_ATTACK_SPEED); } -template <> -struct magic_enum::customize::enum_range { - static constexpr int min = 0; - static constexpr int max = static_cast(WheelGemSupremeModifier_t::Druid_RevelationMastery_TwinBursts); -}; +uint32_t Vocation::getBaseSpeed() const { + return baseSpeed; +} + +uint32_t Vocation::getFromVocation() const { + return fromVocation; +} + +bool Vocation::getMagicShield() const { + return magicShield; +} + +bool Vocation::canCombat() const { + return combat; +} std::vector Vocation::getSupremeGemModifiers() { if (!m_supremeGemModifiers.empty()) { diff --git a/src/creatures/players/vocations/vocation.hpp b/src/creatures/players/vocations/vocation.hpp index 07127af6fec..21a4ea79710 100644 --- a/src/creatures/players/vocations/vocation.hpp +++ b/src/creatures/players/vocations/vocation.hpp @@ -19,42 +19,24 @@ class Vocation { explicit Vocation(uint16_t initId) : id(initId) { } - const std::string &getVocName() const { - return name; - } - const std::string &getVocDescription() const { - return description; - } + const std::string &getVocName() const; + const std::string &getVocDescription() const; absl::uint128 getTotalSkillTries(uint8_t skill, uint16_t level); uint64_t getReqSkillTries(uint8_t skill, uint16_t level); absl::uint128 getTotalMana(uint32_t magLevel); uint64_t getReqMana(uint32_t magLevel); - uint16_t getId() const { - return id; - } + uint16_t getId() const; - uint8_t getClientId() const { - return clientId; - } + uint8_t getClientId() const; - uint8_t getBaseId() const { - return baseId; - } + uint8_t getBaseId() const; - uint16_t getAvatarLookType() const { - return avatarLookType; - } + uint16_t getAvatarLookType() const; - uint32_t getHPGain() const { - return gainHP; - } - uint32_t getManaGain() const { - return gainMana; - } - uint32_t getCapGain() const { - return gainCap; - } + uint32_t getHPGain() const; + uint32_t getManaGain() const; + uint32_t getCapGain() const; uint32_t getManaGainTicks() const; @@ -64,32 +46,20 @@ class Vocation { uint32_t getHealthGainAmount() const; - uint8_t getSoulMax() const { - return soulMax; - } + uint8_t getSoulMax() const; uint32_t getSoulGainTicks() const; - uint32_t getBaseAttackSpeed() const { - return attackSpeed; - } + uint32_t getBaseAttackSpeed() const; uint32_t getAttackSpeed() const; - uint32_t getBaseSpeed() const { - return baseSpeed; - } + uint32_t getBaseSpeed() const; - uint32_t getFromVocation() const { - return fromVocation; - } + uint32_t getFromVocation() const; - bool getMagicShield() const { - return magicShield; - } - bool canCombat() const { - return combat; - } + bool getMagicShield() const; + bool canCombat() const; float meleeDamageMultiplier = 1.0f; float distDamageMultiplier = 1.0f; @@ -162,9 +132,7 @@ class Vocations { bool reload(); std::shared_ptr getVocation(uint16_t id); - const std::map> &getVocations() const { - return vocationsMap; - } + const std::map> &getVocations() const; uint16_t getVocationId(const std::string &name) const; uint16_t getPromotedVocation(uint16_t vocationId) const; diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp index 344bde5f03b..6d3ee2713a7 100644 --- a/src/creatures/players/wheel/player_wheel.cpp +++ b/src/creatures/players/wheel/player_wheel.cpp @@ -9,15 +9,21 @@ #include "creatures/players/wheel/player_wheel.hpp" -#include "enums/player_wheel.hpp" #include "config/configmanager.hpp" -#include "io/io_wheel.hpp" -#include "game/game.hpp" -#include "server/network/message/networkmessage.hpp" -#include "creatures/players/player.hpp" +#include "creatures/combat/condition.hpp" #include "creatures/combat/spells.hpp" -#include "kv/kv.hpp" +#include "creatures/players/player.hpp" +#include "creatures/players/vocations/vocation.hpp" #include "creatures/players/wheel/wheel_gems.hpp" +#include "enums/player_wheel.hpp" +#include "game/game.hpp" +#include "io/io_wheel.hpp" +#include "kv/kv.hpp" +#include "kv/kv_definitions.hpp" +#include "server/network/message/networkmessage.hpp" +#include "server/network/protocol/protocolgame.hpp" + +std::array m_resistance = { 0 }; const static std::vector wheelGemBasicSlot1Allowed = { WheelGemBasicModifier_t::General_FireResistance, diff --git a/src/creatures/players/wheel/player_wheel.hpp b/src/creatures/players/wheel/player_wheel.hpp index 82730b76881..840cc72d8dc 100644 --- a/src/creatures/players/wheel/player_wheel.hpp +++ b/src/creatures/players/wheel/player_wheel.hpp @@ -9,6 +9,7 @@ #pragma once +#include "creatures/creatures_definitions.hpp" #include "creatures/players/wheel/wheel_definitions.hpp" class Creature; @@ -20,11 +21,16 @@ class Spell; class WheelModifierContext; class ValueWrapper; +struct CombatDamage; + enum class WheelFragmentType_t : uint8_t; enum class WheelGemAffinity_t : uint8_t; enum class WheelGemBasicModifier_t : uint8_t; enum class WheelGemQuality_t : uint8_t; enum class WheelGemSupremeModifier_t : uint8_t; +enum CombatType_t : uint8_t; +enum skills_t : int8_t; +enum Vocation_t : uint16_t; struct PlayerWheelGem { std::string uuid; diff --git a/src/creatures/players/wheel/wheel_definitions.hpp b/src/creatures/players/wheel/wheel_definitions.hpp index 5651b8dc216..0aa22c5a7b9 100644 --- a/src/creatures/players/wheel/wheel_definitions.hpp +++ b/src/creatures/players/wheel/wheel_definitions.hpp @@ -9,8 +9,6 @@ #pragma once -#include "creatures/creatures_definitions.hpp" - enum WheelSlots_t : uint8_t { SLOT_GREEN_200 = 1, SLOT_GREEN_TOP_150 = 2, diff --git a/src/creatures/players/wheel/wheel_gems.cpp b/src/creatures/players/wheel/wheel_gems.cpp index 3ee884c077b..a2c7a898766 100644 --- a/src/creatures/players/wheel/wheel_gems.cpp +++ b/src/creatures/players/wheel/wheel_gems.cpp @@ -9,6 +9,7 @@ #include "creatures/players/wheel/wheel_gems.hpp" +#include "creatures/creatures_definitions.hpp" #include "creatures/players/wheel/player_wheel.hpp" #include "enums/player_wheel.hpp" diff --git a/src/database/database.cpp b/src/database/database.cpp index 5b0c55fbc29..fcb7371708d 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -7,8 +7,9 @@ * Website: https://docs.opentibiabr.com/ */ -#include "config/configmanager.hpp" #include "database/database.hpp" + +#include "config/configmanager.hpp" #include "lib/di/container.hpp" #include "lib/metrics/metrics.hpp" diff --git a/src/database/databasemanager.cpp b/src/database/databasemanager.cpp index 3d2d9c9fd64..2941172f35a 100644 --- a/src/database/databasemanager.cpp +++ b/src/database/databasemanager.cpp @@ -7,8 +7,9 @@ * Website: https://docs.opentibiabr.com/ */ -#include "config/configmanager.hpp" #include "database/databasemanager.hpp" + +#include "config/configmanager.hpp" #include "lua/functions/core/libs/core_libs_functions.hpp" #include "lua/scripts/luascript.hpp" diff --git a/src/database/databasetasks.cpp b/src/database/databasetasks.cpp index 69718ec7c53..afc4c3608f3 100644 --- a/src/database/databasetasks.cpp +++ b/src/database/databasetasks.cpp @@ -8,6 +8,7 @@ */ #include "database/databasetasks.hpp" + #include "game/scheduling/dispatcher.hpp" #include "lib/thread/thread_pool.hpp" #include "lib/di/container.hpp" diff --git a/src/game/bank/bank.cpp b/src/game/bank/bank.cpp index 50f79c6d494..c60852fe122 100644 --- a/src/game/bank/bank.cpp +++ b/src/game/bank/bank.cpp @@ -7,10 +7,11 @@ * Website: https://docs.opentibiabr.com/ */ -#include "bank.hpp" -#include "game/game.hpp" +#include "game/bank/bank.hpp" + +#include "config/configmanager.hpp" #include "creatures/players/player.hpp" -#include "io/iologindata.hpp" +#include "game/game.hpp" #include "game/scheduling/save_manager.hpp" #include "lib/metrics/metrics.hpp" diff --git a/src/game/functions/game_reload.cpp b/src/game/functions/game_reload.cpp index 2b699ce8df3..10f20595042 100644 --- a/src/game/functions/game_reload.cpp +++ b/src/game/functions/game_reload.cpp @@ -10,16 +10,26 @@ #include "game/functions/game_reload.hpp" #include "config/configmanager.hpp" -#include "lua/creature/events.hpp" +#include "creatures/appearance/mounts/mounts.hpp" +#include "creatures/interactions/chat.hpp" +#include "creatures/monsters/monsters.hpp" +#include "creatures/npcs/npcs.hpp" #include "creatures/players/imbuements/imbuements.hpp" -#include "lua/scripts/lua_environment.hpp" +#include "game/game.hpp" +#include "game/zones/zone.hpp" +#include "lib/di/container.hpp" +#include "lua/creature/events.hpp" #include "lua/modules/modules.hpp" +#include "lua/scripts/lua_environment.hpp" #include "lua/scripts/scripts.hpp" -#include "game/zones/zone.hpp" GameReload::GameReload() = default; GameReload::~GameReload() = default; +GameReload &GameReload::getInstance() { + return inject(); +} + bool GameReload::init(Reload_t reloadTypes) { switch (reloadTypes) { case Reload_t::RELOAD_TYPE_ALL: @@ -126,7 +136,7 @@ bool GameReload::reloadOutfits() { } bool GameReload::reloadMounts() { - const bool result = g_game().mounts.reload(); + const bool result = g_game().mounts->reload(); logReloadStatus("Mounts", result); return result; } diff --git a/src/game/functions/game_reload.hpp b/src/game/functions/game_reload.hpp index 0f59046a9d4..4ef141c10aa 100644 --- a/src/game/functions/game_reload.hpp +++ b/src/game/functions/game_reload.hpp @@ -9,10 +9,6 @@ #pragma once -#include "game/game.hpp" - -class Game; - enum class Reload_t : uint8_t { RELOAD_TYPE_NONE, RELOAD_TYPE_ALL, @@ -37,7 +33,7 @@ enum class Reload_t : uint8_t { RELOAD_TYPE_LAST }; -class GameReload : public Game { +class GameReload { public: GameReload(); ~GameReload(); @@ -46,9 +42,7 @@ class GameReload : public Game { GameReload(const GameReload &) = delete; GameReload &operator=(const GameReload &) = delete; - static GameReload &getInstance() { - return inject(); - } + static GameReload &getInstance(); static bool init(Reload_t reloadType); static uint8_t getReloadNumber(Reload_t reloadTypes); diff --git a/src/game/game.cpp b/src/game/game.cpp index b4ce04f7644..4018c1b2f90 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -9,53 +9,70 @@ #include "game/game.hpp" -#include "lua/creature/actions.hpp" -#include "items/bed.hpp" +#include "config/configmanager.hpp" +#include "creatures/appearance/mounts/mounts.hpp" +#include "creatures/combat/condition.hpp" +#include "creatures/combat/spells.hpp" #include "creatures/creature.hpp" -#include "database/databasetasks.hpp" -#include "lua/creature/events.hpp" -#include "lua/callbacks/event_callback.hpp" -#include "lua/callbacks/events_callbacks.hpp" +#include "creatures/interactions/chat.hpp" +#include "creatures/monsters/monster.hpp" +#include "creatures/monsters/monsters.hpp" +#include "creatures/npcs/npc.hpp" +#include "creatures/players/achievement/player_achievement.hpp" +#include "creatures/players/cyclopedia/player_badge.hpp" +#include "creatures/players/cyclopedia/player_cyclopedia.hpp" +#include "creatures/players/grouping/party.hpp" +#include "creatures/players/grouping/team_finder.hpp" #include "creatures/players/highscore_category.hpp" +#include "creatures/players/imbuements/imbuements.hpp" +#include "creatures/players/player.hpp" +#include "creatures/players/vip/player_vip.hpp" +#include "creatures/players/wheel/player_wheel.hpp" +#include "enums/player_wheel.hpp" +#include "database/databasetasks.hpp" +#include "game/scheduling/dispatcher.hpp" +#include "game/scheduling/save_manager.hpp" #include "game/zones/zone.hpp" -#include "lua/global/globalevent.hpp" -#include "io/iologindata.hpp" +#include "io/io_bosstiary.hpp" #include "io/io_wheel.hpp" +#include "io/iobestiary.hpp" +#include "io/ioguild.hpp" +#include "io/iologindata.hpp" #include "io/iomarket.hpp" +#include "io/ioprey.hpp" +#include "items/bed.hpp" +#include "items/containers/inbox/inbox.hpp" +#include "items/containers/rewards/reward.hpp" +#include "items/containers/rewards/rewardchest.hpp" #include "items/items.hpp" -#include "lua/scripts/lua_environment.hpp" -#include "creatures/monsters/monster.hpp" -#include "lua/creature/movement.hpp" -#include "game/scheduling/dispatcher.hpp" -#include "game/scheduling/save_manager.hpp" -#include "server/server.hpp" -#include "creatures/combat/spells.hpp" +#include "items/items_classification.hpp" +#include "lua/callbacks/event_callback.hpp" +#include "lua/callbacks/events_callbacks.hpp" +#include "lua/creature/actions.hpp" +#include "lua/creature/creatureevent.hpp" +#include "lua/creature/events.hpp" #include "lua/creature/talkaction.hpp" -#include "items/weapons/weapons.hpp" -#include "creatures/players/imbuements/imbuements.hpp" -#include "creatures/players/wheel/player_wheel.hpp" -#include "enums/player_wheel.hpp" -#include "creatures/players/achievement/player_achievement.hpp" -#include "creatures/players/cyclopedia/player_badge.hpp" -#include "creatures/players/cyclopedia/player_cyclopedia.hpp" -#include "creatures/players/cyclopedia/player_title.hpp" -#include "creatures/npcs/npc.hpp" -#include "server/network/webhook/webhook.hpp" +#include "lua/global/globalevent.hpp" +#include "lua/scripts/lua_environment.hpp" +#include "map/spectators.hpp" #include "server/network/protocol/protocollogin.hpp" #include "server/network/protocol/protocolstatus.hpp" -#include "map/spectators.hpp" +#include "server/network/protocol/protocolgame.hpp" +#include "server/network/webhook/webhook.hpp" +#include "server/server.hpp" #include "utils/tools.hpp" -#include "kv/kv.hpp" +#include "utils/wildcardtree.hpp" -#include "enums/object_category.hpp" -#include "enums/account_type.hpp" -#include "enums/account_group_type.hpp" -#include "enums/account_errors.hpp" #include "enums/account_coins.hpp" -#include "enums/player_blessings.hpp" +#include "enums/account_errors.hpp" +#include "enums/account_group_type.hpp" +#include "enums/account_type.hpp" +#include "enums/object_category.hpp" #include +std::vector> checkCreatureLists[EVENT_CREATURECOUNT]; + namespace InternalGame { void sendBlockEffect(BlockType_t blockType, CombatType_t combatType, const Position &targetPos, const std::shared_ptr &source) { if (blockType == BLOCK_DEFENSE) { @@ -199,156 +216,162 @@ Game::Game() { wildcardTree = std::make_shared(false); + mounts = std::make_unique(); + + using enum CyclopediaBadge_t; + using enum CyclopediaTitle_t; + using enum HighscoreCategories_t; + using enum BestiaryType_t; m_badges = { - Badge(1, CyclopediaBadge_t::ACCOUNT_AGE, "Fledegeling Hero", 1), - Badge(2, CyclopediaBadge_t::ACCOUNT_AGE, "Veteran Hero", 5), - Badge(3, CyclopediaBadge_t::ACCOUNT_AGE, "Senior Hero", 10), - Badge(4, CyclopediaBadge_t::ACCOUNT_AGE, "Ancient Hero", 15), - Badge(5, CyclopediaBadge_t::ACCOUNT_AGE, "Exalted Hero", 20), - - Badge(6, CyclopediaBadge_t::LOYALTY, "Tibia Loyalist (Grade 1)", 100), - Badge(7, CyclopediaBadge_t::LOYALTY, "Tibia Loyalist (Grade 2)", 1000), - Badge(8, CyclopediaBadge_t::LOYALTY, "Tibia Loyalist (Grade 3)", 5000), - - Badge(9, CyclopediaBadge_t::ACCOUNT_ALL_LEVEL, "Global Player (Grade 1)", 500), - Badge(10, CyclopediaBadge_t::ACCOUNT_ALL_LEVEL, "Global Player (Grade 2)", 1000), - Badge(11, CyclopediaBadge_t::ACCOUNT_ALL_LEVEL, "Global Player (Grade 3)", 2000), - - Badge(12, CyclopediaBadge_t::ACCOUNT_ALL_VOCATIONS, "Master Class (Grade 1)", 100), - Badge(13, CyclopediaBadge_t::ACCOUNT_ALL_VOCATIONS, "Master Class (Grade 2)", 250), - Badge(14, CyclopediaBadge_t::ACCOUNT_ALL_VOCATIONS, "Master Class (Grade 3)", 500), - - Badge(15, CyclopediaBadge_t::TOURNAMENT_PARTICIPATION, "Freshman of the Tournament", 1), - Badge(16, CyclopediaBadge_t::TOURNAMENT_PARTICIPATION, "Regular of the Tournament", 5), - Badge(17, CyclopediaBadge_t::TOURNAMENT_PARTICIPATION, "Hero of the Tournament", 10), - - Badge(18, CyclopediaBadge_t::TOURNAMENT_POINTS, "Tournament Competitor", 1000), - Badge(19, CyclopediaBadge_t::TOURNAMENT_POINTS, "Tournament Challenger", 2500), - Badge(20, CyclopediaBadge_t::TOURNAMENT_POINTS, "Tournament Master", 5000), - Badge(21, CyclopediaBadge_t::TOURNAMENT_POINTS, "Tournament Champion", 10000), + Badge(1, ACCOUNT_AGE, "Fledegeling Hero", 1), + Badge(2, ACCOUNT_AGE, "Veteran Hero", 5), + Badge(3, ACCOUNT_AGE, "Senior Hero", 10), + Badge(4, ACCOUNT_AGE, "Ancient Hero", 15), + Badge(5, ACCOUNT_AGE, "Exalted Hero", 20), + + Badge(6, LOYALTY, "Tibia Loyalist (Grade 1)", 100), + Badge(7, LOYALTY, "Tibia Loyalist (Grade 2)", 1000), + Badge(8, LOYALTY, "Tibia Loyalist (Grade 3)", 5000), + + Badge(9, ACCOUNT_ALL_LEVEL, "Global Player (Grade 1)", 500), + Badge(10, ACCOUNT_ALL_LEVEL, "Global Player (Grade 2)", 1000), + Badge(11, ACCOUNT_ALL_LEVEL, "Global Player (Grade 3)", 2000), + + Badge(12, ACCOUNT_ALL_VOCATIONS, "Master Class (Grade 1)", 100), + Badge(13, ACCOUNT_ALL_VOCATIONS, "Master Class (Grade 2)", 250), + Badge(14, ACCOUNT_ALL_VOCATIONS, "Master Class (Grade 3)", 500), + + Badge(15, TOURNAMENT_PARTICIPATION, "Freshman of the Tournament", 1), + Badge(16, TOURNAMENT_PARTICIPATION, "Regular of the Tournament", 5), + Badge(17, TOURNAMENT_PARTICIPATION, "Hero of the Tournament", 10), + + Badge(18, TOURNAMENT_POINTS, "Tournament Competitor", 1000), + Badge(19, TOURNAMENT_POINTS, "Tournament Challenger", 2500), + Badge(20, TOURNAMENT_POINTS, "Tournament Master", 5000), + Badge(21, TOURNAMENT_POINTS, "Tournament Champion", 10000), }; m_titles = { - Title(1, CyclopediaTitle_t::GOLD, "Gold Hoarder", "Earned at least 1,000,000 gold.", 1000000, false), - Title(2, CyclopediaTitle_t::GOLD, "Platinum Hoarder", "Earned at least 10,000,000 gold.", 10000000, false), - Title(3, CyclopediaTitle_t::GOLD, "Crystal Hoarder", "Earned at least 100,000,000 gold.", 100000000, false), - - Title(4, CyclopediaTitle_t::MOUNTS, "Beaststrider (Grade 1)", "Unlocked 10 or more Mounts.", 10, true), - Title(5, CyclopediaTitle_t::MOUNTS, "Beaststrider (Grade 2)", "Unlocked 20 or more Mounts.", 20, true), - Title(6, CyclopediaTitle_t::MOUNTS, "Beaststrider (Grade 3)", "Unlocked 30 or more Mounts.", 30, true), - Title(7, CyclopediaTitle_t::MOUNTS, "Beaststrider (Grade 4)", "Unlocked 40 or more Mounts.", 40, true), - Title(8, CyclopediaTitle_t::MOUNTS, "Beaststrider (Grade 5)", "Unlocked 50 or more Mounts.", 50, true), - - Title(9, CyclopediaTitle_t::OUTFITS, "Tibia's Topmodel (Grade 1)", "Unlocked 10 or more Outfits.", 10, true), - Title(10, CyclopediaTitle_t::OUTFITS, "Tibia's Topmodel (Grade 2)", "Unlocked 20 or more Outfits.", 20, true), - Title(11, CyclopediaTitle_t::OUTFITS, "Tibia's Topmodel (Grade 3)", "Unlocked 30 or more Outfits.", 30, true), - Title(12, CyclopediaTitle_t::OUTFITS, "Tibia's Topmodel (Grade 4)", "Unlocked 40 or more Outfits.", 40, true), - Title(13, CyclopediaTitle_t::OUTFITS, "Tibia's Topmodel (Grade 5)", "Unlocked 50 or more Outfits.", 50, true), - - Title(14, CyclopediaTitle_t::LEVEL, "Trolltrasher", "Reached level 50.", 50, false), - Title(15, CyclopediaTitle_t::LEVEL, "Cyclopscamper", "Reached level 100.", 100, false), - Title(16, CyclopediaTitle_t::LEVEL, "Dragondouser", "Reached level 200.", 200, false), - Title(17, CyclopediaTitle_t::LEVEL, "Demondoom", "Reached level 300.", 300, false), - Title(18, CyclopediaTitle_t::LEVEL, "Drakenbane", "Reached level 400.", 400, false), - Title(19, CyclopediaTitle_t::LEVEL, "Silencer", "Reached level 500.", 500, false), - Title(20, CyclopediaTitle_t::LEVEL, "Exalted", "Reached level 1000.", 1000, false), - - Title(21, CyclopediaTitle_t::HIGHSCORES, "Apex Predator", "", "Highest Level on character's world.", static_cast(HighscoreCategories_t::EXPERIENCE)), - Title(22, CyclopediaTitle_t::HIGHSCORES, "Big Boss", "", "Highest score of accumulated boss points on character's world.", static_cast(HighscoreCategories_t::BOSS_POINTS)), - Title(23, CyclopediaTitle_t::HIGHSCORES, "Jack of all Taints", "", "Highest score for killing Goshnar and his aspects on character's world.", static_cast(HighscoreCategories_t::GOSHNAR)), - Title(24, CyclopediaTitle_t::HIGHSCORES, "Legend of Fishing", "", "Highest fishing level on character's world.", static_cast(HighscoreCategories_t::FISHING)), - Title(25, CyclopediaTitle_t::HIGHSCORES, "Legend of Magic", "", "Highest magic level on character's world.", static_cast(HighscoreCategories_t::MAGIC_LEVEL)), - Title(26, CyclopediaTitle_t::HIGHSCORES, "Legend of Marksmanship", "", "Highest distance level on character's world.", static_cast(HighscoreCategories_t::DISTANCE_FIGHTING)), - Title(27, CyclopediaTitle_t::HIGHSCORES, "Legend of the Axe", "", "Highest axe level on character's world.", static_cast(HighscoreCategories_t::AXE_FIGHTING)), - Title(28, CyclopediaTitle_t::HIGHSCORES, "Legend of the Club", "", "Highest club level on character's world.", static_cast(HighscoreCategories_t::CLUB_FIGHTING)), - Title(29, CyclopediaTitle_t::HIGHSCORES, "Legend of the Fist", "", "Highest fist level on character's world.", static_cast(HighscoreCategories_t::FIST_FIGHTING)), - Title(30, CyclopediaTitle_t::HIGHSCORES, "Legend of the Shield", "", "Highest shielding level on character's world.", static_cast(HighscoreCategories_t::SHIELDING)), - Title(31, CyclopediaTitle_t::HIGHSCORES, "Legend of the Sword", "", "Highest sword level on character's world.", static_cast(HighscoreCategories_t::SWORD_FIGHTING)), - Title(32, CyclopediaTitle_t::HIGHSCORES, "Prince Charming", "Princess Charming", "Highest score of accumulated charm points on character's world.", static_cast(HighscoreCategories_t::CHARMS)), - Title(33, CyclopediaTitle_t::HIGHSCORES, "Reigning Drome Champion", "", "Finished most recent Tibiadrome rota ranked in the top 5.", static_cast(HighscoreCategories_t::DROME)), - - Title(34, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_HUMANOID), "Bipedantic", "", "Unlocked All Humanoid Bestiary entries."), - Title(35, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_LYCANTHROPE), "Blood Moon Hunter", "Blood Moon Huntress", "Unlocked All Lycanthrope Bestiary entries."), - Title(36, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_AMPHIBIC), "Coldblooded", "", "Unlocked All Amphibic Bestiary entries."), - Title(37, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_BIRD), "Death from Below", "", "Unlocked all Bird Bestiary entries."), - Title(38, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_DEMON), "Demonator", "", "Unlocked all Demon Bestiary entries."), - Title(39, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_DRAGON), "Dragonslayer", "", "Unlocked all Dragon Bestiary entries."), - Title(40, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_ELEMENTAL), "Elementalist", "", "Unlocked all Elemental Bestiary entries."), - Title(41, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_VERMIN), "Exterminator", "", "Unlocked all Vermin Bestiary entries."), - Title(42, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_FEY), "Fey Swatter", "", "Unlocked all Fey Bestiary entries."), - Title(43, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_UNDEAD), "Ghosthunter", "Ghosthuntress", "Unlocked all Undead Bestiary entries."), - Title(44, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_CONSTRUCT), "Handyman", "Handywoman", "Unlocked all Construct Bestiary entries."), - Title(45, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_MAMMAL), "Huntsman", "Huntress", "Unlocked all Mammal Bestiary entries."), - Title(46, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_EXTRA_DIMENSIONAL), "Interdimensional Destroyer", "", "Unlocked all Extra Dimensional Bestiary entries."), - Title(47, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_HUMAN), "Manhunter", "Manhuntress", "Unlocked all Human Bestiary entries."), - Title(48, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_MAGICAL), "Master of Illusion", "Mistress of Illusion", "Unlocked all Magical Bestiary entries."), - Title(49, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_SLIME), "Ooze Blues", "", "Unlocked all Slime Bestiary entries."), - Title(50, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_AQUATIC), "Sea Bane", "", "Unlocked all Aquatic Bestiary entries."), - Title(51, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_REPTILE), "Snake Charmer", "", "Unlocked all Reptile Bestiary entries."), - Title(52, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_GIANT), "Tumbler", "", "Unlocked all Giant Bestiary entries."), - Title(53, CyclopediaTitle_t::BESTIARY, static_cast(BestiaryType_t::BESTY_RACE_PLANT), "Weedkiller", "", "Unlocked all Plant Bestiary entries."), - Title(54, CyclopediaTitle_t::BESTIARY, 0, "Executioner", "", "Unlocked all Bestiary entries."), - - Title(55, CyclopediaTitle_t::BOSSTIARY, static_cast(BosstiaryRarity_t::RARITY_NEMESIS), "Boss Annihilator", "", "Unlocked all Nemesis bosses.", 0, false), - Title(56, CyclopediaTitle_t::BOSSTIARY, static_cast(BosstiaryRarity_t::RARITY_ARCHFOE), "Boss Destroyer", "", "Unlocked 10 or more Archfoe bosses.", 10, true), - Title(57, CyclopediaTitle_t::BOSSTIARY, static_cast(BosstiaryRarity_t::RARITY_NEMESIS), "Boss Devastator", "", "Unlocked 10 or more Nemesis bosses.", 10, true), - Title(58, CyclopediaTitle_t::BOSSTIARY, static_cast(BosstiaryRarity_t::RARITY_ARCHFOE), "Boss Eraser", "", "Unlocked all Archfoe bosses.", 0, false), - Title(59, CyclopediaTitle_t::BOSSTIARY, 0, "Boss Executioner", "", "Unlocked all bosses.", 0, false), - Title(60, CyclopediaTitle_t::BOSSTIARY, static_cast(BosstiaryRarity_t::RARITY_BANE), "Boss Hunter", "", "Unlocked 10 or more Bane bosses.", 10, true), - Title(61, CyclopediaTitle_t::BOSSTIARY, static_cast(BosstiaryRarity_t::RARITY_NEMESIS), "Boss Obliterator", "", "Unlocked 40 or more Nemesis bosses.", 40, true), - Title(62, CyclopediaTitle_t::BOSSTIARY, static_cast(BosstiaryRarity_t::RARITY_BANE), "Boss Slayer", "", "Unlocked all Bane bosses.", 0, false), - Title(63, CyclopediaTitle_t::BOSSTIARY, static_cast(BosstiaryRarity_t::RARITY_ARCHFOE), "Boss Smiter", "", "Unlocked 40 or more Archfoe bosses.", 40, true), - Title(64, CyclopediaTitle_t::BOSSTIARY, static_cast(BosstiaryRarity_t::RARITY_BANE), "Boss Veteran", "", "Unlocked 40 or more Bane bosses.", 40, true), - - Title(65, CyclopediaTitle_t::DAILY_REWARD, "Creature of Habit (Grade 1)", "Reward Streak of at least 7 days of consecutive logins.", 7, true), - Title(66, CyclopediaTitle_t::DAILY_REWARD, "Creature of Habit (Grade 2)", "Reward Streak of at least 30 days of consecutive logins.", 30, true), - Title(67, CyclopediaTitle_t::DAILY_REWARD, "Creature of Habit (Grade 3)", "Reward Streak of at least 90 days of consecutive logins.", 90, true), - Title(68, CyclopediaTitle_t::DAILY_REWARD, "Creature of Habit (Grade 4)", "Reward Streak of at least 180 days of consecutive logins.", 180, true), - Title(69, CyclopediaTitle_t::DAILY_REWARD, "Creature of Habit (Grade 5)", "Reward Streak of at least 365 days of consecutive logins.", 365, true), - - Title(70, CyclopediaTitle_t::TASK, "Aspiring Huntsman", "Invested 160,000 tasks points.", 160000, true, "Aspiring Huntswoman"), - Title(71, CyclopediaTitle_t::TASK, "Competent Beastslayer", "Invested 320,000 tasks points.", 320000, true), - Title(72, CyclopediaTitle_t::TASK, "Feared Bountyhunter", "Invested 430,000 tasks points.", 430000, true), - - Title(73, CyclopediaTitle_t::MAP, "Dedicated Entrepreneur", "Explored 50% of all the map areas.", 50, false), - Title(74, CyclopediaTitle_t::MAP, "Globetrotter", "Explored all map areas.", 100, false), - - Title(75, CyclopediaTitle_t::OTHERS, "Guild Leader", "Leading a Guild.", false), - Title(76, CyclopediaTitle_t::OTHERS, "Proconsul of Iksupan", "Only a true devotee to the cause of the ancient Iks and their lost legacy may step up to the rank of proconsul.", true), - Title(77, CyclopediaTitle_t::OTHERS, "Admirer of the Crown", "Adjust your crown and handle it.", true), - Title(78, CyclopediaTitle_t::OTHERS, "Big Spender", "Unlocked the full Golden Outfit.", true), - Title(79, CyclopediaTitle_t::OTHERS, "Challenger of the Iks", "Challenged Ahau, guardian of Iksupan, in traditional Iks warrior attire.", true), - Title(80, CyclopediaTitle_t::OTHERS, "Royal Bounacean Advisor", "Called to the court of Bounac by Kesar the Younger himself.", true), - Title(81, CyclopediaTitle_t::OTHERS, "Aeternal", "Awarded exclusively to stalwart heroes keeping the faith under all circumstances.", true), - Title(82, CyclopediaTitle_t::OTHERS, "Robinson Crusoe", "Some discoveries are reserved to only the most experienced adventurers. Until the next frontier opens on the horizon.", true), - Title(83, CyclopediaTitle_t::OTHERS, "Chompmeister", "Awarded only to true connoisseurs undertaking even the most exotic culinary escapades.", true), - Title(84, CyclopediaTitle_t::OTHERS, "Bringer of Rain", "Forging through battle after battle like a true gladiator.", true), - Title(85, CyclopediaTitle_t::OTHERS, "Beastly", "Reached 2000 charm points. Quite beastly!", true), - Title(86, CyclopediaTitle_t::OTHERS, "Midnight Hunter", "When the hunter becomes the hunted, perseverance decides the game.", true), - Title(87, CyclopediaTitle_t::OTHERS, "Ratinator", "Killing some snarky cave rats is helpful, killing over ten thousand of them is a statement.", true), - Title(88, CyclopediaTitle_t::OTHERS, "Doomsday Nemesis", "Awarded for great help in the battle against Gaz'haragoth.", true), - Title(89, CyclopediaTitle_t::OTHERS, "Hero of Bounac", "You prevailed during the battle of Bounac and broke the siege that held Bounac's people in its firm grasp.", true), // Derrotar o boss Drume. - Title(90, CyclopediaTitle_t::OTHERS, "King of Demon", "Defeat Morshabaal 5 times.", 0, true, "Queen of Demon"), - Title(91, CyclopediaTitle_t::OTHERS, "Planegazer", "Followed the trail of the Planestrider to the end.", true), // Derrotar o boss Planestrider - Title(92, CyclopediaTitle_t::OTHERS, "Time Traveller", "Anywhere in time or space.", true), // Derrotar o boss Lord Retro - Title(93, CyclopediaTitle_t::OTHERS, "Truly Boss", "Reach 15,000 boss points.", true), + Title(1, GOLD, "Gold Hoarder", "Earned at least 1,000,000 gold.", 1000000, false), + Title(2, GOLD, "Platinum Hoarder", "Earned at least 10,000,000 gold.", 10000000, false), + Title(3, GOLD, "Crystal Hoarder", "Earned at least 100,000,000 gold.", 100000000, false), + + Title(4, MOUNTS, "Beaststrider (Grade 1)", "Unlocked 10 or more Mounts.", 10, true), + Title(5, MOUNTS, "Beaststrider (Grade 2)", "Unlocked 20 or more Mounts.", 20, true), + Title(6, MOUNTS, "Beaststrider (Grade 3)", "Unlocked 30 or more Mounts.", 30, true), + Title(7, MOUNTS, "Beaststrider (Grade 4)", "Unlocked 40 or more Mounts.", 40, true), + Title(8, MOUNTS, "Beaststrider (Grade 5)", "Unlocked 50 or more Mounts.", 50, true), + + Title(9, OUTFITS, "Tibia's Topmodel (Grade 1)", "Unlocked 10 or more Outfits.", 10, true), + Title(10, OUTFITS, "Tibia's Topmodel (Grade 2)", "Unlocked 20 or more Outfits.", 20, true), + Title(11, OUTFITS, "Tibia's Topmodel (Grade 3)", "Unlocked 30 or more Outfits.", 30, true), + Title(12, OUTFITS, "Tibia's Topmodel (Grade 4)", "Unlocked 40 or more Outfits.", 40, true), + Title(13, OUTFITS, "Tibia's Topmodel (Grade 5)", "Unlocked 50 or more Outfits.", 50, true), + + Title(14, LEVEL, "Trolltrasher", "Reached level 50.", 50, false), + Title(15, LEVEL, "Cyclopscamper", "Reached level 100.", 100, false), + Title(16, LEVEL, "Dragondouser", "Reached level 200.", 200, false), + Title(17, LEVEL, "Demondoom", "Reached level 300.", 300, false), + Title(18, LEVEL, "Drakenbane", "Reached level 400.", 400, false), + Title(19, LEVEL, "Silencer", "Reached level 500.", 500, false), + Title(20, LEVEL, "Exalted", "Reached level 1000.", 1000, false), + + Title(21, HIGHSCORES, "Apex Predator", "", "Highest Level on character's world.", static_cast(EXPERIENCE)), + Title(22, HIGHSCORES, "Big Boss", "", "Highest score of accumulated boss points on character's world.", static_cast(BOSS_POINTS)), + Title(23, HIGHSCORES, "Jack of all Taints", "", "Highest score for killing Goshnar and his aspects on character's world.", static_cast(GOSHNAR)), + Title(24, HIGHSCORES, "Legend of Fishing", "", "Highest fishing level on character's world.", static_cast(FISHING)), + Title(25, HIGHSCORES, "Legend of Magic", "", "Highest magic level on character's world.", static_cast(MAGIC_LEVEL)), + Title(26, HIGHSCORES, "Legend of Marksmanship", "", "Highest distance level on character's world.", static_cast(DISTANCE_FIGHTING)), + Title(27, HIGHSCORES, "Legend of the Axe", "", "Highest axe level on character's world.", static_cast(AXE_FIGHTING)), + Title(28, HIGHSCORES, "Legend of the Club", "", "Highest club level on character's world.", static_cast(CLUB_FIGHTING)), + Title(29, HIGHSCORES, "Legend of the Fist", "", "Highest fist level on character's world.", static_cast(FIST_FIGHTING)), + Title(30, HIGHSCORES, "Legend of the Shield", "", "Highest shielding level on character's world.", static_cast(SHIELDING)), + Title(31, HIGHSCORES, "Legend of the Sword", "", "Highest sword level on character's world.", static_cast(SWORD_FIGHTING)), + Title(32, HIGHSCORES, "Prince Charming", "Princess Charming", "Highest score of accumulated charm points on character's world.", static_cast(CHARMS)), + Title(33, HIGHSCORES, "Reigning Drome Champion", "", "Finished most recent Tibiadrome rota ranked in the top 5.", static_cast(DROME)), + + Title(34, BESTIARY, static_cast(BESTY_RACE_HUMANOID), "Bipedantic", "", "Unlocked All Humanoid Bestiary entries."), + Title(35, BESTIARY, static_cast(BESTY_RACE_LYCANTHROPE), "Blood Moon Hunter", "Blood Moon Huntress", "Unlocked All Lycanthrope Bestiary entries."), + Title(36, BESTIARY, static_cast(BESTY_RACE_AMPHIBIC), "Coldblooded", "", "Unlocked All Amphibic Bestiary entries."), + Title(37, BESTIARY, static_cast(BESTY_RACE_BIRD), "Death from Below", "", "Unlocked all Bird Bestiary entries."), + Title(38, BESTIARY, static_cast(BESTY_RACE_DEMON), "Demonator", "", "Unlocked all Demon Bestiary entries."), + Title(39, BESTIARY, static_cast(BESTY_RACE_DRAGON), "Dragonslayer", "", "Unlocked all Dragon Bestiary entries."), + Title(40, BESTIARY, static_cast(BESTY_RACE_ELEMENTAL), "Elementalist", "", "Unlocked all Elemental Bestiary entries."), + Title(41, BESTIARY, static_cast(BESTY_RACE_VERMIN), "Exterminator", "", "Unlocked all Vermin Bestiary entries."), + Title(42, BESTIARY, static_cast(BESTY_RACE_FEY), "Fey Swatter", "", "Unlocked all Fey Bestiary entries."), + Title(43, BESTIARY, static_cast(BESTY_RACE_UNDEAD), "Ghosthunter", "Ghosthuntress", "Unlocked all Undead Bestiary entries."), + Title(44, BESTIARY, static_cast(BESTY_RACE_CONSTRUCT), "Handyman", "Handywoman", "Unlocked all Construct Bestiary entries."), + Title(45, BESTIARY, static_cast(BESTY_RACE_MAMMAL), "Huntsman", "Huntress", "Unlocked all Mammal Bestiary entries."), + Title(46, BESTIARY, static_cast(BESTY_RACE_EXTRA_DIMENSIONAL), "Interdimensional Destroyer", "", "Unlocked all Extra Dimensional Bestiary entries."), + Title(47, BESTIARY, static_cast(BESTY_RACE_HUMAN), "Manhunter", "Manhuntress", "Unlocked all Human Bestiary entries."), + Title(48, BESTIARY, static_cast(BESTY_RACE_MAGICAL), "Master of Illusion", "Mistress of Illusion", "Unlocked all Magical Bestiary entries."), + Title(49, BESTIARY, static_cast(BESTY_RACE_SLIME), "Ooze Blues", "", "Unlocked all Slime Bestiary entries."), + Title(50, BESTIARY, static_cast(BESTY_RACE_AQUATIC), "Sea Bane", "", "Unlocked all Aquatic Bestiary entries."), + Title(51, BESTIARY, static_cast(BESTY_RACE_REPTILE), "Snake Charmer", "", "Unlocked all Reptile Bestiary entries."), + Title(52, BESTIARY, static_cast(BESTY_RACE_GIANT), "Tumbler", "", "Unlocked all Giant Bestiary entries."), + Title(53, BESTIARY, static_cast(BESTY_RACE_PLANT), "Weedkiller", "", "Unlocked all Plant Bestiary entries."), + Title(54, BESTIARY, 0, "Executioner", "", "Unlocked all Bestiary entries."), + + Title(55, BOSSTIARY, static_cast(BosstiaryRarity_t::RARITY_NEMESIS), "Boss Annihilator", "", "Unlocked all Nemesis bosses.", 0, false), + Title(56, BOSSTIARY, static_cast(BosstiaryRarity_t::RARITY_ARCHFOE), "Boss Destroyer", "", "Unlocked 10 or more Archfoe bosses.", 10, true), + Title(57, BOSSTIARY, static_cast(BosstiaryRarity_t::RARITY_NEMESIS), "Boss Devastator", "", "Unlocked 10 or more Nemesis bosses.", 10, true), + Title(58, BOSSTIARY, static_cast(BosstiaryRarity_t::RARITY_ARCHFOE), "Boss Eraser", "", "Unlocked all Archfoe bosses.", 0, false), + Title(59, BOSSTIARY, 0, "Boss Executioner", "", "Unlocked all bosses.", 0, false), + Title(60, BOSSTIARY, static_cast(BosstiaryRarity_t::RARITY_BANE), "Boss Hunter", "", "Unlocked 10 or more Bane bosses.", 10, true), + Title(61, BOSSTIARY, static_cast(BosstiaryRarity_t::RARITY_NEMESIS), "Boss Obliterator", "", "Unlocked 40 or more Nemesis bosses.", 40, true), + Title(62, BOSSTIARY, static_cast(BosstiaryRarity_t::RARITY_BANE), "Boss Slayer", "", "Unlocked all Bane bosses.", 0, false), + Title(63, BOSSTIARY, static_cast(BosstiaryRarity_t::RARITY_ARCHFOE), "Boss Smiter", "", "Unlocked 40 or more Archfoe bosses.", 40, true), + Title(64, BOSSTIARY, static_cast(BosstiaryRarity_t::RARITY_BANE), "Boss Veteran", "", "Unlocked 40 or more Bane bosses.", 40, true), + + Title(65, DAILY_REWARD, "Creature of Habit (Grade 1)", "Reward Streak of at least 7 days of consecutive logins.", 7, true), + Title(66, DAILY_REWARD, "Creature of Habit (Grade 2)", "Reward Streak of at least 30 days of consecutive logins.", 30, true), + Title(67, DAILY_REWARD, "Creature of Habit (Grade 3)", "Reward Streak of at least 90 days of consecutive logins.", 90, true), + Title(68, DAILY_REWARD, "Creature of Habit (Grade 4)", "Reward Streak of at least 180 days of consecutive logins.", 180, true), + Title(69, DAILY_REWARD, "Creature of Habit (Grade 5)", "Reward Streak of at least 365 days of consecutive logins.", 365, true), + + Title(70, TASK, "Aspiring Huntsman", "Invested 160,000 tasks points.", 160000, true, "Aspiring Huntswoman"), + Title(71, TASK, "Competent Beastslayer", "Invested 320,000 tasks points.", 320000, true), + Title(72, TASK, "Feared Bountyhunter", "Invested 430,000 tasks points.", 430000, true), + + Title(73, MAP, "Dedicated Entrepreneur", "Explored 50% of all the map areas.", 50, false), + Title(74, MAP, "Globetrotter", "Explored all map areas.", 100, false), + + Title(75, OTHERS, "Guild Leader", "Leading a Guild.", false), + Title(76, OTHERS, "Proconsul of Iksupan", "Only a true devotee to the cause of the ancient Iks and their lost legacy may step up to the rank of proconsul.", true), + Title(77, OTHERS, "Admirer of the Crown", "Adjust your crown and handle it.", true), + Title(78, OTHERS, "Big Spender", "Unlocked the full Golden Outfit.", true), + Title(79, OTHERS, "Challenger of the Iks", "Challenged Ahau, guardian of Iksupan, in traditional Iks warrior attire.", true), + Title(80, OTHERS, "Royal Bounacean Advisor", "Called to the court of Bounac by Kesar the Younger himself.", true), + Title(81, OTHERS, "Aeternal", "Awarded exclusively to stalwart heroes keeping the faith under all circumstances.", true), + Title(82, OTHERS, "Robinson Crusoe", "Some discoveries are reserved to only the most experienced adventurers. Until the next frontier opens on the horizon.", true), + Title(83, OTHERS, "Chompmeister", "Awarded only to true connoisseurs undertaking even the most exotic culinary escapades.", true), + Title(84, OTHERS, "Bringer of Rain", "Forging through battle after battle like a true gladiator.", true), + Title(85, OTHERS, "Beastly", "Reached 2000 charm points. Quite beastly!", true), + Title(86, OTHERS, "Midnight Hunter", "When the hunter becomes the hunted, perseverance decides the game.", true), + Title(87, OTHERS, "Ratinator", "Killing some snarky cave rats is helpful, killing over ten thousand of them is a statement.", true), + Title(88, OTHERS, "Doomsday Nemesis", "Awarded for great help in the battle against Gaz'haragoth.", true), + Title(89, OTHERS, "Hero of Bounac", "You prevailed during the battle of Bounac and broke the siege that held Bounac's people in its firm grasp.", true), // Derrotar o boss Drume. + Title(90, OTHERS, "King of Demon", "Defeat Morshabaal 5 times.", 0, true, "Queen of Demon"), + Title(91, OTHERS, "Planegazer", "Followed the trail of the Planestrider to the end.", true), // Derrotar o boss Planestrider + Title(92, OTHERS, "Time Traveller", "Anywhere in time or space.", true), // Derrotar o boss Lord Retro + Title(93, OTHERS, "Truly Boss", "Reach 15,000 boss points.", true), }; m_highscoreCategoriesNames = { - { static_cast(HighscoreCategories_t::ACHIEVEMENTS), "Achievement Points" }, - { static_cast(HighscoreCategories_t::AXE_FIGHTING), "Axe Fighting" }, - { static_cast(HighscoreCategories_t::BOSS_POINTS), "Boss Points" }, - { static_cast(HighscoreCategories_t::CHARMS), "Charm Points" }, - { static_cast(HighscoreCategories_t::CLUB_FIGHTING), "Club Fighting" }, - { static_cast(HighscoreCategories_t::DISTANCE_FIGHTING), "Distance Fighting" }, - { static_cast(HighscoreCategories_t::DROME), "Drome Score" }, - { static_cast(HighscoreCategories_t::EXPERIENCE), "Experience Points" }, - { static_cast(HighscoreCategories_t::FISHING), "Fishing" }, - { static_cast(HighscoreCategories_t::FIST_FIGHTING), "Fist Fighting" }, - { static_cast(HighscoreCategories_t::GOSHNAR), "Goshnar's Taint" }, - { static_cast(HighscoreCategories_t::LOYALTY), "Loyalty Points" }, - { static_cast(HighscoreCategories_t::MAGIC_LEVEL), "Magic Level" }, - { static_cast(HighscoreCategories_t::SHIELDING), "Shielding" }, + { static_cast(ACHIEVEMENTS), "Achievement Points" }, + { static_cast(AXE_FIGHTING), "Axe Fighting" }, + { static_cast(BOSS_POINTS), "Boss Points" }, + { static_cast(CHARMS), "Charm Points" }, + { static_cast(CLUB_FIGHTING), "Club Fighting" }, + { static_cast(DISTANCE_FIGHTING), "Distance Fighting" }, + { static_cast(DROME), "Drome Score" }, + { static_cast(EXPERIENCE), "Experience Points" }, + { static_cast(FISHING), "Fishing" }, + { static_cast(FIST_FIGHTING), "Fist Fighting" }, + { static_cast(GOSHNAR), "Goshnar's Taint" }, + { static_cast(LOYALTY_POINTS), "Loyalty Points" }, + { static_cast(MAGIC_LEVEL), "Magic Level" }, + { static_cast(SHIELDING), "Shielding" }, { static_cast(HighscoreCategories_t::SWORD_FIGHTING), "Sword Fighting" }, }; @@ -395,6 +418,10 @@ Game::Game() { Game::~Game() = default; +Game &Game::getInstance() { + return inject(); +} + void Game::resetMonsters() const { for (const auto &[monsterId, monster] : getMonsters()) { monster->clearTargetList(); @@ -543,6 +570,28 @@ void Game::setWorldType(WorldType_t type) { worldType = type; } +const std::unique_ptr &Game::getTeamFinder(const std::shared_ptr &player) const { + auto it = teamFinderMap.find(player->getGUID()); + if (it != teamFinderMap.end()) { + return it->second; + } + + return TeamFinderNull; +} + +const std::unique_ptr &Game::getOrCreateTeamFinder(const std::shared_ptr &player) { + auto it = teamFinderMap.find(player->getGUID()); + if (it != teamFinderMap.end()) { + return it->second; + } + + return teamFinderMap[player->getGUID()] = std::make_unique(); +} + +void Game::removeTeamFinderListed(uint32_t leaderGuid) { + teamFinderMap.erase(leaderGuid); +} + void Game::setGameState(GameState_t newState) { if (gameState == GAME_STATE_SHUTDOWN) { return; // this cannot be stopped @@ -573,7 +622,7 @@ void Game::setGameState(GameState_t newState) { raids.loadFromXml(); raids.startup(); - mounts.loadFromXml(); + mounts->loadFromXml(); loadMotdNum(); loadPlayersRecord(); @@ -3101,7 +3150,7 @@ ReturnValue Game::processLootItems(const std::shared_ptr &player, std::s return ret; } -ReturnValue Game::internalCollectManagedItems(const std::shared_ptr &player, const std::shared_ptr &item, ObjectCategory_t category /* = OBJECTCATEGORY_DEFAULT*/, bool isLootContainer /* = true*/) { +ReturnValue Game::internalCollectManagedItems(const std::shared_ptr &player, const std::shared_ptr &item, ObjectCategory_t category, bool isLootContainer /* = true*/) { if (!player || !item) { return RETURNVALUE_NOTPOSSIBLE; } @@ -4300,7 +4349,7 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos outfit.lookAddons = 0; } - const auto mount = mounts.getMountByClientID(outfit.lookMount); + const auto mount = mounts->getMountByClientID(outfit.lookMount); if (!mount || !player->hasMount(mount) || player->isWearingSupportOutfit()) { outfit.lookMount = 0; } @@ -6028,7 +6077,7 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit, uint8_t isMoun player->setRandomMount(isMountRandomized); if (isMountRandomized && outfit.lookMount != 0 && player->hasAnyMount()) { - auto randomMount = mounts.getMountByID(player->getRandomMountId()); + auto randomMount = mounts->getMountByID(player->getRandomMountId()); outfit.lookMount = randomMount->clientId; } @@ -6038,7 +6087,7 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit, uint8_t isMoun } if (outfit.lookMount != 0) { - const auto mount = mounts.getMountByClientID(outfit.lookMount); + const auto mount = mounts->getMountByClientID(outfit.lookMount); if (!mount) { return; } @@ -6058,7 +6107,7 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit, uint8_t isMoun auto deltaSpeedChange = mount->speed; if (player->isMounted()) { - const auto prevMount = mounts.getMountByID(player->getLastMount()); + const auto prevMount = mounts->getMountByID(player->getLastMount()); if (prevMount) { deltaSpeedChange -= prevMount->speed; } @@ -6391,7 +6440,7 @@ void Game::addCreatureCheck(const std::shared_ptr &creature) { creature->inCheckCreaturesVector.store(true); - g_dispatcher().context().tryAddEvent([this, creature] { + g_dispatcher().context().tryAddEvent([creature] { checkCreatureLists[uniform_random(0, EVENT_CREATURECOUNT - 1)].emplace_back(creature); }, "addCreatureCheck"); @@ -8033,6 +8082,22 @@ void Game::checkLight() { } } +ItemClassification* Game::getItemsClassification(uint8_t id, bool create) { + auto it = std::ranges::find_if(itemsClassifications, [id](ItemClassification* classification) { + return classification->id == id; + }); + + if (it != itemsClassifications.end()) { + return *it; + } else if (create) { + auto itemClassification = new ItemClassification(id); + addItemsClassification(itemClassification); + return itemClassification; + } + + return nullptr; +} + LightInfo Game::getWorldLightInfo() const { return { lightLevel, 0xD7 }; } diff --git a/src/game/game.hpp b/src/game/game.hpp index 7d614d760e4..b1afd810100 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -9,22 +9,15 @@ #pragma once -#include "account/account.hpp" -#include "creatures/combat/combat.hpp" -#include "items/containers/container.hpp" +#include "creatures/appearance/outfit/outfit.hpp" +#include "creatures/players/cyclopedia/player_badge.hpp" +#include "creatures/players/cyclopedia/player_title.hpp" +#include "creatures/players/grouping/familiars.hpp" #include "creatures/players/grouping/groups.hpp" -#include "io/iobestiary.hpp" -#include "items/item.hpp" -#include "map/map.hpp" -#include "creatures/npcs/npc.hpp" -#include "movement/position.hpp" -#include "creatures/players/player.hpp" #include "lua/creature/raids.hpp" -#include "creatures/players/grouping/team_finder.hpp" -#include "utils/wildcardtree.hpp" -#include "items/items_classification.hpp" +#include "map/map.hpp" #include "modal_window/modal_window.hpp" -#include "enums/object_category.hpp" +#include "movement/position.hpp" // Forward declaration for protobuf class namespace Canary { @@ -46,11 +39,25 @@ class ItemClassification; class Guild; class Mounts; class Spectators; +class Player; +class Account; +class TeamFinder; +class NetworkMessage; +class Task; +class Container; +class ContainerIterator; +class Item; +class BedItem; +class WildcardTreeNode; struct Achievement; struct HighscoreCategory; -struct Badge; -struct Title; +struct TextMessage; + +enum ObjectCategory_t : uint8_t; +enum class ForgeAction_t : uint8_t; + +using CreatureVector = std::vector>; static constexpr uint16_t SERVER_BEAT = 0x32; static constexpr int32_t EVENT_MS = 10000; @@ -87,9 +94,7 @@ class Game { Game(const Game &) = delete; Game &operator=(const Game &) = delete; - static Game &getInstance() { - return inject(); - } + static Game &getInstance(); void resetMonsters() const; void resetNpcs() const; @@ -129,27 +134,11 @@ class Game { return teamFinderMap; } - const std::unique_ptr &getTeamFinder(const std::shared_ptr &player) const { - const auto it = teamFinderMap.find(player->getGUID()); - if (it != teamFinderMap.end()) { - return it->second; - } + const std::unique_ptr &getTeamFinder(const std::shared_ptr &player) const; - return TeamFinderNull; - } + const std::unique_ptr &getOrCreateTeamFinder(const std::shared_ptr &player); - const std::unique_ptr &getOrCreateTeamFinder(const std::shared_ptr &player) { - const auto it = teamFinderMap.find(player->getGUID()); - if (it != teamFinderMap.end()) { - return it->second; - } - - return teamFinderMap[player->getGUID()] = std::make_unique(); - } - - void removeTeamFinderListed(uint32_t leaderGuid) { - teamFinderMap.erase(leaderGuid); - } + void removeTeamFinderListed(uint32_t leaderGuid); std::shared_ptr internalGetCylinder(const std::shared_ptr &player, const Position &pos); std::shared_ptr internalGetThing(const std::shared_ptr &player, const Position &pos, int32_t index, uint32_t itemId, StackPosType_t type); @@ -205,21 +194,7 @@ class Game { void addItemsClassification(ItemClassification* itemsClassification) { itemsClassifications.push_back(itemsClassification); } - ItemClassification* getItemsClassification(uint8_t id, bool create) { - const auto it = std::ranges::find_if(itemsClassifications, [id](const ItemClassification* item) { - return item->id == id; - }); - - if (it != itemsClassifications.end()) { - return *it; - } else if (create) { - auto* itemClassification = new ItemClassification(id); - addItemsClassification(itemClassification); - return itemClassification; - } - - return nullptr; - } + ItemClassification* getItemsClassification(uint8_t id, bool create); LightInfo getWorldLightInfo() const; @@ -581,10 +556,10 @@ class Game { bool hasDistanceEffect(uint16_t effectId); Groups groups; - Familiars familiars; + [[no_unique_address]] Familiars familiars; Map map; - Mounts mounts; - Outfits outfits; + std::unique_ptr mounts; + [[no_unique_address]] Outfits outfits; Raids raids; std::unique_ptr m_appearancesPtr; @@ -828,7 +803,7 @@ class Game { * @param category Category of the item (default is OBJECTCATEGORY_DEFAULT). * @return Return value indicating success or error. */ - ReturnValue internalCollectManagedItems(const std::shared_ptr &player, const std::shared_ptr &item, ObjectCategory_t category = OBJECTCATEGORY_DEFAULT, bool isLootContainer = true); + ReturnValue internalCollectManagedItems(const std::shared_ptr &player, const std::shared_ptr &item, ObjectCategory_t category, bool isLootContainer = true); /** * @brief Collects items from the reward chest. @@ -859,7 +834,6 @@ class Game { std::string boostedCreature; std::vector> CharmList; - std::vector> checkCreatureLists[EVENT_CREATURECOUNT]; std::vector registeredMagicEffects; std::vector registeredDistanceEffects; diff --git a/src/game/game_definitions.hpp b/src/game/game_definitions.hpp index a6ce6e7eaa8..e122cdf979f 100644 --- a/src/game/game_definitions.hpp +++ b/src/game/game_definitions.hpp @@ -94,7 +94,7 @@ enum class HighscoreCategories_t : uint8_t { SHIELDING = 6, FISHING = 7, MAGIC_LEVEL = 8, - LOYALTY = 9, + LOYALTY_POINTS = 9, ACHIEVEMENTS = 10, CHARMS = 11, DROME = 12, diff --git a/src/game/movement/position.cpp b/src/game/movement/position.cpp index 46ea6ea573d..1972f1c2506 100644 --- a/src/game/movement/position.cpp +++ b/src/game/movement/position.cpp @@ -8,6 +8,7 @@ */ #include "game/movement/position.hpp" + #include "utils/tools.hpp" double Position::getEuclideanDistance(const Position &p1, const Position &p2) { diff --git a/src/game/movement/teleport.cpp b/src/game/movement/teleport.cpp index d670aaba329..935ec443617 100644 --- a/src/game/movement/teleport.cpp +++ b/src/game/movement/teleport.cpp @@ -7,8 +7,10 @@ * Website: https://docs.opentibiabr.com/ */ -#include "game/game.hpp" #include "game/movement/teleport.hpp" + +#include "creatures/creature.hpp" +#include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" Attr_ReadValue Teleport::readAttr(AttrTypes_t attr, PropStream &propStream) { diff --git a/src/game/movement/teleport.hpp b/src/game/movement/teleport.hpp index ce41505434c..893cca2610d 100644 --- a/src/game/movement/teleport.hpp +++ b/src/game/movement/teleport.hpp @@ -9,7 +9,10 @@ #pragma once -#include "items/tile.hpp" +#include "items/cylinder.hpp" +#include "items/item.hpp" + +class Tile; class Teleport final : public Item, public Cylinder { public: diff --git a/src/game/scheduling/dispatcher.cpp b/src/game/scheduling/dispatcher.cpp index c8fb29b889b..e716e9867cc 100644 --- a/src/game/scheduling/dispatcher.cpp +++ b/src/game/scheduling/dispatcher.cpp @@ -8,6 +8,7 @@ */ #include "game/scheduling/dispatcher.hpp" + #include "lib/thread/thread_pool.hpp" #include "lib/di/container.hpp" #include "utils/tools.hpp" diff --git a/src/game/scheduling/events_scheduler.cpp b/src/game/scheduling/events_scheduler.cpp index 44005fce11f..4a9bb017335 100644 --- a/src/game/scheduling/events_scheduler.cpp +++ b/src/game/scheduling/events_scheduler.cpp @@ -7,8 +7,9 @@ * Website: https://docs.opentibiabr.com/ */ -#include "config/configmanager.hpp" #include "game/scheduling/events_scheduler.hpp" + +#include "config/configmanager.hpp" #include "lua/scripts/scripts.hpp" bool EventsScheduler::loadScheduleEventFromXml() { diff --git a/src/game/scheduling/save_manager.cpp b/src/game/scheduling/save_manager.cpp index 254786f747d..9cccc4052a7 100644 --- a/src/game/scheduling/save_manager.cpp +++ b/src/game/scheduling/save_manager.cpp @@ -9,9 +9,13 @@ #include "game/scheduling/save_manager.hpp" +#include "config/configmanager.hpp" +#include "creatures/players/grouping/guild.hpp" #include "game/game.hpp" +#include "io/ioguild.hpp" #include "io/iologindata.hpp" #include "kv/kv.hpp" +#include "lib/di/container.hpp" SaveManager::SaveManager(ThreadPool &threadPool, KVStore &kvStore, Logger &logger, Game &game) : threadPool(threadPool), kv(kvStore), logger(logger), game(game) { } diff --git a/src/game/scheduling/task.cpp b/src/game/scheduling/task.cpp index 3049aa29e53..b3a79f7ab18 100644 --- a/src/game/scheduling/task.cpp +++ b/src/game/scheduling/task.cpp @@ -7,7 +7,7 @@ * Website: https://docs.opentibiabr.com/ */ -#include "task.hpp" +#include "game/scheduling/task.hpp" #include "lib/metrics/metrics.hpp" diff --git a/src/game/zones/zone.cpp b/src/game/zones/zone.cpp index 1f519d29a89..6f9f36886ea 100644 --- a/src/game/zones/zone.cpp +++ b/src/game/zones/zone.cpp @@ -7,7 +7,8 @@ * Website: https://docs.opentibiabr.com/ */ -#include "zone.hpp" +#include "game/zones/zone.hpp" + #include "game/game.hpp" #include "creatures/monsters/monster.hpp" #include "creatures/npcs/npc.hpp" diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index 05e72ba856e..f7efef90de4 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -7,13 +7,28 @@ * Website: https://docs.opentibiabr.com/ */ -#include "creatures/players/wheel/player_wheel.hpp" -#include "creatures/players/achievement/player_achievement.hpp" #include "io/functions/iologindata_load_player.hpp" -#include "game/game.hpp" -#include "enums/object_category.hpp" + +#include "config/configmanager.hpp" +#include "creatures/combat/condition.hpp" +#include "creatures/monsters/monsters.hpp" +#include "creatures/players/achievement/player_achievement.hpp" +#include "creatures/players/cyclopedia/player_badge.hpp" +#include "creatures/players/cyclopedia/player_cyclopedia.hpp" +#include "creatures/players/cyclopedia/player_title.hpp" +#include "creatures/players/vip/player_vip.hpp" +#include "creatures/players/vocations/vocation.hpp" +#include "creatures/players/wheel/player_wheel.hpp" #include "enums/account_coins.hpp" #include "enums/account_errors.hpp" +#include "enums/object_category.hpp" +#include "game/game.hpp" +#include "io/ioguild.hpp" +#include "io/ioprey.hpp" +#include "items/containers/depot/depotchest.hpp" +#include "items/containers/inbox/inbox.hpp" +#include "items/containers/rewards/reward.hpp" +#include "items/containers/rewards/rewardchest.hpp" #include "utils/tools.hpp" void IOLoginDataLoad::loadItems(ItemsMap &itemsMap, const DBResult_ptr &result, const std::shared_ptr &player) { diff --git a/src/io/functions/iologindata_save_player.cpp b/src/io/functions/iologindata_save_player.cpp index 5fa235c7f41..c6b92514969 100644 --- a/src/io/functions/iologindata_save_player.cpp +++ b/src/io/functions/iologindata_save_player.cpp @@ -8,7 +8,15 @@ */ #include "io/functions/iologindata_save_player.hpp" + +#include "config/configmanager.hpp" +#include "creatures/combat/condition.hpp" +#include "creatures/monsters/monsters.hpp" #include "game/game.hpp" +#include "io/ioprey.hpp" +#include "items/containers/depot/depotchest.hpp" +#include "items/containers/inbox/inbox.hpp" +#include "items/containers/rewards/reward.hpp" bool IOLoginDataSave::saveItems(const std::shared_ptr &player, const ItemBlockList &itemList, DBInsert &query_insert, PropWriteStream &propWriteStream) { if (!player) { diff --git a/src/io/functions/iologindata_save_player.hpp b/src/io/functions/iologindata_save_player.hpp index 8a35db73d11..abafc2c186a 100644 --- a/src/io/functions/iologindata_save_player.hpp +++ b/src/io/functions/iologindata_save_player.hpp @@ -11,6 +11,8 @@ #include "io/iologindata.hpp" +class PropWriteStream; + class IOLoginDataSave : public IOLoginData { public: static bool savePlayerFirst(const std::shared_ptr &player); diff --git a/src/io/io_bosstiary.cpp b/src/io/io_bosstiary.cpp index 68eaf2f8e3e..3e8a05524e6 100644 --- a/src/io/io_bosstiary.cpp +++ b/src/io/io_bosstiary.cpp @@ -12,8 +12,12 @@ #include "creatures/monsters/monsters.hpp" #include "creatures/players/player.hpp" #include "game/game.hpp" +#include "lib/di/container.hpp" #include "utils/tools.hpp" -#include "items/item.hpp" + +IOBosstiary &IOBosstiary::getInstance() { + return inject(); +} void IOBosstiary::loadBoostedBoss() { Database &database = Database::getInstance(); diff --git a/src/io/io_bosstiary.hpp b/src/io/io_bosstiary.hpp index 3afdb936c6d..92a8bf0b624 100644 --- a/src/io/io_bosstiary.hpp +++ b/src/io/io_bosstiary.hpp @@ -9,11 +9,6 @@ #pragma once -#include -#include -#include -#include "lib/di/container.hpp" - enum class BosstiaryRarity_t : uint8_t { RARITY_BANE = 0, RARITY_ARCHFOE = 1, @@ -39,9 +34,7 @@ class IOBosstiary { IOBosstiary(const IOBosstiary &) = delete; void operator=(const IOBosstiary &) = delete; - static IOBosstiary &getInstance() { - return inject(); - } + static IOBosstiary &getInstance(); void loadBoostedBoss(); diff --git a/src/io/iobestiary.cpp b/src/io/iobestiary.cpp index 3113f14d36d..b18ed181467 100644 --- a/src/io/iobestiary.cpp +++ b/src/io/iobestiary.cpp @@ -7,11 +7,13 @@ * Website: https://docs.opentibiabr.com/ */ -#include "declarations.hpp" -#include "game/game.hpp" #include "io/iobestiary.hpp" + +#include "creatures/combat/combat.hpp" +#include "creatures/combat/condition.hpp" #include "creatures/monsters/monsters.hpp" #include "creatures/players/player.hpp" +#include "game/game.hpp" #include "lib/metrics/metrics.hpp" SoftSingleton IOBestiary::instanceTracker("IOBestiary"); @@ -93,6 +95,10 @@ bool IOBestiary::parseCharmCombat(const std::shared_ptr &charm, const std return false; } +IOBestiary &IOBestiary::getInstance() { + return inject(); +} + std::shared_ptr IOBestiary::getBestiaryCharm(charmRune_t activeCharm, bool force /*= false*/) const { const auto charmInternal = g_game().getCharmList(); for (const auto &tmpCharm : charmInternal) { diff --git a/src/io/iobestiary.hpp b/src/io/iobestiary.hpp index 6baef14c89d..4eb576327fe 100644 --- a/src/io/iobestiary.hpp +++ b/src/io/iobestiary.hpp @@ -9,12 +9,16 @@ #pragma once -#include "declarations.hpp" #include "lib/di/soft_singleton.hpp" -#include "lua/scripts/luascript.hpp" -#include "creatures/players/player.hpp" +#include "creatures/creatures_definitions.hpp" + +class Player; class Game; +class SoftSingleton; +class SoftSingletonGuard; +class MonsterType; +class Creature; class Charm { public: @@ -33,7 +37,7 @@ class Charm { std::string logMsg; CombatType_t dmgtype = COMBAT_NONE; - uint16_t effect = CONST_ME_NONE; + uint16_t effect = 0; SoundEffect_t soundImpactEffect = SoundEffect_t::SILENCE; SoundEffect_t soundCastEffect = SoundEffect_t::SILENCE; @@ -50,9 +54,7 @@ class IOBestiary { IOBestiary(const IOBestiary &) = delete; void operator=(const IOBestiary &) = delete; - static IOBestiary &getInstance() { - return inject(); - } + static IOBestiary &getInstance(); std::shared_ptr getBestiaryCharm(charmRune_t activeCharm, bool force = false) const; void addBestiaryKill(const std::shared_ptr &player, const std::shared_ptr &mtype, uint32_t amount = 1); diff --git a/src/io/ioguild.cpp b/src/io/ioguild.cpp index 2e128aed94a..55c29b4155a 100644 --- a/src/io/ioguild.cpp +++ b/src/io/ioguild.cpp @@ -7,9 +7,10 @@ * Website: https://docs.opentibiabr.com/ */ +#include "io/ioguild.hpp" + #include "database/database.hpp" #include "creatures/players/grouping/guild.hpp" -#include "io/ioguild.hpp" std::shared_ptr IOGuild::loadGuild(uint32_t guildId) { Database &db = Database::getInstance(); diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp index eff416b8358..83116537b34 100644 --- a/src/io/iologindata.cpp +++ b/src/io/iologindata.cpp @@ -8,6 +8,8 @@ */ #include "io/iologindata.hpp" + +#include "config/configmanager.hpp" #include "io/functions/iologindata_load_player.hpp" #include "io/functions/iologindata_save_player.hpp" #include "game/game.hpp" diff --git a/src/io/iomap.cpp b/src/io/iomap.cpp index 3f66f5764ac..922613c0350 100644 --- a/src/io/iomap.cpp +++ b/src/io/iomap.cpp @@ -8,6 +8,7 @@ */ #include "io/iomap.hpp" + #include "game/movement/teleport.hpp" #include "game/game.hpp" #include "io/filestream.hpp" diff --git a/src/io/iomapserialize.cpp b/src/io/iomapserialize.cpp index 378f2f4246e..b1de604dd14 100644 --- a/src/io/iomapserialize.cpp +++ b/src/io/iomapserialize.cpp @@ -8,6 +8,8 @@ */ #include "io/iomapserialize.hpp" + +#include "config/configmanager.hpp" #include "io/iologindata.hpp" #include "game/game.hpp" #include "items/bed.hpp" diff --git a/src/io/iomarket.cpp b/src/io/iomarket.cpp index 321e251fc6e..3332ffab1c3 100644 --- a/src/io/iomarket.cpp +++ b/src/io/iomarket.cpp @@ -8,11 +8,14 @@ */ #include "io/iomarket.hpp" + +#include "config/configmanager.hpp" #include "database/databasetasks.hpp" -#include "io/iologindata.hpp" #include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" #include "game/scheduling/save_manager.hpp" +#include "io/iologindata.hpp" +#include "items/containers/inbox/inbox.hpp" uint8_t IOMarket::getTierFromDatabaseTable(const std::string &string) { auto tier = static_cast(std::atoi(string.c_str())); diff --git a/src/io/ioprey.cpp b/src/io/ioprey.cpp index 39996e0e77d..65838a1660c 100644 --- a/src/io/ioprey.cpp +++ b/src/io/ioprey.cpp @@ -9,14 +9,13 @@ #include "io/ioprey.hpp" -#include "lib/di/container.hpp" -#include "creatures/monsters/monster.hpp" -#include "creatures/players/player.hpp" #include "config/configmanager.hpp" +#include "creatures/monsters/monsters.hpp" +#include "creatures/players/player.hpp" #include "game/game.hpp" +#include "lib/di/container.hpp" #include "lib/metrics/metrics.hpp" #include "server/network/message/networkmessage.hpp" -#include "server/network/protocol/protocolgame.hpp" // Prey class PreySlot::PreySlot(PreySlot_t id) : diff --git a/src/items/bed.cpp b/src/items/bed.cpp index 830e8f11082..25e95318e74 100644 --- a/src/items/bed.cpp +++ b/src/items/bed.cpp @@ -8,10 +8,14 @@ */ #include "items/bed.hpp" + +#include "config/configmanager.hpp" +#include "creatures/combat/condition.hpp" #include "game/game.hpp" -#include "io/iologindata.hpp" #include "game/scheduling/dispatcher.hpp" #include "game/scheduling/save_manager.hpp" +#include "io/iologindata.hpp" +#include "server/network/protocol/protocolgame.hpp" BedItem::BedItem(uint16_t id) : Item(id) { diff --git a/src/items/containers/container.cpp b/src/items/containers/container.cpp index eb6e24c9134..e19de0da183 100644 --- a/src/items/containers/container.cpp +++ b/src/items/containers/container.cpp @@ -8,8 +8,9 @@ */ #include "items/containers/container.hpp" -#include "items/decay/decay.hpp" -#include "io/iomap.hpp" + +#include "config/configmanager.hpp" +#include "creatures/players/player.hpp" #include "game/game.hpp" #include "map/spectators.hpp" diff --git a/src/items/containers/depot/depotchest.cpp b/src/items/containers/depot/depotchest.cpp index b4372840eeb..ea5a5d3897d 100644 --- a/src/items/containers/depot/depotchest.cpp +++ b/src/items/containers/depot/depotchest.cpp @@ -8,6 +8,7 @@ */ #include "items/containers/depot/depotchest.hpp" + #include "utils/tools.hpp" DepotChest::DepotChest(uint16_t type) : diff --git a/src/items/containers/inbox/inbox.cpp b/src/items/containers/inbox/inbox.cpp index 8d4ae9b9ca7..92b113f19cc 100644 --- a/src/items/containers/inbox/inbox.cpp +++ b/src/items/containers/inbox/inbox.cpp @@ -8,6 +8,7 @@ */ #include "items/containers/inbox/inbox.hpp" + #include "utils/tools.hpp" Inbox::Inbox(uint16_t type) : diff --git a/src/items/containers/mailbox/mailbox.cpp b/src/items/containers/mailbox/mailbox.cpp index 4bba415ada0..9abe6d0f6a3 100644 --- a/src/items/containers/mailbox/mailbox.cpp +++ b/src/items/containers/mailbox/mailbox.cpp @@ -8,9 +8,11 @@ */ #include "items/containers/mailbox/mailbox.hpp" + +#include "creatures/players/player.hpp" #include "game/game.hpp" -#include "io/iologindata.hpp" #include "game/scheduling/save_manager.hpp" +#include "items/containers/inbox/inbox.hpp" #include "map/spectators.hpp" ReturnValue Mailbox::queryAdd(int32_t, const std::shared_ptr &thing, uint32_t, uint32_t, const std::shared_ptr &) { diff --git a/src/items/cylinder.hpp b/src/items/cylinder.hpp index 4a0246b81f4..c858d398620 100644 --- a/src/items/cylinder.hpp +++ b/src/items/cylinder.hpp @@ -9,7 +9,7 @@ #pragma once -#include "declarations.hpp" +#include "items/items_definitions.hpp" #include "items/thing.hpp" class Item; diff --git a/src/items/decay/decay.cpp b/src/items/decay/decay.cpp index 52cee455830..5421340d9ad 100644 --- a/src/items/decay/decay.cpp +++ b/src/items/decay/decay.cpp @@ -9,9 +9,10 @@ #include "items/decay/decay.hpp" -#include "lib/di/container.hpp" +#include "creatures/players/player.hpp" #include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" +#include "lib/di/container.hpp" Decay &Decay::getInstance() { return inject(); diff --git a/src/items/functions/item/attribute.cpp b/src/items/functions/item/attribute.cpp index f960dbeb00e..3c205f83ae6 100644 --- a/src/items/functions/item/attribute.cpp +++ b/src/items/functions/item/attribute.cpp @@ -9,6 +9,8 @@ #include "items/functions/item/attribute.hpp" +#include "utils/tools.hpp" + /* ============================= * ItemAttribute class (Attributes methods) diff --git a/src/items/functions/item/attribute.hpp b/src/items/functions/item/attribute.hpp index b89920a4f18..3d99187d93a 100644 --- a/src/items/functions/item/attribute.hpp +++ b/src/items/functions/item/attribute.hpp @@ -11,7 +11,6 @@ #include "enums/item_attribute.hpp" #include "items/functions/item/custom_attribute.hpp" -#include "utils/tools.hpp" class ItemAttributeHelper { public: diff --git a/src/items/functions/item/item_parse.cpp b/src/items/functions/item/item_parse.cpp index 8167e301425..761740bfc2d 100644 --- a/src/items/functions/item/item_parse.cpp +++ b/src/items/functions/item/item_parse.cpp @@ -8,9 +8,12 @@ */ #include "items/functions/item/item_parse.hpp" + +#include "config/configmanager.hpp" #include "items/weapons/weapons.hpp" #include "lua/creature/movement.hpp" #include "utils/pugicast.hpp" +#include "utils/tools.hpp" #include "creatures/combat/combat.hpp" void ItemParse::initParse(const std::string &stringValue, pugi::xml_node attributeNode, pugi::xml_attribute valueAttribute, ItemType &itemType) { diff --git a/src/items/item.cpp b/src/items/item.cpp index 7439d1edefc..2fbec7c84f1 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -8,18 +8,25 @@ */ #include "items/item.hpp" + +#include "config/configmanager.hpp" +#include "containers/rewards/rewardchest.hpp" +#include "creatures/combat/combat.hpp" +#include "creatures/combat/spells.hpp" +#include "creatures/players/imbuements/imbuements.hpp" +#include "creatures/players/player.hpp" +#include "creatures/players/vocations/vocation.hpp" +#include "enums/object_category.hpp" +#include "game/game.hpp" +#include "game/movement/teleport.hpp" +#include "items/bed.hpp" #include "items/containers/container.hpp" +#include "items/containers/depot/depotlocker.hpp" +#include "items/containers/mailbox/mailbox.hpp" #include "items/decay/decay.hpp" -#include "game/movement/teleport.hpp" #include "items/trashholder.hpp" -#include "items/containers/mailbox/mailbox.hpp" -#include "map/house/house.hpp" -#include "game/game.hpp" -#include "items/bed.hpp" -#include "containers/rewards/rewardchest.hpp" -#include "creatures/players/imbuements/imbuements.hpp" #include "lua/creature/actions.hpp" -#include "creatures/combat/spells.hpp" +#include "map/house/house.hpp" #define ITEM_IMBUEMENT_SLOT 500 @@ -138,6 +145,80 @@ bool Item::hasImbuementCategoryId(uint16_t categoryId) const { return false; } +double Item::getDodgeChance() const { + if (getTier() == 0) { + return 0; + } + return quadraticPoly( + g_configManager().getFloat(RUSE_CHANCE_FORMULA_A), + g_configManager().getFloat(RUSE_CHANCE_FORMULA_B), + g_configManager().getFloat(RUSE_CHANCE_FORMULA_C), + getTier() + ); +} + +double Item::getFatalChance() const { + if (getTier() == 0) { + return 0; + } + return quadraticPoly( + g_configManager().getFloat(ONSLAUGHT_CHANCE_FORMULA_A), + g_configManager().getFloat(ONSLAUGHT_CHANCE_FORMULA_B), + g_configManager().getFloat(ONSLAUGHT_CHANCE_FORMULA_C), + getTier() + ); +} + +double Item::getMomentumChance() const { + if (getTier() == 0) { + return 0; + } + return quadraticPoly( + g_configManager().getFloat(MOMENTUM_CHANCE_FORMULA_A), + g_configManager().getFloat(MOMENTUM_CHANCE_FORMULA_B), + g_configManager().getFloat(MOMENTUM_CHANCE_FORMULA_C), + getTier() + ); +} + +double Item::getTranscendenceChance() const { + if (getTier() == 0) { + return 0; + } + return quadraticPoly( + g_configManager().getFloat(TRANSCENDANCE_CHANCE_FORMULA_A), + g_configManager().getFloat(TRANSCENDANCE_CHANCE_FORMULA_B), + g_configManager().getFloat(TRANSCENDANCE_CHANCE_FORMULA_C), + getTier() + ); +} + +uint8_t Item::getTier() const { + if (!hasAttribute(ItemAttribute_t::TIER)) { + return 0; + } + + auto tier = getAttribute(ItemAttribute_t::TIER); + if (tier > g_configManager().getNumber(FORGE_MAX_ITEM_TIER)) { + g_logger().error("{} - Item {} have a wrong tier {}", __FUNCTION__, getName(), tier); + return 0; + } + + return tier; +} + +void Item::setTier(uint8_t tier) { + auto configTier = g_configManager().getNumber(FORGE_MAX_ITEM_TIER); + if (tier > configTier) { + g_logger().error("{} - It is not possible to set a tier higher than {}", __FUNCTION__, configTier); + return; + } + + if (items[id].upgradeClassification) { + setAttribute(ItemAttribute_t::TIER, tier); + } +} + std::shared_ptr Item::CreateItemAsContainer(const uint16_t type, uint16_t size) { if (const ItemType &it = Item::items[type]; it.id == 0 @@ -366,6 +447,14 @@ std::shared_ptr Item::getTile() { return std::dynamic_pointer_cast(cylinder); } +bool Item::isRemoved() { + auto parent = getParent(); + if (parent) { + return parent->isRemoved(); + } + return true; +} + uint16_t Item::getSubType() const { const ItemType &it = items[id]; if (it.isFluidContainer() || it.isSplash()) { @@ -1130,6 +1219,18 @@ uint32_t Item::getWeight() const { return baseWeight; } +int32_t Item::getReflectionFlat(CombatType_t combatType) const { + return items[id].abilities->reflectFlat[combatTypeToIndex(combatType)]; +} + +int32_t Item::getReflectionPercent(CombatType_t combatType) const { + return items[id].abilities->reflectPercent[combatTypeToIndex(combatType)]; +} + +int32_t Item::getSpecializedMagicLevel(CombatType_t combat) const { + return items[id].abilities->specializedMagicLevel[combatTypeToIndex(combat)]; +} + std::vector> Item::getDescriptions(const ItemType &it, const std::shared_ptr &item /*= nullptr*/) { std::ostringstream ss; @@ -3312,3 +3413,22 @@ void Item::updateTileFlags() { tile->updateTileFlags(static_self_cast()); } } + +// Custom Attributes + +const std::map> &ItemProperties::getCustomAttributeMap() const { + static std::map> map = {}; + if (!attributePtr) { + return map; + } + return attributePtr->getCustomAttributeMap(); +} + +int32_t ItemProperties::getDuration() const { + ItemDecayState_t decayState = getDecaying(); + if (decayState == DECAYING_TRUE || decayState == DECAYING_STOPPING) { + return std::max(0, getAttribute(ItemAttribute_t::DURATION_TIMESTAMP) - static_cast(OTSYS_TIME())); + } else { + return getAttribute(ItemAttribute_t::DURATION); + } +} diff --git a/src/items/item.hpp b/src/items/item.hpp index 8234d8890df..d199fab15cf 100644 --- a/src/items/item.hpp +++ b/src/items/item.hpp @@ -9,14 +9,11 @@ #pragma once -#include "items/cylinder.hpp" -#include "items/thing.hpp" #include "enums/item_attribute.hpp" -#include "items/items.hpp" -#include "items/functions/item/attribute.hpp" -#include "lua/scripts/luascript.hpp" -#include "utils/tools.hpp" #include "io/fileloader.hpp" +#include "items/functions/item/attribute.hpp" +#include "items/items.hpp" +#include "items/thing.hpp" class Creature; class Player; @@ -30,6 +27,7 @@ class MagicField; class BedItem; class Imbuement; class Item; +class Cylinder; // This class ItemProperties that serves as an interface to access and modify attributes of an item. The item's attributes are stored in an instance of ItemAttribute. The class ItemProperties has methods to get and set integer and string attributes, check if an attribute exists, remove an attribute, get the underlying attribute bits, and get a vector of attributes. It also has methods to get and set custom attributes, which are stored in a std::map>. The class has a data member attributePtr of type std::unique_ptr that stores a pointer to the item's attributes methods. class ItemProperties { @@ -76,13 +74,7 @@ class ItemProperties { } // Custom Attributes - const std::map> &getCustomAttributeMap() const { - static std::map> map = {}; - if (!attributePtr) { - return map; - } - return attributePtr->getCustomAttributeMap(); - } + const std::map> &getCustomAttributeMap() const; const CustomAttribute* getCustomAttribute(const std::string &attributeName) const { if (!attributePtr) { return nullptr; @@ -116,14 +108,7 @@ class ItemProperties { return getAttribute(ItemAttribute_t::CHARGES); } - int32_t getDuration() const { - const ItemDecayState_t decayState = getDecaying(); - if (decayState == DECAYING_TRUE || decayState == DECAYING_STOPPING) { - return std::max(0, getAttribute(ItemAttribute_t::DURATION_TIMESTAMP) - static_cast(OTSYS_TIME())); - } else { - return getAttribute(ItemAttribute_t::DURATION); - } - } + int32_t getDuration() const; bool isStoreItem() const { return getAttribute(ItemAttribute_t::STORE) > 0; @@ -366,13 +351,9 @@ class Item : virtual public Thing, public ItemProperties, public SharedObject { return items[id].abilities->perfectShotRange; } - int32_t getReflectionFlat(CombatType_t combatType) const { - return items[id].abilities->reflectFlat[combatTypeToIndex(combatType)]; - } + int32_t getReflectionFlat(CombatType_t combatType) const; - int32_t getReflectionPercent(CombatType_t combatType) const { - return items[id].abilities->reflectPercent[combatTypeToIndex(combatType)]; - } + int32_t getReflectionPercent(CombatType_t combatType) const; int16_t getMagicShieldCapacityPercent() const { return items[id].abilities->magicShieldCapacityPercent; @@ -382,9 +363,7 @@ class Item : virtual public Thing, public ItemProperties, public SharedObject { return items[id].abilities->magicShieldCapacityFlat; } - int32_t getSpecializedMagicLevel(CombatType_t combat) const { - return items[id].abilities->specializedMagicLevel[combatTypeToIndex(combat)]; - } + int32_t getSpecializedMagicLevel(CombatType_t combat) const; int32_t getSpeed() const { const int32_t value = items[id].getSpeed(); @@ -662,13 +641,7 @@ class Item : virtual public Thing, public ItemProperties, public SharedObject { } std::shared_ptr getTopParent(); std::shared_ptr getTile() override; - bool isRemoved() override { - const auto &parent = getParent(); - if (parent) { - return parent->isRemoved(); - } - return true; - } + bool isRemoved() override; bool isInsideDepot(bool includeInbox = false); @@ -714,78 +687,16 @@ class Item : virtual public Thing, public ItemProperties, public SharedObject { return false; } - double getDodgeChance() const { - if (getTier() == 0) { - return 0; - } - return quadraticPoly( - g_configManager().getFloat(RUSE_CHANCE_FORMULA_A), - g_configManager().getFloat(RUSE_CHANCE_FORMULA_B), - g_configManager().getFloat(RUSE_CHANCE_FORMULA_C), - getTier() - ); - } + double getDodgeChance() const; - double getFatalChance() const { - if (getTier() == 0) { - return 0; - } - return quadraticPoly( - g_configManager().getFloat(ONSLAUGHT_CHANCE_FORMULA_A), - g_configManager().getFloat(ONSLAUGHT_CHANCE_FORMULA_B), - g_configManager().getFloat(ONSLAUGHT_CHANCE_FORMULA_C), - getTier() - ); - } - - double getMomentumChance() const { - if (getTier() == 0) { - return 0; - } - return quadraticPoly( - g_configManager().getFloat(MOMENTUM_CHANCE_FORMULA_A), - g_configManager().getFloat(MOMENTUM_CHANCE_FORMULA_B), - g_configManager().getFloat(MOMENTUM_CHANCE_FORMULA_C), - getTier() - ); - } - - double getTranscendenceChance() const { - if (getTier() == 0) { - return 0; - } - return quadraticPoly( - g_configManager().getFloat(TRANSCENDANCE_CHANCE_FORMULA_A), - g_configManager().getFloat(TRANSCENDANCE_CHANCE_FORMULA_B), - g_configManager().getFloat(TRANSCENDANCE_CHANCE_FORMULA_C), - getTier() - ); - } + double getFatalChance() const; - uint8_t getTier() const { - if (!hasAttribute(ItemAttribute_t::TIER)) { - return 0; - } - - auto tier = getAttribute(ItemAttribute_t::TIER); - if (tier > g_configManager().getNumber(FORGE_MAX_ITEM_TIER)) { - g_logger().error("{} - Item {} have a wrong tier {}", __FUNCTION__, getName(), tier); - return 0; - } + double getMomentumChance() const; - return tier; - } - void setTier(uint8_t tier) { - auto configTier = g_configManager().getNumber(FORGE_MAX_ITEM_TIER); - if (tier > configTier) { - g_logger().error("{} - It is not possible to set a tier higher than {}", __FUNCTION__, configTier); - return; - } + double getTranscendenceChance() const; - if (items[id].upgradeClassification) { - setAttribute(ItemAttribute_t::TIER, tier); - } - } + uint8_t getTier() const; + void setTier(uint8_t tier); uint8_t getClassification() const { return items[id].upgradeClassification; } diff --git a/src/items/items.cpp b/src/items/items.cpp index c498ef9d8fb..69ce46ced92 100644 --- a/src/items/items.cpp +++ b/src/items/items.cpp @@ -7,12 +7,15 @@ * Website: https://docs.opentibiabr.com/ */ -#include "items/functions/item/item_parse.hpp" #include "items/items.hpp" + +#include "config/configmanager.hpp" +#include "game/game.hpp" +#include "items/functions/item/item_parse.hpp" #include "items/weapons/weapons.hpp" #include "lua/creature/movement.hpp" -#include "game/game.hpp" #include "utils/pugicast.hpp" +#include "utils/tools.hpp" #include @@ -106,6 +109,15 @@ std::string ItemType::getFormattedAugmentDescription(const std::shared_ptr {:+}% {}", augmentSpellNameCapitalized, augmentInfo->value, augmentName); } +void ItemType::addAugment(std::string spellName, Augment_t augmentType, int32_t value) { + auto augmentInfo = std::make_shared(spellName, augmentType, value); + augments.emplace_back(augmentInfo); +} + +void ItemType::setImbuementType(ImbuementTypes_t imbuementType, uint16_t slotMaxTier) { + imbuementTypes[imbuementType] = std::min(IMBUEMENT_MAX_TIER, slotMaxTier); +} + bool Items::reload() { clear(); loadFromProtobuf(); @@ -381,3 +393,19 @@ bool Items::hasItemType(size_t hasId) const { } return false; } + +uint32_t Abilities::getHealthGain() const { + return healthGain * g_configManager().getFloat(RATE_HEALTH_REGEN); +} + +uint32_t Abilities::getHealthTicks() const { + return healthTicks / g_configManager().getFloat(RATE_HEALTH_REGEN_SPEED); +} + +uint32_t Abilities::getManaGain() const { + return manaGain * g_configManager().getFloat(RATE_MANA_REGEN); +} + +uint32_t Abilities::getManaTicks() const { + return manaTicks / g_configManager().getFloat(RATE_MANA_REGEN_SPEED); +} diff --git a/src/items/items.hpp b/src/items/items.hpp index d8096b9d104..b977f0bf7a9 100644 --- a/src/items/items.hpp +++ b/src/items/items.hpp @@ -9,14 +9,13 @@ #pragma once -#include "config/configmanager.hpp" -#include "config/config_enums.hpp" -#include "utils/utils_definitions.hpp" -#include "declarations.hpp" +#include "creatures/creatures_definitions.hpp" #include "game/movement/position.hpp" +#include "items/items_definitions.hpp" +#include "utils/utils_definitions.hpp" +#include "enums/item_attribute.hpp" struct Abilities { -public: std::array conditionImmunities = {}; std::array conditionSuppressions = {}; @@ -66,35 +65,26 @@ struct Abilities { healthGain = value; } - uint32_t getHealthGain() const { - return healthGain * g_configManager().getFloat(RATE_HEALTH_REGEN); - } + uint32_t getHealthGain() const; void setHealthTicks(uint32_t value) { healthTicks = value; } - uint32_t getHealthTicks() const { - return healthTicks / g_configManager().getFloat(RATE_HEALTH_REGEN_SPEED); - } + uint32_t getHealthTicks() const; void setManaGain(uint32_t value) { manaGain = value; } - uint32_t getManaGain() const { - return manaGain * g_configManager().getFloat(RATE_MANA_REGEN); - } + uint32_t getManaGain() const; void setManaTicks(uint32_t value) { manaTicks = value; } - uint32_t getManaTicks() const { - return manaTicks / g_configManager().getFloat(RATE_MANA_REGEN_SPEED); - } + uint32_t getManaTicks() const; -private: uint32_t healthGain = 0; uint32_t healthTicks = 0; uint32_t manaGain = 0; @@ -256,14 +246,9 @@ class ItemType { std::string parseAugmentDescription(bool inspect = false) const; std::string getFormattedAugmentDescription(const std::shared_ptr &augmentInfo) const; - void addAugment(const std::string &spellName, Augment_t augmentType, int32_t value) { - auto augmentInfo = std::make_shared(spellName, augmentType, value); - augments.emplace_back(augmentInfo); - } + void addAugment(std::string spellName, Augment_t augmentType, int32_t value); - void setImbuementType(ImbuementTypes_t imbuementType, uint16_t slotMaxTier) { - imbuementTypes[imbuementType] = std::min(IMBUEMENT_MAX_TIER, slotMaxTier); - } + void setImbuementType(ImbuementTypes_t imbuementType, uint16_t slotMaxTier); ItemGroup_t group = ITEM_GROUP_NONE; ItemTypes_t type = ITEM_TYPE_NONE; diff --git a/src/items/items_definitions.hpp b/src/items/items_definitions.hpp index 35c6b0f293d..403200e231a 100644 --- a/src/items/items_definitions.hpp +++ b/src/items/items_definitions.hpp @@ -458,7 +458,7 @@ enum TileFlags_t : uint32_t { TILESTATE_FLOORCHANGE = TILESTATE_FLOORCHANGE_DOWN | TILESTATE_FLOORCHANGE_NORTH | TILESTATE_FLOORCHANGE_SOUTH | TILESTATE_FLOORCHANGE_EAST | TILESTATE_FLOORCHANGE_WEST | TILESTATE_FLOORCHANGE_SOUTH_ALT | TILESTATE_FLOORCHANGE_EAST_ALT, }; -enum ZoneType_t { +enum ZoneType_t : uint8_t { ZONE_PROTECTION, ZONE_NOPVP, ZONE_PVP, diff --git a/src/items/thing.cpp b/src/items/thing.cpp index b4c1275d933..4c2f1d5a88c 100644 --- a/src/items/thing.cpp +++ b/src/items/thing.cpp @@ -8,6 +8,7 @@ */ #include "items/thing.hpp" + #include "items/tile.hpp" const Position &Thing::getPosition() { diff --git a/src/items/tile.cpp b/src/items/tile.cpp index efb1a2401b4..f15e2bd8a94 100644 --- a/src/items/tile.cpp +++ b/src/items/tile.cpp @@ -8,18 +8,21 @@ */ #include "items/tile.hpp" -#include "creatures/creature.hpp" + +#include "config/configmanager.hpp" #include "creatures/combat/combat.hpp" +#include "creatures/creature.hpp" +#include "creatures/monsters/monster.hpp" +#include "creatures/players/player.hpp" +#include "enums/account_type.hpp" #include "game/game.hpp" +#include "game/movement/teleport.hpp" #include "game/zones/zone.hpp" #include "items/containers/mailbox/mailbox.hpp" -#include "creatures/monsters/monster.hpp" -#include "lua/creature/movement.hpp" -#include "game/movement/teleport.hpp" #include "items/trashholder.hpp" -#include "io/iomap.hpp" +#include "lua/creature/movement.hpp" #include "map/spectators.hpp" -#include "enums/account_type.hpp" +#include "utils/tools.hpp" auto real_nullptr_tile = std::make_shared(0xFFFF, 0xFFFF, 0xFF); const std::shared_ptr &Tile::nullptr_tile = real_nullptr_tile; @@ -82,6 +85,10 @@ bool Tile::hasProperty(const std::shared_ptr &exclude, ItemProperty prop) return false; } +bool Tile::hasFlag(uint32_t flag) const { + return hasBitSet(flag, this->flags); +} + bool Tile::hasHeight(uint32_t n) const { uint32_t height = 0; diff --git a/src/items/tile.hpp b/src/items/tile.hpp index 3bd0017ed38..4137b6e115d 100644 --- a/src/items/tile.hpp +++ b/src/items/tile.hpp @@ -10,9 +10,6 @@ #pragma once #include "items/cylinder.hpp" -#include "declarations.hpp" -#include "items/item.hpp" -#include "utils/tools.hpp" class Creature; class Teleport; @@ -22,6 +19,9 @@ class MagicField; class BedItem; class House; class Zone; +class Cylinder; +class Item; +class ItemType; using CreatureVector = std::vector>; using ItemVector = std::vector>; @@ -169,9 +169,7 @@ class Tile : public Cylinder, public SharedObject { bool hasProperty(ItemProperty prop) const; bool hasProperty(const std::shared_ptr &exclude, ItemProperty prop) const; - bool hasFlag(uint32_t flag) const { - return hasBitSet(flag, this->flags); - } + bool hasFlag(uint32_t flag) const; void setFlag(uint32_t flag) { this->flags |= flag; } diff --git a/src/items/trashholder.cpp b/src/items/trashholder.cpp index 42fbe4db4e6..193d29b794e 100644 --- a/src/items/trashholder.cpp +++ b/src/items/trashholder.cpp @@ -8,6 +8,7 @@ */ #include "items/trashholder.hpp" + #include "game/game.hpp" ReturnValue TrashHolder::queryAdd(int32_t, const std::shared_ptr &thing, uint32_t, uint32_t, const std::shared_ptr &actor) { diff --git a/src/items/weapons/weapons.cpp b/src/items/weapons/weapons.cpp index f0db2ca4f30..81e59f6a739 100644 --- a/src/items/weapons/weapons.cpp +++ b/src/items/weapons/weapons.cpp @@ -7,10 +7,12 @@ * Website: https://docs.opentibiabr.com/ */ +#include "items/weapons/weapons.hpp" + +#include "config/configmanager.hpp" #include "creatures/combat/combat.hpp" #include "game/game.hpp" #include "lua/creature/events.hpp" -#include "items/weapons/weapons.hpp" #include "lua/global/lua_variant.hpp" diff --git a/src/kv/value_wrapper.cpp b/src/kv/value_wrapper.cpp index 90d86b40358..69fb942bab4 100644 --- a/src/kv/value_wrapper.cpp +++ b/src/kv/value_wrapper.cpp @@ -8,6 +8,7 @@ */ #include "kv/value_wrapper.hpp" + #include "utils/tools.hpp" ValueWrapper::ValueWrapper(uint64_t timestamp) : diff --git a/src/lib/di/soft_singleton.cpp b/src/lib/di/soft_singleton.cpp index 8fb90288cee..8a2add1c026 100644 --- a/src/lib/di/soft_singleton.cpp +++ b/src/lib/di/soft_singleton.cpp @@ -7,6 +7,7 @@ * Website: https://docs.opentibiabr.com/ */ #include "lib/di/soft_singleton.hpp" + #include "utils/tools.hpp" SoftSingleton::SoftSingleton(std::string id) : diff --git a/src/lib/di/soft_singleton.hpp b/src/lib/di/soft_singleton.hpp index b64df1c77b6..99ef85fe9c9 100644 --- a/src/lib/di/soft_singleton.hpp +++ b/src/lib/di/soft_singleton.hpp @@ -6,9 +6,8 @@ * Contributors: https://github.com/opentibiabr/canary/graphs/contributors * Website: https://docs.opentibiabr.com/ */ -#pragma once -#include +#pragma once class SoftSingleton { public: diff --git a/src/lib/metrics/metrics.cpp b/src/lib/metrics/metrics.cpp index cf11060125a..a6fa18063aa 100644 --- a/src/lib/metrics/metrics.cpp +++ b/src/lib/metrics/metrics.cpp @@ -8,7 +8,8 @@ * Website: https://docs.opentibiabr.com/ */ - #include "metrics.hpp" + #include "lib/metrics/metrics.hpp" + #include "lib/di/container.hpp" using namespace metrics; diff --git a/src/lua/callbacks/creaturecallback.cpp b/src/lua/callbacks/creaturecallback.cpp index 21c4556cdfd..6f279f46d98 100644 --- a/src/lua/callbacks/creaturecallback.cpp +++ b/src/lua/callbacks/creaturecallback.cpp @@ -9,6 +9,9 @@ #include "lua/callbacks/creaturecallback.hpp" +#include "creatures/creature.hpp" +#include "lua/scripts/luascript.hpp" + bool CreatureCallback::startScriptInterface(int32_t scriptId) { if (scriptId == -1) { return false; @@ -49,6 +52,36 @@ void CreatureCallback::pushSpecificCreature(const std::shared_ptr &cre LuaScriptInterface::setMetatable(L, -1, getCreatureClass(creature)); } +bool CreatureCallback::persistLuaState() const { + return params > 0 && scriptInterface->callFunction(params); +} + +void CreatureCallback::pushCreature(const std::shared_ptr &creature) { + params++; + LuaScriptInterface::pushUserdata(L, creature); + LuaScriptInterface::setCreatureMetatable(L, -1, creature); +} + +void CreatureCallback::pushPosition(const Position &position, int32_t stackpos) { + params++; + LuaScriptInterface::pushPosition(L, position, stackpos); +} + +void CreatureCallback::pushNumber(int32_t number) { + params++; + lua_pushnumber(L, number); +} + +void CreatureCallback::pushString(const std::string &str) { + params++; + LuaScriptInterface::pushString(L, str); +} + +void CreatureCallback::pushBoolean(const bool str) { + params++; + LuaScriptInterface::pushBoolean(L, str); +} + std::string CreatureCallback::getCreatureClass(const std::shared_ptr &creature) { if (!creature) { return ""; diff --git a/src/lua/callbacks/creaturecallback.hpp b/src/lua/callbacks/creaturecallback.hpp index cb2e5c4c189..6922b43a87c 100644 --- a/src/lua/callbacks/creaturecallback.hpp +++ b/src/lua/callbacks/creaturecallback.hpp @@ -9,9 +9,10 @@ #pragma once -#include "creatures/creature.hpp" - class Creature; +class LuaScriptInterface; + +struct Position; class CreatureCallback { public: @@ -23,35 +24,17 @@ class CreatureCallback { void pushSpecificCreature(const std::shared_ptr &creature); - bool persistLuaState() const { - return params > 0 && scriptInterface->callFunction(params); - } + bool persistLuaState() const; - void pushCreature(const std::shared_ptr &creature) { - params++; - LuaScriptInterface::pushUserdata(L, creature); - LuaScriptInterface::setCreatureMetatable(L, -1, creature); - } + void pushCreature(const std::shared_ptr &creature); - void pushPosition(const Position &position, int32_t stackpos = 0) { - params++; - LuaScriptInterface::pushPosition(L, position, stackpos); - } + void pushPosition(const Position &position, int32_t stackpos = 0); - void pushNumber(int32_t number) { - params++; - lua_pushnumber(L, number); - } + void pushNumber(int32_t number); - void pushString(const std::string &str) { - params++; - LuaScriptInterface::pushString(L, str); - } + void pushString(const std::string &str); - void pushBoolean(const bool str) { - params++; - LuaScriptInterface::pushBoolean(L, str); - } + void pushBoolean(const bool str); protected: static std::string getCreatureClass(const std::shared_ptr &creature); diff --git a/src/lua/callbacks/event_callback.cpp b/src/lua/callbacks/event_callback.cpp index 0748333fc93..3d95543f1b9 100644 --- a/src/lua/callbacks/event_callback.cpp +++ b/src/lua/callbacks/event_callback.cpp @@ -9,10 +9,11 @@ #include "lua/callbacks/event_callback.hpp" -#include "utils/tools.hpp" -#include "items/item.hpp" +#include "creatures/players/grouping/party.hpp" #include "creatures/players/player.hpp" #include "game/zones/zone.hpp" +#include "items/containers/container.hpp" +#include "items/item.hpp" /** * @class EventCallback diff --git a/src/lua/creature/actions.cpp b/src/lua/creature/actions.cpp index 3673b7c112e..b79cb5d03c7 100644 --- a/src/lua/creature/actions.cpp +++ b/src/lua/creature/actions.cpp @@ -8,12 +8,17 @@ */ #include "lua/creature/actions.hpp" + +#include "config/configmanager.hpp" +#include "creatures/combat/spells.hpp" +#include "creatures/players/player.hpp" +#include "enums/account_group_type.hpp" +#include "game/game.hpp" #include "items/bed.hpp" #include "items/containers/container.hpp" -#include "game/game.hpp" -#include "creatures/combat/spells.hpp" +#include "items/containers/depot/depotlocker.hpp" +#include "items/containers/rewards/reward.hpp" #include "items/containers/rewards/rewardchest.hpp" -#include "enums/account_group_type.hpp" Actions::Actions() = default; Actions::~Actions() = default; diff --git a/src/lua/creature/creatureevent.cpp b/src/lua/creature/creatureevent.cpp index 48ca3fa52b3..575394b605c 100644 --- a/src/lua/creature/creatureevent.cpp +++ b/src/lua/creature/creatureevent.cpp @@ -8,8 +8,9 @@ */ #include "lua/creature/creatureevent.hpp" -#include "utils/tools.hpp" + #include "creatures/players/player.hpp" +#include "items/item.hpp" void CreatureEvents::clear() { for (const auto &[name, event] : creatureEvents) { @@ -53,6 +54,10 @@ std::shared_ptr CreatureEvents::getEventByName(const std::string return nullptr; } +CreatureEvents &CreatureEvents::getInstance() { + return inject(); +} + bool CreatureEvents::playerLogin(const std::shared_ptr &player) const { // fire global event if is registered for (const auto &it : creatureEvents) { diff --git a/src/lua/creature/creatureevent.hpp b/src/lua/creature/creatureevent.hpp index 06d6a21fb77..5000a4b2e0a 100644 --- a/src/lua/creature/creatureevent.hpp +++ b/src/lua/creature/creatureevent.hpp @@ -9,12 +9,10 @@ #pragma once -#include "declarations.hpp" -#include "lib/di/container.hpp" -#include "lua/scripts/luascript.hpp" #include "lua/scripts/scripts.hpp" class CreatureEvent; +class LuaScriptInterface; class CreatureEvent final : public Script { public: @@ -73,9 +71,7 @@ class CreatureEvents final : public Scripts { CreatureEvents(const CreatureEvents &) = delete; CreatureEvents &operator=(const CreatureEvents &) = delete; - static CreatureEvents &getInstance() { - return inject(); - } + static CreatureEvents &getInstance(); // global events bool playerLogin(const std::shared_ptr &player) const; diff --git a/src/lua/creature/events.cpp b/src/lua/creature/events.cpp index 2057a26604c..57a8ff45d31 100644 --- a/src/lua/creature/events.cpp +++ b/src/lua/creature/events.cpp @@ -8,9 +8,16 @@ */ #include "lua/creature/events.hpp" -#include "utils/tools.hpp" -#include "items/item.hpp" + +#include "config/configmanager.hpp" +#include "creatures/monsters/monster.hpp" +#include "creatures/players/grouping/party.hpp" #include "creatures/players/player.hpp" +#include "game/movement/position.hpp" +#include "items/containers/container.hpp" +#include "items/item.hpp" +#include "lib/di/container.hpp" +#include "utils/tools.hpp" Events::Events() : scriptInterface("Event Interface") { @@ -215,6 +222,10 @@ void Events::eventNpcOnSpawn(const std::shared_ptr &npc, const Position &po LuaScriptInterface::resetScriptEnv(); } +Events &Events::getInstance() { + return inject(); +} + // Creature bool Events::eventCreatureOnChangeOutfit(const std::shared_ptr &creature, const Outfit_t &outfit) { // Creature:onChangeOutfit(outfit) or Creature.onChangeOutfit(self, outfit) diff --git a/src/lua/creature/events.hpp b/src/lua/creature/events.hpp index d76503fe425..a74cbaf3ccf 100644 --- a/src/lua/creature/events.hpp +++ b/src/lua/creature/events.hpp @@ -9,14 +9,31 @@ #pragma once -#include "creatures/players/imbuements/imbuements.hpp" #include "lua/scripts/luascript.hpp" -#include "creatures/combat/spells.hpp" +enum ReturnValue : uint16_t; +enum SpeakClasses : uint8_t; +enum TextColor_t : uint8_t; +enum CombatType_t : uint8_t; +enum Direction : uint8_t; +enum skills_t : int8_t; +enum Slots_t : uint8_t; +enum ZoneType_t : uint8_t; +struct Position; +struct Outfit_t; +struct CombatDamage; class Party; class ItemType; -class Tile; class Imbuements; +class Monster; +class Player; +class Item; +class Creature; +class Npc; +class Tile; +class Thing; +class Cylinder; +class Container; class Events { struct EventsInfo { @@ -76,9 +93,7 @@ class Events { Events(const Events &) = delete; void operator=(const Events &) = delete; - static Events &getInstance() { - return inject(); - } + static Events &getInstance(); // Creature bool eventCreatureOnChangeOutfit(const std::shared_ptr &creature, const Outfit_t &outfit); diff --git a/src/lua/creature/movement.cpp b/src/lua/creature/movement.cpp index 9a08affe324..684f0844ee1 100644 --- a/src/lua/creature/movement.cpp +++ b/src/lua/creature/movement.cpp @@ -7,11 +7,15 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/creature/movement.hpp" + +#include "creatures/combat/combat.hpp" +#include "creatures/combat/condition.hpp" +#include "creatures/players/player.hpp" #include "game/game.hpp" -#include "lua/creature/events.hpp" #include "lua/callbacks/event_callback.hpp" #include "lua/callbacks/events_callbacks.hpp" -#include "lua/creature/movement.hpp" +#include "lua/creature/events.hpp" void MoveEvents::clear() { uniqueIdMap.clear(); diff --git a/src/lua/creature/raids.cpp b/src/lua/creature/raids.cpp index f9d000c6fdd..7b262402bb1 100644 --- a/src/lua/creature/raids.cpp +++ b/src/lua/creature/raids.cpp @@ -8,11 +8,13 @@ */ #include "lua/creature/raids.hpp" -#include "utils/pugicast.hpp" + +#include "config/configmanager.hpp" +#include "creatures/monsters/monster.hpp" #include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" -#include "creatures/monsters/monster.hpp" #include "server/network/webhook/webhook.hpp" +#include "utils/pugicast.hpp" Raids::Raids() { scriptInterface.initState(); diff --git a/src/lua/creature/raids.hpp b/src/lua/creature/raids.hpp index 0a352e897eb..d2f45536b4b 100644 --- a/src/lua/creature/raids.hpp +++ b/src/lua/creature/raids.hpp @@ -9,10 +9,11 @@ #pragma once -#include "utils/utils_definitions.hpp" -#include "declarations.hpp" -#include "game/movement/position.hpp" #include "lua/global/baseevents.hpp" +#include "lua/scripts/luascript.hpp" +#include "utils/utils_definitions.hpp" + +struct Position; struct MonsterSpawn { MonsterSpawn(std::string initName, uint32_t initMinAmount, uint32_t initMaxAmount) : diff --git a/src/lua/creature/talkaction.cpp b/src/lua/creature/talkaction.cpp index d1b8b634f59..77323d9acf2 100644 --- a/src/lua/creature/talkaction.cpp +++ b/src/lua/creature/talkaction.cpp @@ -7,9 +7,12 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/creature/talkaction.hpp" + +#include "utils/tools.hpp" +#include "creatures/players/grouping/groups.hpp" #include "creatures/players/player.hpp" #include "lua/scripts/scripts.hpp" -#include "lua/creature/talkaction.hpp" TalkActions::TalkActions() = default; TalkActions::~TalkActions() = default; diff --git a/src/lua/functions/core/game/bank_functions.cpp b/src/lua/functions/core/game/bank_functions.cpp index e387a2d1a4c..ce608939304 100644 --- a/src/lua/functions/core/game/bank_functions.cpp +++ b/src/lua/functions/core/game/bank_functions.cpp @@ -8,6 +8,8 @@ */ #include "lua/functions/core/game/bank_functions.hpp" + +#include "creatures/players/player.hpp" #include "game/bank/bank.hpp" #include "game/game.hpp" diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index df123049df7..16133833bbc 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -7,26 +7,26 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/core/game/game_functions.hpp" + #include "core.hpp" #include "creatures/monsters/monster.hpp" +#include "creatures/monsters/monsters.hpp" +#include "creatures/npcs/npc.hpp" +#include "creatures/players/achievement/player_achievement.hpp" #include "game/functions/game_reload.hpp" #include "game/game.hpp" -#include "items/item.hpp" -#include "io/iobestiary.hpp" -#include "io/io_bosstiary.hpp" -#include "io/iologindata.hpp" -#include "lua/functions/core/game/game_functions.hpp" -#include "lua/functions/events/event_callback_functions.hpp" #include "game/scheduling/dispatcher.hpp" +#include "io/io_bosstiary.hpp" +#include "io/iobestiary.hpp" +#include "items/item.hpp" +#include "lua/callbacks/event_callback.hpp" +#include "lua/callbacks/events_callbacks.hpp" +#include "lua/creature/events.hpp" #include "lua/creature/talkaction.hpp" #include "lua/functions/creatures/npc/npc_type_functions.hpp" +#include "lua/functions/events/event_callback_functions.hpp" #include "lua/scripts/lua_environment.hpp" -#include "lua/creature/events.hpp" -#include "lua/callbacks/event_callback.hpp" -#include "lua/callbacks/events_callbacks.hpp" -#include "creatures/players/achievement/player_achievement.hpp" -#include "creatures/players/cyclopedia/player_badge.hpp" -#include "creatures/players/cyclopedia/player_cyclopedia.hpp" #include "map/spectators.hpp" // Game diff --git a/src/lua/functions/core/game/global_functions.cpp b/src/lua/functions/core/game/global_functions.cpp index 5681dd5d0c9..14d87e5afb2 100644 --- a/src/lua/functions/core/game/global_functions.cpp +++ b/src/lua/functions/core/game/global_functions.cpp @@ -7,19 +7,62 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/core/game/global_functions.hpp" + +#include "config/configmanager.hpp" +#include "creatures/combat/condition.hpp" #include "creatures/interactions/chat.hpp" +#include "creatures/players/wheel/player_wheel.hpp" #include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" #include "game/scheduling/save_manager.hpp" -#include "lua/functions/core/game/global_functions.hpp" +#include "items/containers/depot/depotlocker.hpp" +#include "lua/global/globalevent.hpp" +#include "lua/global/lua_timer_event_descr.hpp" #include "lua/scripts/lua_environment.hpp" #include "lua/scripts/script_environment.hpp" -#include "lua/global/globalevent.hpp" #include "server/network/protocol/protocolstatus.hpp" -#include "creatures/players/wheel/player_wheel.hpp" -#include "lua/global/lua_timer_event_descr.hpp" -class Creature; +void GlobalFunctions::init(lua_State* L) { + lua_register(L, "addEvent", GlobalFunctions::luaAddEvent); + lua_register(L, "cleanMap", GlobalFunctions::luaCleanMap); + lua_register(L, "createCombatArea", GlobalFunctions::luaCreateCombatArea); + lua_register(L, "debugPrint", GlobalFunctions::luaDebugPrint); + lua_register(L, "doAddContainerItem", GlobalFunctions::luaDoAddContainerItem); + lua_register(L, "doAreaCombatCondition", GlobalFunctions::luaDoAreaCombatCondition); + lua_register(L, "doAreaCombatDispel", GlobalFunctions::luaDoAreaCombatDispel); + lua_register(L, "doAreaCombatHealth", GlobalFunctions::luaDoAreaCombatHealth); + lua_register(L, "doAreaCombatMana", GlobalFunctions::luaDoAreaCombatMana); + lua_register(L, "doChallengeCreature", GlobalFunctions::luaDoChallengeCreature); + lua_register(L, "doPlayerAddItem", GlobalFunctions::luaDoPlayerAddItem); + lua_register(L, "doTargetCombatCondition", GlobalFunctions::luaDoTargetCombatCondition); + lua_register(L, "doTargetCombatDispel", GlobalFunctions::luaDoTargetCombatDispel); + lua_register(L, "doTargetCombatHealth", GlobalFunctions::luaDoTargetCombatHealth); + lua_register(L, "doTargetCombatMana", GlobalFunctions::luaDoTargetCombatMana); + lua_register(L, "getDepotId", GlobalFunctions::luaGetDepotId); + lua_register(L, "getWaypointPositionByName", GlobalFunctions::luaGetWaypointPositionByName); + lua_register(L, "getWorldLight", GlobalFunctions::luaGetWorldLight); + lua_register(L, "getWorldTime", GlobalFunctions::luaGetWorldTime); + lua_register(L, "getWorldUpTime", GlobalFunctions::luaGetWorldUpTime); + lua_register(L, "isDepot", GlobalFunctions::luaIsDepot); + lua_register(L, "isInWar", GlobalFunctions::luaIsInWar); + lua_register(L, "isMovable", GlobalFunctions::luaIsMovable); + lua_register(L, "isValidUID", GlobalFunctions::luaIsValidUID); + lua_register(L, "saveServer", GlobalFunctions::luaSaveServer); + lua_register(L, "sendChannelMessage", GlobalFunctions::luaSendChannelMessage); + lua_register(L, "sendGuildChannelMessage", GlobalFunctions::luaSendGuildChannelMessage); + lua_register(L, "stopEvent", GlobalFunctions::luaStopEvent); + + registerGlobalVariable(L, "INDEX_WHEREEVER", INDEX_WHEREEVER); + registerGlobalBoolean(L, "VIRTUAL_PARENT", true); + registerGlobalMethod(L, "isType", GlobalFunctions::luaIsType); + registerGlobalMethod(L, "rawgetmetatable", GlobalFunctions::luaRawGetMetatable); + registerGlobalMethod(L, "createTable", GlobalFunctions::luaCreateTable); + registerGlobalMethod(L, "systemTime", GlobalFunctions::luaSystemTime); + registerGlobalMethod(L, "getFormattedTimeRemaining", GlobalFunctions::luaGetFormattedTimeRemaining); + registerGlobalMethod(L, "reportError", GlobalFunctions::luaReportError); +} + int GlobalFunctions::luaDoPlayerAddItem(lua_State* L) { // doPlayerAddItem(cid, itemid, count/subtype, canDropOnMap) // doPlayerAddItem(cid, itemid, count, canDropOnMap, subtype) diff --git a/src/lua/functions/core/game/global_functions.hpp b/src/lua/functions/core/game/global_functions.hpp index 2b1e249a5fd..9d4ce14b1cf 100644 --- a/src/lua/functions/core/game/global_functions.hpp +++ b/src/lua/functions/core/game/global_functions.hpp @@ -19,45 +19,7 @@ class GlobalFunctions final : LuaScriptInterface { } ~GlobalFunctions() override = default; - static void init(lua_State* L) { - lua_register(L, "addEvent", GlobalFunctions::luaAddEvent); - lua_register(L, "cleanMap", GlobalFunctions::luaCleanMap); - lua_register(L, "createCombatArea", GlobalFunctions::luaCreateCombatArea); - lua_register(L, "debugPrint", GlobalFunctions::luaDebugPrint); - lua_register(L, "doAddContainerItem", GlobalFunctions::luaDoAddContainerItem); - lua_register(L, "doAreaCombatCondition", GlobalFunctions::luaDoAreaCombatCondition); - lua_register(L, "doAreaCombatDispel", GlobalFunctions::luaDoAreaCombatDispel); - lua_register(L, "doAreaCombatHealth", GlobalFunctions::luaDoAreaCombatHealth); - lua_register(L, "doAreaCombatMana", GlobalFunctions::luaDoAreaCombatMana); - lua_register(L, "doChallengeCreature", GlobalFunctions::luaDoChallengeCreature); - lua_register(L, "doPlayerAddItem", GlobalFunctions::luaDoPlayerAddItem); - lua_register(L, "doTargetCombatCondition", GlobalFunctions::luaDoTargetCombatCondition); - lua_register(L, "doTargetCombatDispel", GlobalFunctions::luaDoTargetCombatDispel); - lua_register(L, "doTargetCombatHealth", GlobalFunctions::luaDoTargetCombatHealth); - lua_register(L, "doTargetCombatMana", GlobalFunctions::luaDoTargetCombatMana); - lua_register(L, "getDepotId", GlobalFunctions::luaGetDepotId); - lua_register(L, "getWaypointPositionByName", GlobalFunctions::luaGetWaypointPositionByName); - lua_register(L, "getWorldLight", GlobalFunctions::luaGetWorldLight); - lua_register(L, "getWorldTime", GlobalFunctions::luaGetWorldTime); - lua_register(L, "getWorldUpTime", GlobalFunctions::luaGetWorldUpTime); - lua_register(L, "isDepot", GlobalFunctions::luaIsDepot); - lua_register(L, "isInWar", GlobalFunctions::luaIsInWar); - lua_register(L, "isMovable", GlobalFunctions::luaIsMovable); - lua_register(L, "isValidUID", GlobalFunctions::luaIsValidUID); - lua_register(L, "saveServer", GlobalFunctions::luaSaveServer); - lua_register(L, "sendChannelMessage", GlobalFunctions::luaSendChannelMessage); - lua_register(L, "sendGuildChannelMessage", GlobalFunctions::luaSendGuildChannelMessage); - lua_register(L, "stopEvent", GlobalFunctions::luaStopEvent); - - registerGlobalVariable(L, "INDEX_WHEREEVER", INDEX_WHEREEVER); - registerGlobalBoolean(L, "VIRTUAL_PARENT", true); - registerGlobalMethod(L, "isType", GlobalFunctions::luaIsType); - registerGlobalMethod(L, "rawgetmetatable", GlobalFunctions::luaRawGetMetatable); - registerGlobalMethod(L, "createTable", GlobalFunctions::luaCreateTable); - registerGlobalMethod(L, "systemTime", GlobalFunctions::luaSystemTime); - registerGlobalMethod(L, "getFormattedTimeRemaining", GlobalFunctions::luaGetFormattedTimeRemaining); - registerGlobalMethod(L, "reportError", GlobalFunctions::luaReportError); - } + static void init(lua_State* L); private: static int luaAddEvent(lua_State* L); diff --git a/src/lua/functions/core/game/lua_enums.cpp b/src/lua/functions/core/game/lua_enums.cpp index 99696e9fb59..5a03fa246fa 100644 --- a/src/lua/functions/core/game/lua_enums.cpp +++ b/src/lua/functions/core/game/lua_enums.cpp @@ -9,15 +9,12 @@ #include "lua/functions/core/game/lua_enums.hpp" -#include "creatures/players/wheel/wheel_gems.hpp" #include "creatures/players/wheel/wheel_definitions.hpp" -#include "io/io_bosstiary.hpp" -#include "config/configmanager.hpp" -#include "creatures/creature.hpp" -#include "declarations.hpp" -#include "game/functions/game_reload.hpp" -#include "enums/account_type.hpp" #include "enums/account_group_type.hpp" +#include "enums/account_type.hpp" +#include "enums/item_attribute.hpp" +#include "game/functions/game_reload.hpp" +#include "io/io_bosstiary.hpp" constexpr const char* soundNamespace = "SOUND_EFFECT_TYPE_"; diff --git a/src/lua/functions/core/game/lua_enums.hpp b/src/lua/functions/core/game/lua_enums.hpp index 1c2fd18d755..44c79da7a31 100644 --- a/src/lua/functions/core/game/lua_enums.hpp +++ b/src/lua/functions/core/game/lua_enums.hpp @@ -9,8 +9,6 @@ #pragma once -#include "account/account.hpp" -#include "declarations.hpp" #include "lua/scripts/luascript.hpp" class LuaEnums final : LuaScriptInterface { diff --git a/src/lua/functions/core/game/modal_window_functions.cpp b/src/lua/functions/core/game/modal_window_functions.cpp index a4851cfc85a..95994450424 100644 --- a/src/lua/functions/core/game/modal_window_functions.cpp +++ b/src/lua/functions/core/game/modal_window_functions.cpp @@ -7,8 +7,9 @@ * Website: https://docs.opentibiabr.com/ */ -#include "creatures/players/player.hpp" #include "lua/functions/core/game/modal_window_functions.hpp" + +#include "creatures/players/player.hpp" #include "game/modal_window/modal_window.hpp" // ModalWindow diff --git a/src/lua/functions/core/game/zone_functions.cpp b/src/lua/functions/core/game/zone_functions.cpp index 786edb43f3f..e53c777d675 100644 --- a/src/lua/functions/core/game/zone_functions.cpp +++ b/src/lua/functions/core/game/zone_functions.cpp @@ -8,6 +8,7 @@ */ #include "lua/functions/core/game/zone_functions.hpp" + #include "game/zones/zone.hpp" #include "game/game.hpp" diff --git a/src/lua/functions/core/libs/db_functions.cpp b/src/lua/functions/core/libs/db_functions.cpp index cd6cb57b9b8..858b8faee6d 100644 --- a/src/lua/functions/core/libs/db_functions.cpp +++ b/src/lua/functions/core/libs/db_functions.cpp @@ -7,9 +7,10 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/core/libs/db_functions.hpp" + #include "database/databasemanager.hpp" #include "database/databasetasks.hpp" -#include "lua/functions/core/libs/db_functions.hpp" #include "lua/scripts/lua_environment.hpp" int DBFunctions::luaDatabaseExecute(lua_State* L) { diff --git a/src/lua/functions/core/libs/kv_functions.cpp b/src/lua/functions/core/libs/kv_functions.cpp index d05624c9f41..d18a306944a 100644 --- a/src/lua/functions/core/libs/kv_functions.cpp +++ b/src/lua/functions/core/libs/kv_functions.cpp @@ -7,10 +7,11 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/core/libs/kv_functions.hpp" + #include #include "kv/kv.hpp" -#include "lua/functions/core/libs/kv_functions.hpp" #include "lua/scripts/lua_environment.hpp" int KVFunctions::luaKVScoped(lua_State* L) { diff --git a/src/lua/functions/core/libs/metrics_functions.cpp b/src/lua/functions/core/libs/metrics_functions.cpp index dc9368ab86d..84d81fd214e 100644 --- a/src/lua/functions/core/libs/metrics_functions.cpp +++ b/src/lua/functions/core/libs/metrics_functions.cpp @@ -8,6 +8,7 @@ */ #include "lua/functions/core/libs/metrics_functions.hpp" + #include "lib/metrics/metrics.hpp" void MetricsFunctions::init(lua_State* L) { diff --git a/src/lua/functions/core/network/network_message_functions.cpp b/src/lua/functions/core/network/network_message_functions.cpp index 5b592db3e44..18bb454ee45 100644 --- a/src/lua/functions/core/network/network_message_functions.cpp +++ b/src/lua/functions/core/network/network_message_functions.cpp @@ -8,6 +8,8 @@ */ #include "lua/functions/core/network/network_message_functions.hpp" + +#include "server/network/protocol/protocolgame.hpp" #include "creatures/players/player.hpp" #include "server/network/protocol/protocolstatus.hpp" diff --git a/src/lua/functions/core/network/webhook_functions.cpp b/src/lua/functions/core/network/webhook_functions.cpp index 405996c5bfa..23cba449515 100644 --- a/src/lua/functions/core/network/webhook_functions.cpp +++ b/src/lua/functions/core/network/webhook_functions.cpp @@ -8,6 +8,7 @@ */ #include "lua/functions/core/network/webhook_functions.hpp" + #include "server/network/webhook/webhook.hpp" int WebhookFunctions::luaWebhookSendMessage(lua_State* L) { diff --git a/src/lua/functions/creatures/combat/combat_functions.cpp b/src/lua/functions/creatures/combat/combat_functions.cpp index 13bd3d83098..27d3a76ef04 100644 --- a/src/lua/functions/creatures/combat/combat_functions.cpp +++ b/src/lua/functions/creatures/combat/combat_functions.cpp @@ -7,11 +7,13 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/creatures/combat/combat_functions.hpp" + #include "creatures/combat/combat.hpp" +#include "creatures/combat/condition.hpp" #include "game/game.hpp" -#include "lua/functions/creatures/combat/combat_functions.hpp" -#include "lua/scripts/lua_environment.hpp" #include "lua/global/lua_variant.hpp" +#include "lua/scripts/lua_environment.hpp" int CombatFunctions::luaCombatCreate(lua_State* L) { // Combat() diff --git a/src/lua/functions/creatures/combat/condition_functions.cpp b/src/lua/functions/creatures/combat/condition_functions.cpp index 6d08dcf2e40..1362f0f1f31 100644 --- a/src/lua/functions/creatures/combat/condition_functions.cpp +++ b/src/lua/functions/creatures/combat/condition_functions.cpp @@ -7,9 +7,11 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/creatures/combat/condition_functions.hpp" + #include "creatures/combat/condition.hpp" +#include "enums/player_icons.hpp" #include "game/game.hpp" -#include "lua/functions/creatures/combat/condition_functions.hpp" int ConditionFunctions::luaConditionCreate(lua_State* L) { // Condition(conditionType, conditionId = CONDITIONID_COMBAT, subid = 0, isPersistent = false) diff --git a/src/lua/functions/creatures/combat/spell_functions.cpp b/src/lua/functions/creatures/combat/spell_functions.cpp index d4c3c9eed8d..1a19485afd4 100644 --- a/src/lua/functions/creatures/combat/spell_functions.cpp +++ b/src/lua/functions/creatures/combat/spell_functions.cpp @@ -7,9 +7,12 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/creatures/combat/spell_functions.hpp" + #include "creatures/combat/spells.hpp" #include "creatures/players/vocations/vocation.hpp" -#include "lua/functions/creatures/combat/spell_functions.hpp" +#include "items/item.hpp" +#include "utils/tools.hpp" int SpellFunctions::luaSpellCreate(lua_State* L) { // Spell(words, name or id) to get an existing spell diff --git a/src/lua/functions/creatures/combat/variant_functions.cpp b/src/lua/functions/creatures/combat/variant_functions.cpp index 67dfefaf264..19225017c07 100644 --- a/src/lua/functions/creatures/combat/variant_functions.cpp +++ b/src/lua/functions/creatures/combat/variant_functions.cpp @@ -7,8 +7,9 @@ * Website: https://docs.opentibiabr.com/ */ -#include "items/cylinder.hpp" #include "lua/functions/creatures/combat/variant_functions.hpp" + +#include "items/cylinder.hpp" #include "lua/global/lua_variant.hpp" int VariantFunctions::luaVariantCreate(lua_State* L) { diff --git a/src/lua/functions/creatures/creature_functions.cpp b/src/lua/functions/creatures/creature_functions.cpp index e6bee02d8be..0da3b6cc07a 100644 --- a/src/lua/functions/creatures/creature_functions.cpp +++ b/src/lua/functions/creatures/creature_functions.cpp @@ -7,9 +7,14 @@ * Website: https://docs.opentibiabr.com/ */ -#include "game/game.hpp" -#include "creatures/creature.hpp" #include "lua/functions/creatures/creature_functions.hpp" + +#include "config/configmanager.hpp" +#include "creatures/combat/condition.hpp" +#include "creatures/creature.hpp" +#include "creatures/players/player.hpp" +#include "game/game.hpp" +#include "lua/creature/creatureevent.hpp" #include "map/spectators.hpp" int CreatureFunctions::luaCreatureCreate(lua_State* L) { diff --git a/src/lua/functions/creatures/monster/charm_functions.cpp b/src/lua/functions/creatures/monster/charm_functions.cpp index b90ee4da350..920f4891afe 100644 --- a/src/lua/functions/creatures/monster/charm_functions.cpp +++ b/src/lua/functions/creatures/monster/charm_functions.cpp @@ -7,9 +7,10 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/creatures/monster/charm_functions.hpp" + #include "game/game.hpp" #include "io/iobestiary.hpp" -#include "lua/functions/creatures/monster/charm_functions.hpp" int CharmFunctions::luaCharmCreate(lua_State* L) { // charm(id) diff --git a/src/lua/functions/creatures/monster/loot_functions.cpp b/src/lua/functions/creatures/monster/loot_functions.cpp index 7442a2f3b54..79260ccc935 100644 --- a/src/lua/functions/creatures/monster/loot_functions.cpp +++ b/src/lua/functions/creatures/monster/loot_functions.cpp @@ -7,9 +7,12 @@ * Website: https://docs.opentibiabr.com/ */ -#include "creatures/monsters/monsters.hpp" #include "lua/functions/creatures/monster/loot_functions.hpp" +#include "creatures/monsters/monsters.hpp" +#include "items/item.hpp" +#include "utils/tools.hpp" + int LootFunctions::luaCreateLoot(lua_State* L) { // Loot() will create a new loot item auto loot = std::make_shared(); diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp index d8f24d1d70d..4b85fc98c3a 100644 --- a/src/lua/functions/creatures/monster/monster_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_functions.cpp @@ -7,13 +7,16 @@ * Website: https://docs.opentibiabr.com/ */ -#include "game/game.hpp" +#include "lua/functions/creatures/monster/monster_functions.hpp" + +#include "config/configmanager.hpp" #include "creatures/creature.hpp" #include "creatures/monsters/monster.hpp" #include "creatures/monsters/monsters.hpp" -#include "lua/functions/creatures/monster/monster_functions.hpp" -#include "map/spectators.hpp" +#include "creatures/players/player.hpp" +#include "game/game.hpp" #include "game/scheduling/events_scheduler.hpp" +#include "map/spectators.hpp" int MonsterFunctions::luaMonsterCreate(lua_State* L) { // Monster(id or userdata) diff --git a/src/lua/functions/creatures/monster/monster_spell_functions.cpp b/src/lua/functions/creatures/monster/monster_spell_functions.cpp index 63834d0701f..693da8e6b7e 100644 --- a/src/lua/functions/creatures/monster/monster_spell_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_spell_functions.cpp @@ -8,6 +8,7 @@ */ #include "lua/functions/creatures/monster/monster_spell_functions.hpp" + #include "creatures/monsters/monsters.hpp" int MonsterSpellFunctions::luaCreateMonsterSpell(lua_State* L) { diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp index 134b1fa48b9..3df28d12738 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp @@ -7,13 +7,16 @@ * Website: https://docs.opentibiabr.com/ */ -#include "game/game.hpp" -#include "io/io_bosstiary.hpp" +#include "lua/functions/creatures/monster/monster_type_functions.hpp" + +#include "config/configmanager.hpp" #include "creatures/combat/spells.hpp" -#include "creatures/monsters/monsters.hpp" #include "creatures/monsters/monster.hpp" -#include "lua/functions/creatures/monster/monster_type_functions.hpp" +#include "creatures/monsters/monsters.hpp" +#include "game/game.hpp" +#include "io/io_bosstiary.hpp" #include "lua/scripts/scripts.hpp" +#include "utils/tools.hpp" void MonsterTypeFunctions::createMonsterTypeLootLuaTable(lua_State* L, const std::vector &lootList) { lua_createtable(L, lootList.size(), 0); diff --git a/src/lua/functions/creatures/npc/npc_functions.cpp b/src/lua/functions/creatures/npc/npc_functions.cpp index d4b18072612..f3f5a204ba5 100644 --- a/src/lua/functions/creatures/npc/npc_functions.cpp +++ b/src/lua/functions/creatures/npc/npc_functions.cpp @@ -7,10 +7,12 @@ * Website: https://docs.opentibiabr.com/ */ -#include "game/game.hpp" +#include "lua/functions/creatures/npc/npc_functions.hpp" + #include "creatures/creature.hpp" #include "creatures/npcs/npc.hpp" -#include "lua/functions/creatures/npc/npc_functions.hpp" +#include "creatures/players/player.hpp" +#include "game/game.hpp" #include "map/spectators.hpp" int NpcFunctions::luaNpcCreate(lua_State* L) { diff --git a/src/lua/functions/creatures/npc/npc_type_functions.cpp b/src/lua/functions/creatures/npc/npc_type_functions.cpp index 3e40d2bc63a..9360591318c 100644 --- a/src/lua/functions/creatures/npc/npc_type_functions.cpp +++ b/src/lua/functions/creatures/npc/npc_type_functions.cpp @@ -7,10 +7,12 @@ * Website: https://docs.opentibiabr.com/ */ -#include "creatures/npcs/npcs.hpp" #include "lua/functions/creatures/npc/npc_type_functions.hpp" -#include "lua/scripts/scripts.hpp" + +#include "config/configmanager.hpp" +#include "creatures/npcs/npcs.hpp" #include "game/game.hpp" +#include "lua/scripts/scripts.hpp" void NpcTypeFunctions::createNpcTypeShopLuaTable(lua_State* L, const std::vector &shopVector) { lua_createtable(L, shopVector.size(), 0); diff --git a/src/lua/functions/creatures/npc/shop_functions.cpp b/src/lua/functions/creatures/npc/shop_functions.cpp index 4265f535e85..7ba7f89d43f 100644 --- a/src/lua/functions/creatures/npc/shop_functions.cpp +++ b/src/lua/functions/creatures/npc/shop_functions.cpp @@ -7,9 +7,13 @@ * Website: https://docs.opentibiabr.com/ */ -#include "creatures/npcs/npcs.hpp" #include "lua/functions/creatures/npc/shop_functions.hpp" +#include "creatures/npcs/npcs.hpp" + +#include "items/item.hpp" +#include "utils/tools.hpp" + int ShopFunctions::luaCreateShop(lua_State* L) { // Shop() will create a new shop item pushUserdata(L, std::make_shared()); diff --git a/src/lua/functions/creatures/player/group_functions.cpp b/src/lua/functions/creatures/player/group_functions.cpp index de95a43243a..66003a64926 100644 --- a/src/lua/functions/creatures/player/group_functions.cpp +++ b/src/lua/functions/creatures/player/group_functions.cpp @@ -7,9 +7,10 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/creatures/player/group_functions.hpp" + #include "creatures/players/grouping/groups.hpp" #include "game/game.hpp" -#include "lua/functions/creatures/player/group_functions.hpp" int GroupFunctions::luaGroupCreate(lua_State* L) { // Group(id) diff --git a/src/lua/functions/creatures/player/guild_functions.cpp b/src/lua/functions/creatures/player/guild_functions.cpp index f0d7ac03888..808a5725ea5 100644 --- a/src/lua/functions/creatures/player/guild_functions.cpp +++ b/src/lua/functions/creatures/player/guild_functions.cpp @@ -7,9 +7,10 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/creatures/player/guild_functions.hpp" + #include "game/game.hpp" #include "creatures/players/grouping/guild.hpp" -#include "lua/functions/creatures/player/guild_functions.hpp" int GuildFunctions::luaGuildCreate(lua_State* L) { const uint32_t id = getNumber(L, 2); diff --git a/src/lua/functions/creatures/player/mount_functions.cpp b/src/lua/functions/creatures/player/mount_functions.cpp index bbcd8ebff66..7822e4f8ea5 100644 --- a/src/lua/functions/creatures/player/mount_functions.cpp +++ b/src/lua/functions/creatures/player/mount_functions.cpp @@ -7,18 +7,19 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/creatures/player/mount_functions.hpp" + #include "creatures/appearance/mounts/mounts.hpp" #include "game/game.hpp" -#include "lua/functions/creatures/player/mount_functions.hpp" int MountFunctions::luaCreateMount(lua_State* L) { // Mount(id or name) std::shared_ptr mount; if (isNumber(L, 2)) { - mount = g_game().mounts.getMountByID(getNumber(L, 2)); + mount = g_game().mounts->getMountByID(getNumber(L, 2)); } else if (isString(L, 2)) { - const std::string mountName = getString(L, 2); - mount = g_game().mounts.getMountByName(mountName); + std::string mountName = getString(L, 2); + mount = g_game().mounts->getMountByName(mountName); } else { mount = nullptr; } diff --git a/src/lua/functions/creatures/player/party_functions.cpp b/src/lua/functions/creatures/player/party_functions.cpp index 122e316bd1e..2c420c3ec3f 100644 --- a/src/lua/functions/creatures/player/party_functions.cpp +++ b/src/lua/functions/creatures/player/party_functions.cpp @@ -7,10 +7,11 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/creatures/player/party_functions.hpp" + #include "creatures/players/grouping/party.hpp" #include "creatures/players/player.hpp" #include "game/game.hpp" -#include "lua/functions/creatures/player/party_functions.hpp" int32_t PartyFunctions::luaPartyCreate(lua_State* L) { // Party(userdata) diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index eb9d7d6f50e..dba47d7ed51 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -9,23 +9,33 @@ #include "lua/functions/creatures/player/player_functions.hpp" +#include "creatures/appearance/mounts/mounts.hpp" #include "creatures/combat/spells.hpp" #include "creatures/creature.hpp" #include "creatures/interactions/chat.hpp" -#include "creatures/players/player.hpp" -#include "creatures/players/wheel/player_wheel.hpp" +#include "creatures/monsters/monsters.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_cyclopedia.hpp" +#include "creatures/players/cyclopedia/player_title.hpp" +#include "creatures/players/player.hpp" +#include "creatures/players/vip/player_vip.hpp" +#include "creatures/players/vocations/vocation.hpp" +#include "creatures/players/wheel/player_wheel.hpp" +#include "server/network/protocol/protocolgame.hpp" #include "game/game.hpp" +#include "game/scheduling/save_manager.hpp" +#include "io/iobestiary.hpp" #include "io/iologindata.hpp" #include "io/ioprey.hpp" +#include "items/containers/depot/depotchest.hpp" +#include "items/containers/depot/depotlocker.hpp" +#include "items/containers/rewards/reward.hpp" #include "items/item.hpp" -#include "game/scheduling/save_manager.hpp" -#include "game/scheduling/dispatcher.hpp" #include "map/spectators.hpp" -#include "enums/account_errors.hpp" #include "enums/account_coins.hpp" +#include "enums/account_errors.hpp" +#include "enums/player_icons.hpp" int PlayerFunctions::luaPlayerSendInventory(lua_State* L) { // player:sendInventory() @@ -2369,7 +2379,7 @@ int PlayerFunctions::luaPlayerAddMount(lua_State* L) { if (isNumber(L, 2)) { mountId = getNumber(L, 2); } else { - const auto &mount = g_game().mounts.getMountByName(getString(L, 2)); + const auto &mount = g_game().mounts->getMountByName(getString(L, 2)); if (!mount) { lua_pushnil(L); return 1; @@ -2392,7 +2402,7 @@ int PlayerFunctions::luaPlayerRemoveMount(lua_State* L) { if (isNumber(L, 2)) { mountId = getNumber(L, 2); } else { - const auto &mount = g_game().mounts.getMountByName(getString(L, 2)); + const auto &mount = g_game().mounts->getMountByName(getString(L, 2)); if (!mount) { lua_pushnil(L); return 1; @@ -2413,9 +2423,9 @@ int PlayerFunctions::luaPlayerHasMount(lua_State* L) { std::shared_ptr mount = nullptr; if (isNumber(L, 2)) { - mount = g_game().mounts.getMountByID(getNumber(L, 2)); + mount = g_game().mounts->getMountByID(getNumber(L, 2)); } else { - mount = g_game().mounts.getMountByName(getString(L, 2)); + mount = g_game().mounts->getMountByName(getString(L, 2)); } if (mount) { diff --git a/src/lua/functions/creatures/player/player_functions.hpp b/src/lua/functions/creatures/player/player_functions.hpp index cfcc685a809..090e4371abe 100644 --- a/src/lua/functions/creatures/player/player_functions.hpp +++ b/src/lua/functions/creatures/player/player_functions.hpp @@ -16,6 +16,9 @@ #include "lua/functions/creatures/player/party_functions.hpp" #include "lua/functions/creatures/player/vocation_functions.hpp" +enum class PlayerIcon : uint8_t; +enum class IconBakragore : uint8_t; + class PlayerFunctions final : LuaScriptInterface { explicit PlayerFunctions(lua_State* L) : LuaScriptInterface("PlayerFunctions") { diff --git a/src/lua/functions/creatures/player/vocation_functions.cpp b/src/lua/functions/creatures/player/vocation_functions.cpp index de35c58a198..59b2a0d87dc 100644 --- a/src/lua/functions/creatures/player/vocation_functions.cpp +++ b/src/lua/functions/creatures/player/vocation_functions.cpp @@ -7,9 +7,10 @@ * Website: https://docs.opentibiabr.com/ */ -#include "creatures/players/vocations/vocation.hpp" #include "lua/functions/creatures/player/vocation_functions.hpp" +#include "creatures/players/vocations/vocation.hpp" + int VocationFunctions::luaVocationCreate(lua_State* L) { // Vocation(id or name) uint16_t vocationId; diff --git a/src/lua/functions/events/action_functions.cpp b/src/lua/functions/events/action_functions.cpp index 67ef0af5031..a21ab37ebd9 100644 --- a/src/lua/functions/events/action_functions.cpp +++ b/src/lua/functions/events/action_functions.cpp @@ -7,8 +7,9 @@ * Website: https://docs.opentibiabr.com/ */ -#include "lua/creature/actions.hpp" #include "lua/functions/events/action_functions.hpp" + +#include "lua/creature/actions.hpp" #include "game/game.hpp" #include "items/item.hpp" diff --git a/src/lua/functions/events/creature_event_functions.cpp b/src/lua/functions/events/creature_event_functions.cpp index 60ddb08b65a..ea4c05758d5 100644 --- a/src/lua/functions/events/creature_event_functions.cpp +++ b/src/lua/functions/events/creature_event_functions.cpp @@ -7,8 +7,9 @@ * Website: https://docs.opentibiabr.com/ */ -#include "lua/creature/creatureevent.hpp" #include "lua/functions/events/creature_event_functions.hpp" + +#include "lua/creature/creatureevent.hpp" #include "utils/tools.hpp" int CreatureEventFunctions::luaCreateCreatureEvent(lua_State* L) { diff --git a/src/lua/functions/events/events_scheduler_functions.cpp b/src/lua/functions/events/events_scheduler_functions.cpp index eb16cf5c340..7b9e7709d9e 100644 --- a/src/lua/functions/events/events_scheduler_functions.cpp +++ b/src/lua/functions/events/events_scheduler_functions.cpp @@ -7,9 +7,10 @@ * Website: https://docs.opentibiabr.com/ */ -#include "game/scheduling/events_scheduler.hpp" #include "lua/functions/events/events_scheduler_functions.hpp" +#include "game/scheduling/events_scheduler.hpp" + int EventsSchedulerFunctions::luaEventsSchedulergetEventSLoot(lua_State* L) { // EventsScheduler.getEventSLoot lua_pushnumber(L, g_eventsScheduler().getLootSchedule()); diff --git a/src/lua/functions/events/global_event_functions.cpp b/src/lua/functions/events/global_event_functions.cpp index 6ec9275fd01..32c9eccf2e5 100644 --- a/src/lua/functions/events/global_event_functions.cpp +++ b/src/lua/functions/events/global_event_functions.cpp @@ -8,6 +8,7 @@ */ #include "lua/functions/events/global_event_functions.hpp" + #include "game/game.hpp" #include "lua/global/globalevent.hpp" #include "utils/tools.hpp" diff --git a/src/lua/functions/events/move_event_functions.cpp b/src/lua/functions/events/move_event_functions.cpp index 0c7277b9234..ac7727fb2a9 100644 --- a/src/lua/functions/events/move_event_functions.cpp +++ b/src/lua/functions/events/move_event_functions.cpp @@ -7,9 +7,11 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/events/move_event_functions.hpp" + #include "creatures/creature.hpp" #include "lua/creature/movement.hpp" -#include "lua/functions/events/move_event_functions.hpp" +#include "utils/tools.hpp" int MoveEventFunctions::luaCreateMoveEvent(lua_State* L) { // MoveEvent() diff --git a/src/lua/functions/events/talk_action_functions.cpp b/src/lua/functions/events/talk_action_functions.cpp index a7ef2594f64..1b87fec4b7d 100644 --- a/src/lua/functions/events/talk_action_functions.cpp +++ b/src/lua/functions/events/talk_action_functions.cpp @@ -7,9 +7,10 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/events/talk_action_functions.hpp" + #include "account/account.hpp" #include "lua/creature/talkaction.hpp" -#include "lua/functions/events/talk_action_functions.hpp" #include "utils/tools.hpp" #include "enums/account_group_type.hpp" diff --git a/src/lua/functions/items/container_functions.cpp b/src/lua/functions/items/container_functions.cpp index f74a1ab2a30..8a0a84e3199 100644 --- a/src/lua/functions/items/container_functions.cpp +++ b/src/lua/functions/items/container_functions.cpp @@ -7,9 +7,11 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/items/container_functions.hpp" + #include "game/game.hpp" #include "items/item.hpp" -#include "lua/functions/items/container_functions.hpp" +#include "utils/tools.hpp" int ContainerFunctions::luaContainerCreate(lua_State* L) { // Container(uid) diff --git a/src/lua/functions/items/imbuement_functions.cpp b/src/lua/functions/items/imbuement_functions.cpp index b3676a22183..cf01295ed96 100644 --- a/src/lua/functions/items/imbuement_functions.cpp +++ b/src/lua/functions/items/imbuement_functions.cpp @@ -7,9 +7,10 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/items/imbuement_functions.hpp" + #include "items/weapons/weapons.hpp" #include "creatures/players/imbuements/imbuements.hpp" -#include "lua/functions/items/imbuement_functions.hpp" int ImbuementFunctions::luaCreateImbuement(lua_State* L) { // Imbuement(id) diff --git a/src/lua/functions/items/item_classification_functions.cpp b/src/lua/functions/items/item_classification_functions.cpp index 7631e2f8650..0b68458f2a3 100644 --- a/src/lua/functions/items/item_classification_functions.cpp +++ b/src/lua/functions/items/item_classification_functions.cpp @@ -7,9 +7,11 @@ * Website: https://docs.opentibiabr.com/ */ -#include "game/game.hpp" #include "lua/functions/items/item_classification_functions.hpp" +#include "game/game.hpp" +#include "items/items_classification.hpp" + int ItemClassificationFunctions::luaItemClassificationCreate(lua_State* L) { // ItemClassification(id) if (isNumber(L, 2)) { diff --git a/src/lua/functions/items/item_functions.cpp b/src/lua/functions/items/item_functions.cpp index f75e7adb4eb..c54d43bc94b 100644 --- a/src/lua/functions/items/item_functions.cpp +++ b/src/lua/functions/items/item_functions.cpp @@ -9,12 +9,13 @@ #include "lua/functions/items/item_functions.hpp" +#include "creatures/players/imbuements/imbuements.hpp" +#include "creatures/players/player.hpp" #include "game/game.hpp" -#include "items/item.hpp" -#include "items/decay/decay.hpp" #include "game/scheduling/save_manager.hpp" - -class Imbuement; +#include "items/decay/decay.hpp" +#include "items/item.hpp" +#include "utils/tools.hpp" // Item int ItemFunctions::luaItemCreate(lua_State* L) { diff --git a/src/lua/functions/items/item_type_functions.cpp b/src/lua/functions/items/item_type_functions.cpp index 0cf3f4daf00..57c5cc5b999 100644 --- a/src/lua/functions/items/item_type_functions.cpp +++ b/src/lua/functions/items/item_type_functions.cpp @@ -7,9 +7,10 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/items/item_type_functions.hpp" + #include "items/item.hpp" #include "items/items.hpp" -#include "lua/functions/items/item_type_functions.hpp" int ItemTypeFunctions::luaItemTypeCreate(lua_State* L) { // ItemType(id or name) diff --git a/src/lua/functions/lua_functions_loader.cpp b/src/lua/functions/lua_functions_loader.cpp index 9d12b2ca97c..894cf252c80 100644 --- a/src/lua/functions/lua_functions_loader.cpp +++ b/src/lua/functions/lua_functions_loader.cpp @@ -7,6 +7,8 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/lua_functions_loader.hpp" + #include "creatures/combat/spells.hpp" #include "creatures/monsters/monster.hpp" #include "creatures/npcs/npc.hpp" @@ -20,7 +22,6 @@ #include "lua/functions/creatures/creature_functions.hpp" #include "lua/functions/events/events_functions.hpp" #include "lua/functions/items/item_functions.hpp" -#include "lua/functions/lua_functions_loader.hpp" #include "lua/functions/map/map_functions.hpp" #include "lua/functions/core/game/zone_functions.hpp" #include "lua/global/lua_variant.hpp" diff --git a/src/lua/functions/map/house_functions.cpp b/src/lua/functions/map/house_functions.cpp index a07c2dbc5c1..708b9233a3a 100644 --- a/src/lua/functions/map/house_functions.cpp +++ b/src/lua/functions/map/house_functions.cpp @@ -7,11 +7,13 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/map/house_functions.hpp" + +#include "config/configmanager.hpp" #include "items/bed.hpp" #include "game/game.hpp" #include "game/movement/position.hpp" #include "io/iologindata.hpp" -#include "lua/functions/map/house_functions.hpp" #include "map/house/house.hpp" int HouseFunctions::luaHouseCreate(lua_State* L) { diff --git a/src/lua/functions/map/position_functions.cpp b/src/lua/functions/map/position_functions.cpp index 725dbb339a6..b182ee5e447 100644 --- a/src/lua/functions/map/position_functions.cpp +++ b/src/lua/functions/map/position_functions.cpp @@ -7,10 +7,13 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/map/position_functions.hpp" + +#include "config/configmanager.hpp" +#include "creatures/creature.hpp" +#include "creatures/players/player.hpp" #include "game/game.hpp" #include "game/movement/position.hpp" -#include "lua/functions/map/position_functions.hpp" -#include "map/spectators.hpp" int PositionFunctions::luaPositionCreate(lua_State* L) { // Position([x = 0[, y = 0[, z = 0[, stackpos = 0]]]]) diff --git a/src/lua/functions/map/teleport_functions.cpp b/src/lua/functions/map/teleport_functions.cpp index 3a7b397f24c..84eafe21ba4 100644 --- a/src/lua/functions/map/teleport_functions.cpp +++ b/src/lua/functions/map/teleport_functions.cpp @@ -7,9 +7,10 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/functions/map/teleport_functions.hpp" + #include "game/movement/teleport.hpp" #include "items/item.hpp" -#include "lua/functions/map/teleport_functions.hpp" // Teleport int TeleportFunctions::luaTeleportCreate(lua_State* L) { diff --git a/src/lua/functions/map/tile_functions.cpp b/src/lua/functions/map/tile_functions.cpp index b201d47fb09..d43ce68e6be 100644 --- a/src/lua/functions/map/tile_functions.cpp +++ b/src/lua/functions/map/tile_functions.cpp @@ -7,9 +7,11 @@ * Website: https://docs.opentibiabr.com/ */ -#include "game/game.hpp" #include "lua/functions/map/tile_functions.hpp" +#include "creatures/combat/combat.hpp" +#include "game/game.hpp" + int TileFunctions::luaTileCreate(lua_State* L) { // Tile(x, y, z) // Tile(position) diff --git a/src/lua/functions/map/town_functions.cpp b/src/lua/functions/map/town_functions.cpp index 3ccf112e772..eba9213d0de 100644 --- a/src/lua/functions/map/town_functions.cpp +++ b/src/lua/functions/map/town_functions.cpp @@ -7,8 +7,9 @@ * Website: https://docs.opentibiabr.com/ */ -#include "game/game.hpp" #include "lua/functions/map/town_functions.hpp" + +#include "game/game.hpp" #include "map/town.hpp" int TownFunctions::luaTownCreate(lua_State* L) { diff --git a/src/lua/global/baseevents.cpp b/src/lua/global/baseevents.cpp index 82606618fb4..71832291da6 100644 --- a/src/lua/global/baseevents.cpp +++ b/src/lua/global/baseevents.cpp @@ -8,7 +8,10 @@ */ #include "lua/global/baseevents.hpp" + +#include "config/configmanager.hpp" #include "lua/scripts/lua_environment.hpp" +#include "lua/scripts/luascript.hpp" #include "utils/tools.hpp" bool BaseEvents::loadFromXml() { diff --git a/src/lua/global/baseevents.hpp b/src/lua/global/baseevents.hpp index e053eb13c94..754f8a7b212 100644 --- a/src/lua/global/baseevents.hpp +++ b/src/lua/global/baseevents.hpp @@ -9,8 +9,7 @@ #pragma once -#include "lua/scripts/luascript.hpp" - +class LuaScriptInterface; class Event; using Event_ptr = std::shared_ptr; diff --git a/src/lua/global/globalevent.cpp b/src/lua/global/globalevent.cpp index 90116820a39..f7de024bda1 100644 --- a/src/lua/global/globalevent.cpp +++ b/src/lua/global/globalevent.cpp @@ -8,6 +8,7 @@ */ #include "lua/global/globalevent.hpp" + #include "utils/tools.hpp" #include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" diff --git a/src/lua/lua_definitions.hpp b/src/lua/lua_definitions.hpp index a01f6d9db7e..ace5ee32164 100644 --- a/src/lua/lua_definitions.hpp +++ b/src/lua/lua_definitions.hpp @@ -57,7 +57,7 @@ enum class LuaData_t : uint8_t { ItemClassification, }; -enum CreatureEventType_t { +enum CreatureEventType_t : uint8_t { CREATURE_EVENT_NONE, CREATURE_EVENT_LOGIN, CREATURE_EVENT_LOGOUT, diff --git a/src/lua/modules/modules.cpp b/src/lua/modules/modules.cpp index 1c0484d33e6..1e501b85c83 100644 --- a/src/lua/modules/modules.cpp +++ b/src/lua/modules/modules.cpp @@ -8,6 +8,7 @@ */ #include "lua/modules/modules.hpp" + #include "creatures/players/player.hpp" #include "game/game.hpp" diff --git a/src/lua/modules/modules.hpp b/src/lua/modules/modules.hpp index 5917013ec90..400ced5e0b4 100644 --- a/src/lua/modules/modules.hpp +++ b/src/lua/modules/modules.hpp @@ -12,6 +12,7 @@ #include "declarations.hpp" #include "lib/di/container.hpp" #include "lua/global/baseevents.hpp" +#include "lua/scripts/luascript.hpp" #include "server/network/message/networkmessage.hpp" class Module; diff --git a/src/lua/scripts/lua_environment.cpp b/src/lua/scripts/lua_environment.cpp index ab4410a357e..b4cc620b1ce 100644 --- a/src/lua/scripts/lua_environment.cpp +++ b/src/lua/scripts/lua_environment.cpp @@ -7,8 +7,9 @@ * Website: https://docs.opentibiabr.com/ */ -#include "declarations.hpp" #include "lua/scripts/lua_environment.hpp" + +#include "declarations.hpp" #include "lua/functions/lua_functions_loader.hpp" #include "lua/scripts/script_environment.hpp" #include "lua/global/lua_timer_event_descr.hpp" diff --git a/src/lua/scripts/luascript.cpp b/src/lua/scripts/luascript.cpp index 0b55220c9c4..aa1b6011886 100644 --- a/src/lua/scripts/luascript.cpp +++ b/src/lua/scripts/luascript.cpp @@ -8,6 +8,7 @@ */ #include "lua/scripts/luascript.hpp" + #include "lua/scripts/lua_environment.hpp" #include "lib/metrics/metrics.hpp" diff --git a/src/lua/scripts/script_environment.cpp b/src/lua/scripts/script_environment.cpp index adbf5e791b0..80eea882fa0 100644 --- a/src/lua/scripts/script_environment.cpp +++ b/src/lua/scripts/script_environment.cpp @@ -7,9 +7,11 @@ * Website: https://docs.opentibiabr.com/ */ +#include "lua/scripts/script_environment.hpp" + +#include "creatures/creature.hpp" #include "game/game.hpp" #include "lua/scripts/luascript.hpp" -#include "lua/scripts/script_environment.hpp" ScriptEnvironment::ScriptEnvironment() { resetEnv(); diff --git a/src/lua/scripts/scripts.cpp b/src/lua/scripts/scripts.cpp index 1d30e4e4040..556ed1be50b 100644 --- a/src/lua/scripts/scripts.cpp +++ b/src/lua/scripts/scripts.cpp @@ -7,13 +7,17 @@ * Website: https://docs.opentibiabr.com/ */ -#include "creatures/players/imbuements/imbuements.hpp" -#include "lua/global/globalevent.hpp" -#include "items/weapons/weapons.hpp" -#include "lua/creature/movement.hpp" #include "lua/scripts/scripts.hpp" + +#include "config/configmanager.hpp" #include "creatures/combat/spells.hpp" +#include "creatures/monsters/monsters.hpp" +#include "items/weapons/weapons.hpp" #include "lua/callbacks/events_callbacks.hpp" +#include "lua/creature/creatureevent.hpp" +#include "lua/creature/movement.hpp" +#include "lua/creature/talkaction.hpp" +#include "lua/global/globalevent.hpp" Scripts::Scripts() : scriptInterface("Scripts Interface") { diff --git a/src/map/house/house.cpp b/src/map/house/house.cpp index f4b2e7e3e85..334144cfbd1 100644 --- a/src/map/house/house.cpp +++ b/src/map/house/house.cpp @@ -7,13 +7,17 @@ * Website: https://docs.opentibiabr.com/ */ -#include "utils/pugicast.hpp" #include "map/house/house.hpp" -#include "io/iologindata.hpp" + +#include "config/configmanager.hpp" #include "game/game.hpp" -#include "items/bed.hpp" #include "game/scheduling/save_manager.hpp" +#include "io/ioguild.hpp" +#include "io/iologindata.hpp" +#include "items/bed.hpp" +#include "items/containers/inbox/inbox.hpp" #include "lib/metrics/metrics.hpp" +#include "utils/pugicast.hpp" House::House(uint32_t houseId) : id(houseId) { } diff --git a/src/map/house/housetile.cpp b/src/map/house/housetile.cpp index 0167aa9d906..ce9229ce42c 100644 --- a/src/map/house/housetile.cpp +++ b/src/map/house/housetile.cpp @@ -7,11 +7,15 @@ * Website: https://docs.opentibiabr.com/ */ -#include "items/tile.hpp" -#include "creatures/monsters/monster.hpp" #include "map/house/housetile.hpp" -#include "map/house/house.hpp" + +#include "config/configmanager.hpp" +#include "creatures/monsters/monster.hpp" +#include "creatures/players/player.hpp" #include "game/game.hpp" +#include "items/tile.hpp" +#include "map/house/house.hpp" +#include "utils/tools.hpp" HouseTile::HouseTile(int32_t initX, int32_t initY, int32_t initZ, std::shared_ptr initHouse) : DynamicTile(initX, initY, initZ), house(std::move(initHouse)) { } diff --git a/src/map/map.cpp b/src/map/map.cpp index 6ada1d59231..768ba959dce 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -7,18 +7,19 @@ * Website: https://docs.opentibiabr.com/ */ -#include "map.hpp" -#include "utils/astarnodes.hpp" +#include "map/map.hpp" -#include "lua/callbacks/event_callback.hpp" -#include "lua/callbacks/events_callbacks.hpp" #include "creatures/monsters/monster.hpp" +#include "creatures/players/player.hpp" #include "game/game.hpp" +#include "game/scheduling/dispatcher.hpp" #include "game/zones/zone.hpp" #include "io/iomap.hpp" #include "io/iomapserialize.hpp" -#include "game/scheduling/dispatcher.hpp" +#include "lua/callbacks/event_callback.hpp" +#include "lua/callbacks/events_callbacks.hpp" #include "map/spectators.hpp" +#include "utils/astarnodes.hpp" void Map::load(const std::string &identifier, const Position &pos) { try { diff --git a/src/map/mapcache.cpp b/src/map/mapcache.cpp index 113ed26a32c..01fd9da63d8 100644 --- a/src/map/mapcache.cpp +++ b/src/map/mapcache.cpp @@ -7,20 +7,17 @@ * Website: https://docs.opentibiabr.com/ */ -#include "mapcache.hpp" +#include "map/mapcache.hpp" #include "game/movement/teleport.hpp" #include "game/scheduling/dispatcher.hpp" -#include "items/bed.hpp" -#include "io/iologindata.hpp" -#include "items/item.hpp" -#include "game/game.hpp" #include "game/zones/zone.hpp" -#include "map/map.hpp" -#include "utils/hash.hpp" #include "io/filestream.hpp" - #include "io/iomap.hpp" +#include "items/containers/depot/depotlocker.hpp" +#include "items/item.hpp" +#include "map/map.hpp" +#include "utils/hash.hpp" static phmap::flat_hash_map> items; static phmap::flat_hash_map> tiles; diff --git a/src/map/spectators.cpp b/src/map/spectators.cpp index 85976503d69..74c6aa19af0 100644 --- a/src/map/spectators.cpp +++ b/src/map/spectators.cpp @@ -7,7 +7,9 @@ * Website: https://docs.opentibiabr.com/ */ -#include "spectators.hpp" +#include "map/spectators.hpp" + +#include "creatures/creature.hpp" #include "game/game.hpp" phmap::flat_hash_map Spectators::spectatorsCache; @@ -212,3 +214,20 @@ Spectators Spectators::find(const Position ¢erPos, bool multifloor, bool onl return *this; } + +Spectators Spectators::filter(bool onlyPlayers, bool onlyMonsters, bool onlyNpcs) const { + auto specs = Spectators(); + specs.creatures.reserve(creatures.size()); + + for (const auto &c : creatures) { + if (onlyPlayers && c->getPlayer() != nullptr) { + specs.insert(c); + } else if (onlyMonsters && c->getMonster() != nullptr) { + specs.insert(c); + } else if (onlyNpcs && c->getNpc() != nullptr) { + specs.insert(c); + } + } + + return specs; +} diff --git a/src/map/spectators.hpp b/src/map/spectators.hpp index b8e56fc9d2e..68ad751b056 100644 --- a/src/map/spectators.hpp +++ b/src/map/spectators.hpp @@ -9,13 +9,15 @@ #pragma once -#include "creatures/creature.hpp" - +class Creature; class Player; class Monster; class Npc; struct Position; +// Forward declaration para CreatureVector +using CreatureVector = std::vector>; + struct SpectatorsCache { struct FloorData { std::optional floor; @@ -44,7 +46,12 @@ class Spectators { template requires std::is_base_of_v - Spectators filter() const; + Spectators filter() const { + bool onlyPlayers = std::is_same_v; + bool onlyMonsters = std::is_same_v; + bool onlyNpcs = std::is_same_v; + return filter(onlyPlayers, onlyMonsters, onlyNpcs); + } Spectators insert(const std::shared_ptr &creature); Spectators insertAll(const CreatureVector &list); @@ -86,32 +93,9 @@ class Spectators { Spectators find(const Position ¢erPos, bool multifloor = false, bool onlyPlayers = false, int32_t minRangeX = 0, int32_t maxRangeX = 0, int32_t minRangeY = 0, int32_t maxRangeY = 0, bool useCache = true); CreatureVector getSpectators(const Position ¢erPos, bool multifloor = false, bool onlyPlayers = false, int32_t minRangeX = 0, int32_t maxRangeX = 0, int32_t minRangeY = 0, int32_t maxRangeY = 0); + Spectators filter(bool onlyPlayers, bool onlyMonsters, bool onlyNpcs) const; + bool checkCache(const SpectatorsCache::FloorData &specData, bool onlyPlayers, const Position ¢erPos, bool checkDistance, bool multifloor, int32_t minRangeX, int32_t maxRangeX, int32_t minRangeY, int32_t maxRangeY); CreatureVector creatures; }; - -template - requires std::is_base_of_v -Spectators Spectators::filter() const { - auto specs = Spectators(); - specs.creatures.reserve(creatures.size()); - - for (const auto &c : creatures) { - if constexpr (std::is_same_v) { - if (c->getPlayer() != nullptr) { - specs.insert(c); - } - } else if constexpr (std::is_same_v) { - if (c->getMonster() != nullptr) { - specs.insert(c); - } - } else if constexpr (std::is_same_v) { - if (c->getNpc() != nullptr) { - specs.insert(c); - } - } - } - - return specs; -} diff --git a/src/map/utils/astarnodes.cpp b/src/map/utils/astarnodes.cpp index c1561bc7296..a7e95055b93 100644 --- a/src/map/utils/astarnodes.cpp +++ b/src/map/utils/astarnodes.cpp @@ -7,9 +7,11 @@ * Website: https://docs.opentibiabr.com/ */ -#include "astarnodes.hpp" -#include "creatures/monsters/monster.hpp" +#include "map/utils/astarnodes.hpp" + #include "creatures/combat/combat.hpp" +#include "creatures/monsters/monster.hpp" +#include "items/tile.hpp" AStarNodes::AStarNodes(uint32_t x, uint32_t y, int_fast32_t extraCost) : #if defined(__AVX2__) || defined(__SSE2__) diff --git a/src/map/utils/mapsector.cpp b/src/map/utils/mapsector.cpp index 36bbd01684d..f07c2a3e1b1 100644 --- a/src/map/utils/mapsector.cpp +++ b/src/map/utils/mapsector.cpp @@ -7,8 +7,9 @@ * Website: https://docs.opentibiabr.com/ */ +#include "map/utils/mapsector.hpp" + #include "creatures/creature.hpp" -#include "mapsector.hpp" bool MapSector::newSector = false; diff --git a/src/pch.hpp b/src/pch.hpp index dea85a93106..79f14433fcc 100644 --- a/src/pch.hpp +++ b/src/pch.hpp @@ -184,3 +184,6 @@ constexpr std::string_view methodName(const char* s) { #else #error "Compiler not supported" #endif + +#include "account/account_info.hpp" +#include "config/config_enums.hpp" diff --git a/src/security/argon.cpp b/src/security/argon.cpp index 2e0b05ac7a9..206c4ac0ab0 100644 --- a/src/security/argon.cpp +++ b/src/security/argon.cpp @@ -7,9 +7,10 @@ * Website: https://docs.opentibiabr.com/ */ +#include "security/argon.hpp" + #include "config/configmanager.hpp" #include "database/database.hpp" -#include "security/argon.hpp" #include diff --git a/src/security/rsa.cpp b/src/security/rsa.cpp index f192f6ca3ec..18e1f6e8393 100644 --- a/src/security/rsa.cpp +++ b/src/security/rsa.cpp @@ -7,9 +7,10 @@ * Website: https://docs.opentibiabr.com/ */ -#include "lib/di/container.hpp" #include "security/rsa.hpp" +#include "lib/di/container.hpp" + RSA::RSA(Logger &logger) : logger(logger) { mpz_init(n); diff --git a/src/server/network/message/networkmessage.cpp b/src/server/network/message/networkmessage.cpp index 5b7c8a143b9..0fb8b63324f 100644 --- a/src/server/network/message/networkmessage.cpp +++ b/src/server/network/message/networkmessage.cpp @@ -8,6 +8,7 @@ */ #include "server/network/message/networkmessage.hpp" + #include "items/containers/container.hpp" int32_t NetworkMessage::decodeHeader() { diff --git a/src/server/network/message/outputmessage.cpp b/src/server/network/message/outputmessage.cpp index ad4589581a9..f0f2530fa39 100644 --- a/src/server/network/message/outputmessage.cpp +++ b/src/server/network/message/outputmessage.cpp @@ -7,7 +7,7 @@ * Website: https://docs.opentibiabr.com/ */ -#include "outputmessage.hpp" +#include "server/network/message/outputmessage.hpp" #include "lib/di/container.hpp" #include "server/network/protocol/protocol.hpp" diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 670e37f3bf4..aa99cb57d26 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -7,41 +7,53 @@ * Website: https://docs.opentibiabr.com/ */ -#include "creatures/players/management/ban.hpp" +#include "server/network/protocol/protocolgame.hpp" + +#include "config/configmanager.hpp" #include "core.hpp" -#include "declarations.hpp" -#include "game/game.hpp" -#include "creatures/players/imbuements/imbuements.hpp" -#include "io/functions/iologindata_load_player.hpp" -#include "io/iobestiary.hpp" -#include "io/io_bosstiary.hpp" -#include "io/iologindata.hpp" -#include "io/iomarket.hpp" -#include "lua/modules/modules.hpp" +#include "creatures/appearance/mounts/mounts.hpp" +#include "creatures/combat/condition.hpp" +#include "creatures/combat/spells.hpp" +#include "creatures/interactions/chat.hpp" #include "creatures/monsters/monster.hpp" #include "creatures/monsters/monsters.hpp" -#include "game/modal_window/modal_window.hpp" -#include "server/network/message/outputmessage.hpp" -#include "creatures/players/player.hpp" -#include "creatures/players/wheel/player_wheel.hpp" +#include "creatures/npcs/npc.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" #include "creatures/players/cyclopedia/player_cyclopedia.hpp" #include "creatures/players/grouping/familiars.hpp" -#include "server/network/protocol/protocolgame.hpp" -#include "game/scheduling/dispatcher.hpp" -#include "creatures/combat/spells.hpp" -#include "utils/tools.hpp" +#include "creatures/players/grouping/party.hpp" +#include "creatures/players/grouping/team_finder.hpp" +#include "creatures/players/highscore_category.hpp" +#include "creatures/players/imbuements/imbuements.hpp" +#include "creatures/players/management/ban.hpp" #include "creatures/players/management/waitlist.hpp" +#include "creatures/players/player.hpp" +#include "creatures/players/vip/player_vip.hpp" +#include "creatures/players/wheel/player_wheel.hpp" +#include "enums/player_icons.hpp" +#include "game/game.hpp" +#include "game/modal_window/modal_window.hpp" +#include "game/scheduling/dispatcher.hpp" +#include "io/functions/iologindata_load_player.hpp" +#include "io/io_bosstiary.hpp" +#include "io/iobestiary.hpp" +#include "io/iologindata.hpp" +#include "io/iomarket.hpp" +#include "io/ioprey.hpp" +#include "items/items_classification.hpp" #include "items/weapons/weapons.hpp" -#include "enums/object_category.hpp" -#include "enums/account_type.hpp" -#include "enums/account_group_type.hpp" +#include "lua/creature/creatureevent.hpp" +#include "lua/modules/modules.hpp" +#include "server/network/message/outputmessage.hpp" +#include "utils/tools.hpp" + #include "enums/account_coins.hpp" +#include "enums/account_group_type.hpp" +#include "enums/account_type.hpp" +#include "enums/object_category.hpp" #include "enums/player_blessings.hpp" -#include "creatures/players/highscore_category.hpp" - /* * NOTE: This namespace is used so that we can add functions without having to declare them in the ".hpp/.hpp" file * Do not use functions only in the .cpp scope without having a namespace, it may conflict with functions in other files of the same name @@ -3912,7 +3924,7 @@ void ProtocolGame::sendCyclopediaCharacterOutfitsMounts() { uint16_t mountSize = 0; auto startMounts = msg.getBufferPosition(); msg.skipBytes(2); - for (const auto &mount : g_game().mounts.getMounts()) { + for (const auto &mount : g_game().mounts->getMounts()) { const std::string type = mount->type; if (player->hasMount(mount)) { ++mountSize; @@ -7043,7 +7055,7 @@ void ProtocolGame::sendOutfitWindow() { bool mounted = false; if (!isSupportOutfit) { - const auto currentMount = g_game().mounts.getMountByID(player->getLastMount()); + const auto currentMount = g_game().mounts->getMountByID(player->getLastMount()); if (currentMount) { mounted = (currentOutfit.lookMount == currentMount->clientId); currentOutfit.lookMount = currentMount->clientId; @@ -7079,7 +7091,7 @@ void ProtocolGame::sendOutfitWindow() { } std::vector> mounts; - for (const auto &mount : g_game().mounts.getMounts()) { + for (const auto &mount : g_game().mounts->getMounts()) { if (player->hasMount(mount)) { mounts.emplace_back(mount); } @@ -7177,7 +7189,7 @@ void ProtocolGame::sendOutfitWindow() { uint16_t mountSize = 0; msg.skipBytes(2); - const auto mounts = g_game().mounts.getMounts(); + const auto mounts = g_game().mounts->getMounts(); for (const auto &mount : mounts) { if (player->hasMount(mount)) { msg.add(mount->clientId); @@ -7297,7 +7309,7 @@ void ProtocolGame::sendPodiumWindow(const std::shared_ptr &podium, const P uint16_t mountSize = 0; msg.skipBytes(2); - const auto mounts = g_game().mounts.getMounts(); + const auto mounts = g_game().mounts->getMounts(); for (const auto &mount : mounts) { if (player->hasMount(mount)) { msg.add(mount->clientId); diff --git a/src/server/network/protocol/protocolgame.hpp b/src/server/network/protocol/protocolgame.hpp index 01a5e6e7fd8..27fa6c8716c 100644 --- a/src/server/network/protocol/protocolgame.hpp +++ b/src/server/network/protocol/protocolgame.hpp @@ -10,12 +10,25 @@ #pragma once #include "server/network/protocol/protocol.hpp" -#include "creatures/interactions/chat.hpp" -#include "creatures/creature.hpp" -#include "enums/forge_conversion.hpp" -#include "creatures/players/cyclopedia/player_badge.hpp" -#include "creatures/players/cyclopedia/player_cyclopedia.hpp" -#include "creatures/players/cyclopedia/player_title.hpp" +#include "game/movement/position.hpp" +#include "utils/utils_definitions.hpp" + +enum class PlayerIcon : uint8_t; +enum class IconBakragore : uint8_t; +enum class ForgeAction_t : uint8_t; +enum MessageClasses : uint8_t; +enum ReturnValue : uint16_t; +enum TextColor_t : uint8_t; +enum OperatingSystem_t : uint8_t; +enum ChannelEvent_t : uint8_t; +enum CyclopediaCharacterInfoType_t : uint8_t; +enum Resource_t : uint8_t; +enum class VipStatus_t : uint8_t; +enum SpellGroup_t : uint8_t; +enum Slots_t : uint8_t; +enum CombatType_t : uint8_t; +enum SoundEffect_t : uint16_t; +enum class SourceEffect_t : uint8_t; class NetworkMessage; class Player; @@ -29,13 +42,32 @@ class ProtocolGame; class PreySlot; class TaskHuntingSlot; class TaskHuntingOption; +class Item; +class Party; +class Creature; +class MonsterType; +class Npc; struct ModalWindow; +struct Position; +struct Outfit_t; +struct RecentDeathEntry; +struct RecentPvPKillEntry; struct Achievement; -struct Badge; -struct Title; +struct MarketOffer; +struct ShopBlock; +struct MarketOfferEx; +struct HistoryMarketOffer; +struct LightInfo; using ProtocolGame_ptr = std::shared_ptr; +using ItemVector = std::vector>; +using InvitedMap = std::map>; +using UsersMap = std::map>; +using MarketOfferList = std::list; +using HistoryMarketOfferList = std::list; +using ItemsTierCountList = std::map>; +using StashItemList = std::map; struct TextMessage { TextMessage() = default; diff --git a/src/server/network/protocol/protocollogin.cpp b/src/server/network/protocol/protocollogin.cpp index 3a7725533aa..d1768e68e58 100644 --- a/src/server/network/protocol/protocollogin.cpp +++ b/src/server/network/protocol/protocollogin.cpp @@ -8,6 +8,8 @@ */ #include "server/network/protocol/protocollogin.hpp" + +#include "config/configmanager.hpp" #include "server/network/message/outputmessage.hpp" #include "game/scheduling/dispatcher.hpp" #include "account/account.hpp" diff --git a/src/server/network/protocol/protocolstatus.cpp b/src/server/network/protocol/protocolstatus.cpp index 7d0ee35d747..fd2bd2968aa 100644 --- a/src/server/network/protocol/protocolstatus.cpp +++ b/src/server/network/protocol/protocolstatus.cpp @@ -7,11 +7,11 @@ * Website: https://docs.opentibiabr.com/ */ -#include "core.hpp" - #include "server/network/protocol/protocolstatus.hpp" #include "config/configmanager.hpp" +#include "core.hpp" +#include "creatures/players/player.hpp" #include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" #include "server/network/message/outputmessage.hpp" diff --git a/src/server/network/webhook/webhook.cpp b/src/server/network/webhook/webhook.cpp index 1f17ec8a0c8..006be8bbfaa 100644 --- a/src/server/network/webhook/webhook.cpp +++ b/src/server/network/webhook/webhook.cpp @@ -8,6 +8,7 @@ */ #include "server/network/webhook/webhook.hpp" + #include "config/configmanager.hpp" #include "game/scheduling/dispatcher.hpp" #include "utils/tools.hpp" diff --git a/src/server/server.cpp b/src/server/server.cpp index cc50149af50..2ba8f36dfd0 100644 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -7,8 +7,9 @@ * Website: https://docs.opentibiabr.com/ */ -#include "server/network/message/outputmessage.hpp" #include "server/server.hpp" + +#include "server/network/message/outputmessage.hpp" #include "config/configmanager.hpp" #include "game/scheduling/dispatcher.hpp" #include "creatures/players/management/ban.hpp" diff --git a/src/server/signals.cpp b/src/server/signals.cpp index e309f09ab1e..29382c55809 100644 --- a/src/server/signals.cpp +++ b/src/server/signals.cpp @@ -7,14 +7,18 @@ * Website: https://docs.opentibiabr.com/ */ +#include "server/signals.hpp" + +#include "config/configmanager.hpp" +#include "creatures/appearance/mounts/mounts.hpp" +#include "creatures/interactions/chat.hpp" #include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" #include "game/scheduling/save_manager.hpp" #include "lib/thread/thread_pool.hpp" #include "lua/creature/events.hpp" -#include "lua/scripts/lua_environment.hpp" #include "lua/global/globalevent.hpp" -#include "server/signals.hpp" +#include "lua/scripts/lua_environment.hpp" Signals::Signals(asio::io_service &service) : set(service) { @@ -109,7 +113,7 @@ void Signals::sighupHandler() { Item::items.reload(); g_logger().info("Reloaded items"); - g_game().mounts.reload(); + g_game().mounts->reload(); g_logger().info("Reloaded mounts"); g_events().loadFromXml(); diff --git a/src/utils/tools.cpp b/src/utils/tools.cpp index 301d2c61cf8..f393f5332ae 100644 --- a/src/utils/tools.cpp +++ b/src/utils/tools.cpp @@ -10,8 +10,11 @@ #include "utils/tools.hpp" #include "core.hpp" -#include "items/item.hpp" #include "enums/object_category.hpp" +#include "items/item.hpp" +#include "lua/lua_definitions.hpp" +#include "utils/const.hpp" +#include "config/configmanager.hpp" #include "absl/debugging/stacktrace.h" #include "absl/debugging/symbolize.h" @@ -2066,3 +2069,13 @@ void printStackTrace() { } } } + +const std::map &getMaxValuePerSkill() { + static std::map maxValuePerSkill = { + { SKILL_LIFE_LEECH_CHANCE, 100 }, + { SKILL_MANA_LEECH_CHANCE, 100 }, + { SKILL_CRITICAL_HIT_CHANCE, 100 * g_configManager().getNumber(CRITICALCHANCE) } + }; + + return maxValuePerSkill; +} diff --git a/src/utils/tools.hpp b/src/utils/tools.hpp index ad1df76509c..7a7d4a22bdd 100644 --- a/src/utils/tools.hpp +++ b/src/utils/tools.hpp @@ -210,3 +210,5 @@ EnumType enumFromValue(UnderlyingType value) { bool caseInsensitiveCompare(std::string_view str1, std::string_view str2, size_t length = std::string_view::npos); void printStackTrace(); + +const std::map &getMaxValuePerSkill(); diff --git a/tests/fixture/account/in_memory_account_repository.hpp b/tests/fixture/account/in_memory_account_repository.hpp index 8a294992274..3c4f5e3f061 100644 --- a/tests/fixture/account/in_memory_account_repository.hpp +++ b/tests/fixture/account/in_memory_account_repository.hpp @@ -33,10 +33,10 @@ namespace tests { accounts[descriptor] = acc; } - bool loadByID(const uint32_t &id, AccountInfo &acc) final { + bool loadByID(const uint32_t &id, std::unique_ptr &acc) final { for (const auto &account : accounts) { if (account.second.id == id) { - acc = account.second; + acc = std::make_unique(account.second); return true; } } @@ -44,29 +44,29 @@ namespace tests { return false; } - bool loadByEmailOrName(bool oldProtocol, const std::string &email, AccountInfo &acc) final { + bool loadByEmailOrName(bool oldProtocol, const std::string &email, std::unique_ptr &acc) final { auto account = accounts.find(email); if (account == accounts.end()) { return false; } - acc = account->second; + acc = std::make_unique(account->second); return true; } - bool loadBySession(const std::string &sessionKey, AccountInfo &acc) final { + bool loadBySession(const std::string &sessionKey, std::unique_ptr &acc) final { auto account = accounts.find(sessionKey); if (account == accounts.end()) { return false; } - acc = account->second; + acc = std::make_unique(account->second); return true; } - bool save(const AccountInfo &accInfo) final { + bool save(const std::unique_ptr &accInfo) final { return !failSave; } diff --git a/tests/integration/main.cpp b/tests/integration/main.cpp index 6fb0c291e49..019c385243d 100644 --- a/tests/integration/main.cpp +++ b/tests/integration/main.cpp @@ -36,15 +36,15 @@ void createAccount(Database &db) { )); } -void assertAccountLoad(AccountInfo acc) { - expect(eq(acc.id, 111)); - expect(eq(acc.accountType, AccountType::ACCOUNT_TYPE_SENIORTUTOR)); - expect(eq(acc.premiumRemainingDays, 11)); - expect(eq(acc.premiumLastDay, 1293912)); - expect(eq(acc.players.size(), 0)); - expect(eq(acc.oldProtocol, false)); - expect(eq(acc.premiumDaysPurchased, 11)); - expect(approx(acc.creationTime, 42183281, 60 * 60 * 1000)); +void assertAccountLoad(const std::unique_ptr &acc) { + expect(eq(acc->id, 111)); + expect(eq(acc->accountType, AccountType::ACCOUNT_TYPE_SENIORTUTOR)); + expect(eq(acc->premiumRemainingDays, 11)); + expect(eq(acc->premiumLastDay, 1293912)); + expect(eq(acc->players.size(), 0)); + expect(eq(acc->oldProtocol, false)); + expect(eq(acc->premiumDaysPurchased, 11)); + expect(approx(acc->creationTime, 42183281, 60 * 60 * 1000)); } int main() { @@ -74,10 +74,10 @@ int main() { AccountRepositoryDB accRepo{}; createAccount(db); - AccountInfo acc{}; + auto acc = std::make_unique(); accRepo.loadByID(111, acc); assertAccountLoad(acc); - expect(eq(acc.sessionExpires, 0)); + expect(eq(acc->sessionExpires, 0)); }); test("AccountRepositoryDB::loadByEmailOrName") = databaseTest(db, [&db] { @@ -85,10 +85,10 @@ int main() { AccountRepositoryDB accRepo {}; createAccount(db); - AccountInfo acc {}; + auto acc = std::make_unique(); accRepo.loadByEmailOrName(false, "@test", acc); assertAccountLoad(acc); - expect(eq(acc.sessionExpires, 0)); + expect(eq(acc->sessionExpires, 0)); }); test("AccountRepositoryDB::loadBySession") = databaseTest(db, [&db] { @@ -96,25 +96,25 @@ int main() { AccountRepositoryDB accRepo {}; createAccount(db); - AccountInfo acc {}; + auto acc = std::make_unique(); accRepo.loadBySession("test", acc); assertAccountLoad(acc); - expect(eq(acc.sessionExpires, 1337)); + expect(eq(acc->sessionExpires, 1337)); }); test("AccountRepositoryDB load sets premium day purchased = remaining days, if needed") = databaseTest(db, [&db] { InMemoryLogger logger{}; AccountRepositoryDB accRepo{}; - AccountInfo acc{}; + auto acc = std::make_unique(); accRepo.loadByID(1, acc); - acc.premiumRemainingDays = 10; + acc->premiumRemainingDays = 10; accRepo.save(acc); accRepo.loadByID(1, acc); - expect(eq(acc.premiumDaysPurchased, 10)); + expect(eq(acc->premiumDaysPurchased, 10)); }); test("AccountRepositoryDB::getPassword") = databaseTest(db, [&db] { @@ -143,21 +143,21 @@ int main() { InMemoryLogger logger {}; AccountRepositoryDB accRepo {}; - AccountInfo acc {}; - acc.id = 1; - acc.accountType = AccountType::ACCOUNT_TYPE_SENIORTUTOR; - acc.premiumRemainingDays = 10; - acc.premiumLastDay = 10; - acc.sessionExpires = 99999999; + auto acc = std::make_unique(); + acc->id = 1; + acc->accountType = AccountType::ACCOUNT_TYPE_SENIORTUTOR; + acc->premiumRemainingDays = 10; + acc->premiumLastDay = 10; + acc->sessionExpires = 99999999; expect(accRepo.save(acc)); - AccountInfo acc2 {}; + auto acc2 = std::make_unique(); accRepo.loadByID(1, acc2); - expect(eq(acc2.id, 1)); - expect(eq(acc2.accountType, AccountType::ACCOUNT_TYPE_SENIORTUTOR)); - expect(eq(acc2.premiumRemainingDays, 10)); - expect(eq(acc2.premiumLastDay, 10)); + expect(eq(acc2->id, 1)); + expect(eq(acc2->accountType, AccountType::ACCOUNT_TYPE_SENIORTUTOR)); + expect(eq(acc2->premiumRemainingDays, 10)); + expect(eq(acc2->premiumLastDay, 10)); // sessionExpires is not saved - expect(eq(acc2.sessionExpires, 0)); + expect(eq(acc2->sessionExpires, 0)); }); } diff --git a/tests/unit/account/account_test.cpp b/tests/unit/account/account_test.cpp index c0c3ebbb832..db3b84a3674 100644 --- a/tests/unit/account/account_test.cpp +++ b/tests/unit/account/account_test.cpp @@ -19,44 +19,49 @@ #include "enums/account_group_type.hpp" #include "utils/tools.hpp" +using namespace boost::ut; +using namespace std; + suite<"account"> accountTest = [] { - InjectionFixture injectionFixture {}; + InjectionFixture injectionFixture{}; + test("Account::Account default constructors") = [] { - Account byId { 1 }, byDescriptor { "canary@test.com" }; + shared_ptr byId = make_shared(1); + shared_ptr byDescriptor = make_shared("canary@test.com"); - expect(eq(byId.getID(), 1)); - expect(eq(byDescriptor.getID(), 0)); + expect(eq(byId->getID(), 1)); + expect(eq(byDescriptor->getID(), 0)); - expect(byId.getDescriptor().empty()); - expect(eq(byDescriptor.getDescriptor(), std::string { "canary@test.com" })); + expect(byId->getDescriptor().empty()); + expect(eq(byDescriptor->getDescriptor(), string{"canary@test.com"})); - for (auto &account : { byId, byDescriptor }) { + for (auto& account : { byId, byDescriptor }) { expect( - eq(account.getPremiumRemainingDays(), 0) and - eq(account.getPremiumLastDay(), 0) and - eq(account.getAccountType(), AccountType::ACCOUNT_TYPE_NORMAL) + eq(account->getPremiumRemainingDays(), 0) and + eq(account->getPremiumLastDay(), 0) and + eq(account->getAccountType(), AccountType::ACCOUNT_TYPE_NORMAL) ); } }; struct AccountLoadTestCase { - std::string description; - Account account; + string description; + shared_ptr account; AccountErrors_t expectedError; }; - std::vector accountLoadTestCases { - { "returns by id if exists", Account { 1 }, AccountErrors_t::Ok }, - { "returns by descriptor if exists", Account { "canary@test.com" }, AccountErrors_t::Ok }, - { "returns error if id is not valid", Account { 2 }, AccountErrors_t::LoadingAccount }, - { "returns error if descriptor is not valid", Account { "not@valid.com" }, AccountErrors_t::LoadingAccount } + vector accountLoadTestCases{ + {"returns by id if exists", make_shared(1), AccountErrors_t::Ok}, + {"returns by descriptor if exists", make_shared("canary@test.com"), AccountErrors_t::Ok}, + {"returns error if id is not valid", make_shared(2), AccountErrors_t::LoadingAccount}, + {"returns error if descriptor is not valid", make_shared("not@valid.com"), AccountErrors_t::LoadingAccount} }; - for (auto accountLoadTestCase : accountLoadTestCases) { - test(accountLoadTestCase.description) = [&injectionFixture, &accountLoadTestCase] { + for (auto& testCase : accountLoadTestCases) { + test(testCase.description) = [&injectionFixture, &testCase] { auto [accountRepository] = injectionFixture.get(); - accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); - expect(eq(accountLoadTestCase.account.load(), enumToValue(accountLoadTestCase.expectedError))) << accountLoadTestCase.description; + accountRepository.addAccount("canary@test.com", AccountInfo{1, 1, 1, AccountType::ACCOUNT_TYPE_GOD}); + expect(eq(testCase.account->load(), enumToValue(testCase.expectedError))) << testCase.description; }; } @@ -242,9 +247,9 @@ suite<"account"> accountTest = [] { Account acc { 1 }; accountRepository.failAddCoins = false; - accountRepository.setCoins(1, enumToValue(CoinType::Normal), 100); - accountRepository.setCoins(1, enumToValue(CoinType::Tournament), 57); - accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); + accountRepository.setCoins(1, enumToValue(CoinType::Normal), 100); + accountRepository.setCoins(1, enumToValue(CoinType::Tournament), 57); + accountRepository.addAccount("canary@test.com", AccountInfo { 1, 1, 1, AccountType::ACCOUNT_TYPE_GOD }); expect( eq(acc.load(), enumToValue(AccountErrors_t::Ok)) and From 135866a617281954de1bcf4a357fc64c729a8f94 Mon Sep 17 00:00:00 2001 From: HT Cesta <58153179+htc16@users.noreply.github.com> Date: Wed, 30 Oct 2024 20:06:46 -0300 Subject: [PATCH 2/3] fix: quests from version 11 revised (#2938) --- data-otservbr-global/lib/core/quests.lua | 208 ++++-- data-otservbr-global/lib/core/storages.lua | 656 ++++++++++-------- .../monster/bosses/the_book_of_death.lua | 4 + .../monster/bosses/the_flaming_orchid.lua | 126 ++++ .../quests/cults_of_tibia/misguided_bully.lua | 8 +- .../cults_of_tibia/misguided_shadow.lua | 8 +- .../quests/cults_of_tibia/misguided_thief.lua | 4 + .../quests/the_first_dragon/angry_plant.lua | 4 + .../bosses/fallen_challenger.lua | 4 + .../the_first_dragon/dragon_essence.lua | 4 + .../the_first_dragon/somewhat_beatable.lua | 2 +- .../the_secret_library/bosses/brokul.lua | 4 + .../the_secret_library/bosses/ghulosh.lua | 2 +- .../the_secret_library/bosses/gorzindel.lua | 2 +- .../bosses/grand_canon_dominus.lua | 2 +- .../bosses/grand_chaplain_gaunder.lua | 2 +- .../bosses/grand_commander_soeren.lua | 2 +- .../bosses/grand_master_oberon.lua | 5 + .../bosses/preceptor_lazare.lua | 2 +- .../the_secret_library/concentrated_death.lua | 4 + .../the_secret_library/dazed_leaf_golem.lua | 2 +- .../quests/the_secret_library/lokathmor.lua | 2 +- .../quests/the_secret_library/mazzinor.lua | 3 +- .../stolen_knowledge_of_armor.lua | 4 + .../stolen_knowledge_of_healing.lua | 4 + .../stolen_knowledge_of_lifesteal.lua | 4 + .../stolen_knowledge_of_spells.lua | 4 + .../stolen_knowledge_of_summoning.lua | 4 + .../the_secret_library/the_blazing_rose.lua | 4 + .../the_diamond_blossom.lua | 4 + .../the_secret_library/the_lily_of_night.lua | 4 + data-otservbr-global/npc/a_dragon_mother.lua | 12 +- .../npc/a_strange_chalice.lua | 10 +- .../npc/a_weakened_forest_fury.lua | 58 +- data-otservbr-global/npc/ahmet.lua | 2 +- data-otservbr-global/npc/albinius.lua | 77 +- data-otservbr-global/npc/alkestios.lua | 22 +- data-otservbr-global/npc/angelo.lua | 111 ++- data-otservbr-global/npc/buddel_helheim.lua | 8 +- data-otservbr-global/npc/buddel_okolnir.lua | 8 +- .../npc/buddel_raider_camp.lua | 8 +- data-otservbr-global/npc/buddel_tyrsung.lua | 8 +- data-otservbr-global/npc/charles.lua | 97 ++- data-otservbr-global/npc/dedoras.lua | 266 ++++++- data-otservbr-global/npc/denominator.lua | 102 ++- data-otservbr-global/npc/gail.lua | 7 +- data-otservbr-global/npc/gareth.lua | 127 ++-- .../npc/gelidrazahs_thirst.lua | 2 +- data-otservbr-global/npc/gerimor.lua | 350 ++++------ data-otservbr-global/npc/gnomus.lua | 289 ++++---- data-otservbr-global/npc/ivalisse.lua | 122 ++-- data-otservbr-global/npc/iwar.lua | 25 +- data-otservbr-global/npc/jamesfrancis.lua | 77 +- data-otservbr-global/npc/kizar.lua | 56 ++ data-otservbr-global/npc/klom_stonecutter.lua | 242 ++++--- data-otservbr-global/npc/lardoc_bashsmite.lua | 206 +++--- data-otservbr-global/npc/maelyrra.lua | 64 +- data-otservbr-global/npc/muriel.lua | 55 +- data-otservbr-global/npc/noozer.lua | 40 +- data-otservbr-global/npc/spectulus.lua | 30 +- data-otservbr-global/npc/tamoril.lua | 38 +- data-otservbr-global/npc/the_first_dragon.lua | 36 +- data-otservbr-global/npc/tigo.lua | 25 +- .../bosses_levers/dragonking_zyrtarch.lua | 30 - .../actions/bosses_levers/lady_tenebris.lua | 28 - .../scripts/actions/bosses_levers/lloyd.lua | 29 - .../bosses_levers/melting_frozen_horror.lua | 34 - .../bosses_levers/the_last_lore_keeper.lua | 44 -- .../bosses_levers/the_thorn_knight.lua | 30 - .../bosses_levers/the_time_guardian.lua | 27 - .../scripts/actions/farmine/boat.lua | 36 - .../actions/object/imbuement_shrine.lua | 2 +- .../actions/other/others/quest_system2.lua | 2 +- .../actions/valuables/random_items.lua | 7 + .../creaturescripts/customs/freequests.lua | 168 ++--- .../scripts/lib/register_actions.lua | 60 +- .../movements/teleport/falcon_castle.lua | 71 -- .../cradle_of_monsters/the_monster_fight.lua | 1 + .../cults_of_tibia/actions_analyser.lua | 4 +- .../cults_of_tibia/actions_bosses_levers.lua | 322 +++++---- .../cults_of_tibia/actions_counter_agent.lua | 4 +- .../quests/cults_of_tibia/actions_crate.lua | 6 +- .../cults_of_tibia/actions_cult_symbol.lua | 19 +- .../cults_of_tibia/actions_document.lua | 7 +- .../cults_of_tibia/actions_last_object.lua | 7 +- .../cults_of_tibia/actions_lever_mota.lua | 71 +- .../cults_of_tibia/actions_magnifier.lua | 4 +- .../cults_of_tibia/actions_misguided.lua | 23 +- .../quests/cults_of_tibia/actions_torch.lua | 7 +- .../creaturescripts_bosses_mission_cults.lua | 14 +- ...creaturescripts_glowing_rubbish_amulet.lua | 26 +- .../creaturescripts_minotaur_task_count.lua | 4 +- .../cults_of_tibia/creaturescripts_splash.lua | 1 + .../cults_of_tibia/movements_begin_task.lua | 21 +- .../cults_of_tibia/movements_check_oasis.lua | 4 +- .../cults_of_tibia/movements_energy_fence.lua | 24 +- .../quests/cults_of_tibia/movements_ice.lua | 37 +- .../cults_of_tibia/movements_ice_death.lua | 29 +- .../cults_of_tibia/movements_looktype.lua | 12 +- .../movements_river_teleport.lua | 4 +- .../movements_sand_entrance.lua | 2 +- .../quests/cults_of_tibia/movements_tar.lua | 108 ++- .../movements_task_teleport.lua | 12 +- .../actions_crude_lava_pump.lua | 12 +- .../actions_crude_lava_pump_achievements.lua | 18 +- .../dangerous_depth/actions_gnome_chart.lua | 40 +- .../dangerous_depth/actions_gnome_items.lua | 22 +- .../actions_gnome_trignometre.lua | 34 +- .../dangerous_depth/actions_gnomish_chest.lua | 9 +- .../actions_gnomish_pesticide.lua | 9 +- .../dangerous_depth/actions_lever_barrel.lua | 94 ++- .../actions_using_crystals.lua | 36 +- .../actions_warzone_crystals.lua | 36 +- .../dangerous_depth/actions_wooden_trash.lua | 5 +- .../creaturescripts_lost_exile_task.lua | 41 +- .../movements_energy_entrance.lua | 24 +- .../movements_gnome_ordnance.lua | 4 +- .../movements_gnomes_ordnance_end.lua | 12 +- .../dangerous_depth/movements_lost_exiles.lua | 6 +- .../movements_warzone_entrance.lua | 55 +- .../actions_ferumbras_lever.lua | 153 ++-- .../ferumbras_ascension/actions_mazoran.lua | 120 +--- .../ferumbras_ascension/actions_plagirath.lua | 115 +-- .../ferumbras_ascension/actions_ragiaz.lua | 133 ++-- .../ferumbras_ascension/actions_rat_lever.lua | 117 +--- .../ferumbras_ascension/actions_razzagorn.lua | 112 +-- .../ferumbras_ascension/actions_shulgrax.lua | 118 +--- .../ferumbras_ascension/actions_tarbaz.lua | 115 +-- .../actions_the_shatterer_lever.lua | 128 +--- .../actions_the_shatterer_levers.lua | 27 +- .../ferumbras_ascension/actions_zamulosh.lua | 126 +--- .../forgotten_knowledge/actions_bird_cage.lua | 6 +- .../actions_dragonking_zyrtarch.lua | 52 ++ .../forgotten_knowledge/actions_fount.lua | 4 +- .../actions_frozen_horror.lua | 59 ++ .../actions_girl_picture.lua | 4 +- .../actions_lady_tenebris.lua | 43 ++ .../forgotten_knowledge/actions_lloyd.lua | 51 ++ .../forgotten_knowledge/actions_old_desk.lua | 10 +- .../forgotten_knowledge/actions_plant.lua | 14 +- .../actions_secret_wall.lua | 2 +- .../actions_servants_mechanism.lua | 8 +- .../actions_the_last_lore_keeper.lua | 51 ++ .../actions_the_thorn_knight.lua | 43 ++ .../actions_the_time_guardian.lua | 47 ++ .../actions_time_machine.lua | 1 + .../creaturescripts_astral_source.lua | 4 +- .../creaturescripts_bosses_kill.lua | 29 +- .../creaturescripts_bound_astral_power.lua | 10 +- .../creaturescripts_distorted_source.lua | 4 +- .../creaturescripts_energy_prism_death.lua | 2 +- .../creaturescripts_lloyd_preparedeath.lua | 2 +- .../creaturescripts_replica_servants.lua | 10 +- .../movements_challenger.lua | 2 +- .../movements_entrance_teleport.lua | 44 +- .../forgotten_knowledge/movements_fount.lua | 2 +- .../movements_ice_teleport.lua | 2 +- .../movements_lava_teleport.lua | 2 +- .../movements_servant_teleport.lua | 2 +- .../movements_teleport_tree.lua | 2 +- .../roshamuul_quest/actions_mixture.lua | 1 + .../action_water_fountain.lua | 2 +- .../actions_lair_entrance.lua | 2 +- .../quests/the_first_dragon/actions_lever.lua | 53 +- .../the_first_dragon/actions_rewards.lua | 30 +- .../actions_sacrifice_items.lua | 16 +- .../actions_treasure_chest.lua | 14 +- .../creaturescripts_death_first_dragon.lua | 4 +- ...reaturescripts_death_somewhat_beatable.lua | 8 +- .../creaturescripts_kill_dragon.lua | 4 +- .../movements_entrance_teleport.lua | 12 +- .../movements_last_teleport.lua | 26 +- .../movements_zorvorax_secrets.lua | 17 +- .../actions_asura_mirror.lua | 21 - .../actions_basinfire.lua | 18 - .../actions_bastion_access.lua | 13 - .../actions_blacktp.lua | 14 - .../actions_bluetp.lua | 14 - .../actions_boattp.lua | 17 - .../the_secret_library_quest/actions_bone.lua | 13 - .../actions_bonedoor.lua | 17 - .../the_secret_library_quest/actions_book.lua | 14 - .../actions_bosses_killed.lua | 32 - .../actions_corpse.lua | 14 - .../actions_energybasin.lua | 44 -- .../actions_enterlibrary.lua | 15 - .../actions_eyekey.lua | 14 - .../the_secret_library_quest/actions_fish.lua | 14 - .../actions_greentp.lua | 14 - .../actions_leverdoor.lua | 14 - .../actions_leverruby.lua | 14 - .../actions_library_entrances.lua | 43 -- .../actions_lotuskey.lua | 14 - .../actions_peacock.lua | 14 - .../actions_pinktp.lua | 14 - .../actions_sealedbook.lua | 14 - .../actions_skullground.lua | 15 - .../actions_statuedeeplings.lua | 34 - .../actions_strandhair.lua | 14 - .../actions_telescop.lua | 13 - .../actions_bony_rod.lua | 29 + .../actions_doors.lua | 27 + .../actions_gems.lua | 27 + .../actions_levers.lua | 55 ++ .../actions_sample_blood.lua | 15 + .../movements_teleportTo.lua | 67 ++ .../creaturescripts_falcon_minibosses.lua | 56 -- .../creaturescripts_kill.lua | 57 ++ .../creaturescripts_login.lua | 8 + ...nts_secret_library_grand_canon_dominus.lua | 16 - ..._secret_library_grand_chaplain_gaunder.lua | 16 - ..._secret_library_grand_commander_soeren.lua | 16 - ...events_secret_library_preceptor_lazare.lua | 14 - ...obalevents_secret_library_spawn_damage.lua | 84 --- .../high_and_dry_isles/actions_telescope.lua | 13 + .../movements_boat_puzzle.lua | 43 ++ .../high_and_dry_isles/movements_stepIn.lua | 100 +++ .../library_area/actions_bossesLever.lua | 167 +++++ .../library_area/actions_parchment.lua | 54 ++ .../library_area/creaturescripts_ghulosh.lua | 67 ++ .../creaturescripts_gorzindel.lua | 63 ++ .../creaturescripts_lokathmor.lua | 12 + .../library_area/creaturescripts_mazzinor.lua | 33 + .../library_area/movements_gorzindel.lua | 39 ++ .../library_area/movements_mazzinor.lua | 21 + .../library_area/movements_timers.lua | 37 + .../liquid_death/actions_brokulLever.lua | 113 +++ .../liquid_death/actions_usableItens.lua | 89 +++ .../liquid_death/movements_bossWay.lua | 41 ++ .../liquid_death/movements_teleportTo.lua | 53 ++ .../movements_boat.lua | 17 - .../movements_golem_teleport.lua | 17 - .../movements_pink_teleport.lua | 17 - .../movements_teleport.lua | 35 - .../the_lament_asuras/actions_bonefiddle.lua | 46 ++ .../the_lament_asuras/actions_doors.lua | 28 + .../the_lament_asuras/actions_fragrance.lua | 12 + .../the_lament_asuras/actions_keys.lua | 35 + .../the_lament_asuras/actions_mirror.lua | 19 + .../creaturescripts_asuras_mechanic.lua | 50 ++ .../movements_elemental_portals.lua | 158 +++++ .../the_order_of_the_falcon/actions_doors.lua | 75 ++ .../actions_entrance.lua | 28 + .../actions_oberonLever.lua | 39 ++ .../movements_bossEntrance.lua | 53 ++ .../actions_chests.lua | 31 + .../actions_doors.lua | 30 + .../actions_totems.lua | 54 ++ .../movements_color_puzzle.lua | 41 ++ .../movements_sacrifice_foods.lua | 237 +++++++ .../movements_teleportTo.lua | 187 +++++ ...release.lua => action_fairies_release.lua} | 0 ...moon_mirror.lua => action_moon_mirror.lua} | 0 ...acher_book.lua => action_poacher_book.lua} | 0 ...her_notes.lua => action_poacher_notes.lua} | 0 ...ght_vial.lua => action_starlight_vial.lua} | 0 ...sun_catcher.lua => action_sun_catcher.lua} | 0 ...ion-whelp_fur.lua => action_whelp_fur.lua} | 0 ...erb_bush.lua => event_raven_herb_bush.lua} | 0 ...zur_room.lua => movement_kroazur_room.lua} | 1 + ...r_notes.lua => movement_poacher_notes.lua} | 0 ...eathers.lua => movement_swan_feathers.lua} | 0 .../scripts/spells/monster/exploding_cask.lua | 2 +- .../iron_servant_transformation.lua | 6 +- data-otservbr-global/startup/tables/chest.lua | 170 ++++- .../startup/tables/door_quest.lua | 83 +-- data-otservbr-global/startup/tables/item.lua | 540 ++++++++++++++ data-otservbr-global/startup/tables/lever.lua | 61 +- .../startup/tables/teleport.lua | 67 ++ .../startup/tables/teleport_item.lua | 82 ++- data-otservbr-global/startup/tables/tile.lua | 1 + .../world/otservbr-monster.xml | 22 +- data-otservbr-global/world/otservbr-npc.xml | 3 + data/events/scripts/player.lua | 12 +- data/items/items.xml | 12 +- data/libs/functions/boss_lever.lua | 16 +- data/modules/scripts/cults_of_tibia/death.lua | 4 +- 277 files changed, 7217 insertions(+), 4432 deletions(-) create mode 100644 data-otservbr-global/monster/bosses/the_flaming_orchid.lua create mode 100644 data-otservbr-global/npc/kizar.lua delete mode 100644 data-otservbr-global/scripts/actions/bosses_levers/dragonking_zyrtarch.lua delete mode 100644 data-otservbr-global/scripts/actions/bosses_levers/lady_tenebris.lua delete mode 100644 data-otservbr-global/scripts/actions/bosses_levers/lloyd.lua delete mode 100644 data-otservbr-global/scripts/actions/bosses_levers/melting_frozen_horror.lua delete mode 100644 data-otservbr-global/scripts/actions/bosses_levers/the_last_lore_keeper.lua delete mode 100644 data-otservbr-global/scripts/actions/bosses_levers/the_thorn_knight.lua delete mode 100644 data-otservbr-global/scripts/actions/bosses_levers/the_time_guardian.lua delete mode 100644 data-otservbr-global/scripts/actions/farmine/boat.lua delete mode 100644 data-otservbr-global/scripts/movements/teleport/falcon_castle.lua create mode 100644 data-otservbr-global/scripts/quests/forgotten_knowledge/actions_dragonking_zyrtarch.lua create mode 100644 data-otservbr-global/scripts/quests/forgotten_knowledge/actions_frozen_horror.lua create mode 100644 data-otservbr-global/scripts/quests/forgotten_knowledge/actions_lady_tenebris.lua create mode 100644 data-otservbr-global/scripts/quests/forgotten_knowledge/actions_lloyd.lua create mode 100644 data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_last_lore_keeper.lua create mode 100644 data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_thorn_knight.lua create mode 100644 data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_time_guardian.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_asura_mirror.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_basinfire.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bastion_access.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_blacktp.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bluetp.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_boattp.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bone.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bonedoor.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_book.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bosses_killed.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_corpse.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_energybasin.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_enterlibrary.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_eyekey.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_fish.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_greentp.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_leverdoor.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_leverruby.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_library_entrances.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_lotuskey.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_peacock.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_pinktp.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_sealedbook.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_skullground.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_statuedeeplings.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_strandhair.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/actions_telescop.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_bony_rod.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_doors.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_gems.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_levers.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_sample_blood.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/movements_teleportTo.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/creaturescripts_falcon_minibosses.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/creaturescripts_kill.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/creaturescripts_login.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_grand_canon_dominus.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_grand_chaplain_gaunder.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_grand_commander_soeren.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_preceptor_lazare.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_spawn_damage.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/high_and_dry_isles/actions_telescope.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/high_and_dry_isles/movements_boat_puzzle.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/high_and_dry_isles/movements_stepIn.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_bossesLever.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_parchment.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/creaturescripts_ghulosh.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/creaturescripts_gorzindel.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/creaturescripts_lokathmor.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/creaturescripts_mazzinor.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/movements_gorzindel.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/movements_mazzinor.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/movements_timers.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/liquid_death/actions_brokulLever.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/liquid_death/actions_usableItens.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/liquid_death/movements_bossWay.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/liquid_death/movements_teleportTo.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/movements_boat.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/movements_golem_teleport.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/movements_pink_teleport.lua delete mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/movements_teleport.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_bonefiddle.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_doors.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_fragrance.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_keys.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_mirror.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/creaturescripts_asuras_mechanic.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/movements_elemental_portals.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/the_order_of_the_falcon/actions_doors.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/the_order_of_the_falcon/actions_entrance.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/the_order_of_the_falcon/actions_oberonLever.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/the_order_of_the_falcon/movements_bossEntrance.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/actions_chests.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/actions_doors.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/actions_totems.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/movements_color_puzzle.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/movements_sacrifice_foods.lua create mode 100644 data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/movements_teleportTo.lua rename data-otservbr-global/scripts/quests/threatened_dreams/{action-fairies_release.lua => action_fairies_release.lua} (100%) rename data-otservbr-global/scripts/quests/threatened_dreams/{action-moon_mirror.lua => action_moon_mirror.lua} (100%) rename data-otservbr-global/scripts/quests/threatened_dreams/{action-poacher_book.lua => action_poacher_book.lua} (100%) rename data-otservbr-global/scripts/quests/threatened_dreams/{action-poacher_notes.lua => action_poacher_notes.lua} (100%) rename data-otservbr-global/scripts/quests/threatened_dreams/{action-starlight_vial.lua => action_starlight_vial.lua} (100%) rename data-otservbr-global/scripts/quests/threatened_dreams/{action-sun_catcher.lua => action_sun_catcher.lua} (100%) rename data-otservbr-global/scripts/quests/threatened_dreams/{action-whelp_fur.lua => action_whelp_fur.lua} (100%) rename data-otservbr-global/scripts/quests/threatened_dreams/{event-raven_herb_bush.lua => event_raven_herb_bush.lua} (100%) rename data-otservbr-global/scripts/quests/threatened_dreams/{movement-kroazur_room.lua => movement_kroazur_room.lua} (99%) rename data-otservbr-global/scripts/quests/threatened_dreams/{movement-poacher_notes.lua => movement_poacher_notes.lua} (100%) rename data-otservbr-global/scripts/quests/threatened_dreams/{movement-swan_feathers.lua => movement_swan_feathers.lua} (100%) diff --git a/data-otservbr-global/lib/core/quests.lua b/data-otservbr-global/lib/core/quests.lua index 8d7d9ede37c..19b5a058986 100644 --- a/data-otservbr-global/lib/core/quests.lua +++ b/data-otservbr-global/lib/core/quests.lua @@ -5169,12 +5169,12 @@ if not Quests then }, [36] = { name = "Forgotten Knowledge", - startStorageId = Storage.ForgottenKnowledge.Tomes, + startStorageId = Storage.Quest.U11_02.ForgottenKnowledge.Tomes, startStorageValue = 1, missions = { [1] = { name = "Circle of the Black Sphinx", - storageId = Storage.ForgottenKnowledge.LadyTenebrisKilled, + storageId = Storage.Quest.U11_02.ForgottenKnowledge.LadyTenebrisKilled, missionId = 10361, startValue = 0, endValue = 1522018605, @@ -5184,7 +5184,7 @@ if not Quests then }, [2] = { name = "Bane of the Cosmic Force", - storageId = Storage.ForgottenKnowledge.LloydKilled, + storageId = Storage.Quest.U11_02.ForgottenKnowledge.LloydKilled, missionId = 10362, startValue = 0, endValue = 1522018605, @@ -5195,7 +5195,7 @@ if not Quests then }, [3] = { name = "The Desecrated Glade", - storageId = Storage.ForgottenKnowledge.ThornKnightKilled, + storageId = Storage.Quest.U11_02.ForgottenKnowledge.ThornKnightKilled, missionId = 10363, startValue = 0, endValue = 1522018605, @@ -5206,7 +5206,7 @@ if not Quests then }, [4] = { name = "The Unwary Mage", - storageId = Storage.ForgottenKnowledge.DragonkingKilled, + storageId = Storage.Quest.U11_02.ForgottenKnowledge.DragonkingKilled, missionId = 10364, startValue = 0, endValue = 1522018605, @@ -5217,7 +5217,7 @@ if not Quests then }, [5] = { name = "Dragon in Distress", - storageId = Storage.ForgottenKnowledge.HorrorKilled, + storageId = Storage.Quest.U11_02.ForgottenKnowledge.HorrorKilled, missionId = 10365, startValue = 0, endValue = 1522018605, @@ -5227,7 +5227,7 @@ if not Quests then }, [6] = { name = "Time is a Window", - storageId = Storage.ForgottenKnowledge.TimeGuardianKilled, + storageId = Storage.Quest.U11_02.ForgottenKnowledge.TimeGuardianKilled, missionId = 10366, startValue = 0, endValue = 1522018605, @@ -5238,7 +5238,7 @@ if not Quests then }, [7] = { name = "Final Fight", - storageId = Storage.ForgottenKnowledge.LastLoreKilled, + storageId = Storage.Quest.U11_02.ForgottenKnowledge.LastLoreKilled, missionId = 10367, startValue = 0, endValue = 1522018605, @@ -5250,22 +5250,22 @@ if not Quests then }, [37] = { name = "The First Dragon", - startStorageId = Storage.FirstDragon.Questline, + startStorageId = Storage.Quest.U11_02.TheFirstDragon.Questline, startStorageValue = 1, missions = { [1] = { name = "Power", - storageId = Storage.FirstDragon.DragonCounter, + storageId = Storage.Quest.U11_02.TheFirstDragon.DragonCounter, missionId = 10368, startValue = 0, endValue = 200, description = function(player) - return ("You already hunted %d/200 dragons."):format(player:getStorageValue(Storage.FirstDragon.DragonCounter)) + return ("You already hunted %d/200 dragons."):format(player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.DragonCounter)) end, }, [2] = { name = "Treasure", - storageId = Storage.FirstDragon.ChestCounter, + storageId = Storage.Quest.U11_02.TheFirstDragon.ChestCounter, missionId = 10369, startValue = 0, endValue = 20, @@ -5274,7 +5274,7 @@ if not Quests then }, [3] = { name = "Knowledge", - storageId = Storage.FirstDragon.GelidrazahAccess, + storageId = Storage.Quest.U11_02.TheFirstDragon.GelidrazahAccess, missionId = 10370, startValue = 0, endValue = 1, @@ -5283,7 +5283,7 @@ if not Quests then }, [4] = { name = "Life", - storageId = Storage.FirstDragon.SecretsCounter, + storageId = Storage.Quest.U11_02.TheFirstDragon.SecretsCounter, missionId = 10371, startValue = 0, endValue = 3, @@ -5294,23 +5294,23 @@ if not Quests then }, [38] = { name = "Cults of Tibia", - startStorageId = Storage.CultsOfTibia.Questline, + startStorageId = Storage.Quest.U11_40.CultsOfTibia.Questline, startStorageValue = 1, missions = { [1] = { name = "The Strengthtening of the Minotaurs", - storageId = Storage.CultsOfTibia.Minotaurs.JamesfrancisTask, + storageId = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.JamesfrancisTask, missionId = 10372, startValue = 0, endValue = 50, description = function(player) return ("James asked you to enter the cave for hunting 50 empowered minotaurs. \z - Then he will be able to continue his research.\nMinotaurs killed: %d/50"):format(player:getStorageValue(Storage.CultsOfTibia.Minotaurs.JamesfrancisTask)) + Then he will be able to continue his research.\nMinotaurs killed: %d/50"):format(player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Minotaurs.JamesfrancisTask)) end, }, [2] = { name = "The Strengthtening of the Minotaurs", - storageId = Storage.CultsOfTibia.Minotaurs.Mission, + storageId = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.Mission, missionId = 10373, startValue = 1, endValue = 5, @@ -5326,7 +5326,7 @@ if not Quests then }, [3] = { name = "Patron of Arts", - storageId = Storage.CultsOfTibia.MotA.Mission, + storageId = Storage.Quest.U11_40.CultsOfTibia.MotA.Mission, missionId = 10374, startValue = 1, endValue = 15, @@ -5359,7 +5359,7 @@ if not Quests then }, [4] = { name = "Barkless", - storageId = Storage.CultsOfTibia.Barkless.Mission, + storageId = Storage.Quest.U11_40.CultsOfTibia.Barkless.Mission, missionId = 10375, startValue = 1, endValue = 7, @@ -5371,7 +5371,7 @@ if not Quests then The hardest part, however, is yet to come. Give your life to the ice... to become true and purified.", [3] = function(player) return ("You survived the Trial. Barkless now have the right to see the cult leader but a \z - powerful relic is sealing the path. Barkless markings broken to reverse the power of the cult object: \z %d of 10"):format(math.max(player:getStorageValue(Storage.CultsOfTibia.Barkless.Objects), 0)) + powerful relic is sealing the path. Barkless markings broken to reverse the power of the cult object: \z %d of 10"):format(math.max(player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Objects), 0)) end, [4] = "You broke enough Barkless markings to now reverse the seal \z held up by the cult object in the ritual chamber. \z @@ -5387,7 +5387,7 @@ if not Quests then }, [5] = { name = "Misguided", - storageId = Storage.CultsOfTibia.Misguided.Mission, + storageId = Storage.Quest.U11_40.CultsOfTibia.Misguided.Mission, missionId = 10376, startValue = 1, endValue = 5, @@ -5397,7 +5397,7 @@ if not Quests then hinted at something weird going on in a cave. He seemed too confused to decide whether to stop you.", [3] = function(player) return ("While rubbish, the amulet you equipe emits a strange aura of splendour. \z - You feel an urge to fulfill the amulets hunger for especific deaths... Exorcisms: %d/5 "):format(math.max(player:getStorageValue(Storage.CultsOfTibia.Misguided.Exorcisms), 0)) + You feel an urge to fulfill the amulets hunger for especific deaths... Exorcisms: %d/5 "):format(math.max(player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.Exorcisms), 0)) end, [4] = "You defeated the cult leader of Misguided by uncovering the true master to pull his strings \z and freeing this world from its malicious existance. Return to Gerimor to tell him about the victory.", @@ -5406,7 +5406,7 @@ if not Quests then }, [6] = { name = "The Orc Idol", - storageId = Storage.CultsOfTibia.Orcs.Mission, + storageId = Storage.Quest.U11_40.CultsOfTibia.Orcs.Mission, missionId = 10377, startValue = 1, endValue = 3, @@ -5420,7 +5420,7 @@ if not Quests then }, [7] = { name = "The Secret of the Sandy Cave", - storageId = Storage.CultsOfTibia.Life.Mission, + storageId = Storage.Quest.U11_40.CultsOfTibia.Life.Mission, missionId = 10378, startValue = 1, endValue = 10, @@ -5445,7 +5445,7 @@ if not Quests then }, [8] = { name = "Zathroth Remmants", - storageId = Storage.CultsOfTibia.Humans.Mission, + storageId = Storage.Quest.U11_40.CultsOfTibia.Humans.Mission, missionId = 10379, startValue = 1, endValue = 3, @@ -5463,12 +5463,12 @@ if not Quests then }, [39] = { name = "Dangerous Depths", - startStorageId = Storage.DangerousDepths.Questline, + startStorageId = Storage.Quest.U11_50.DangerousDepths.Questline, startStorageValue = 1, missions = { [1] = { name = "Dwarves: Home Improvement", - storageId = Storage.DangerousDepths.Dwarves.Home, + storageId = Storage.Quest.U11_50.DangerousDepths.Dwarves.Home, missionId = 10380, startValue = 1, endValue = 2, @@ -5476,14 +5476,14 @@ if not Quests then [1] = function(player) return ("Destroy makeshift homes of the Lost to force them to fight you! Try making some \z prisoners in the progress and report back to Klom Stonecutter.\n\nLost Exiles: %d/20\nPrisoners (bonus): \z - %d/3"):format(math.max(player:getStorageValue(Storage.DangerousDepths.Dwarves.LostExiles), 0), math.max(player:getStorageValue(Storage.DangerousDepths.Dwarves.Prisoners), 0)) + %d/3"):format(math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.LostExiles), 0), math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Prisoners), 0)) end, [2] = "You drove off the Lost but more are sure to come. Check back with Klom Stonecutter at a later time.", }, }, [2] = { name = "Dwarves: Subterranean Life", - storageId = Storage.DangerousDepths.Dwarves.Subterranean, + storageId = Storage.Quest.U11_50.DangerousDepths.Dwarves.Subterranean, missionId = 10381, startValue = 1, endValue = 2, @@ -5491,7 +5491,7 @@ if not Quests then [1] = function(player) return ("Klome Stonecutter sent you on a grave mission to exterminate large populaces of \z subterranian life. Looks like the dwarves make short work of the deep intruders.\n\nSubterranean organisms: \z - %d/50"):format(math.max(player:getStorageValue(Storage.DangerousDepths.Dwarves.Organisms), 0)) + %d/50"):format(math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Organisms), 0)) end, [2] = "You helped Klom defend the outpost by cutting down a number of vermin from deep down below. \z The gnomes don't seem to completely approve of this but everyone appreciates the drop in the enemy's ranks.", @@ -5499,7 +5499,7 @@ if not Quests then }, [3] = { name = "Gnomes: Gnomal Warming Measurements", - storageId = Storage.DangerousDepths.Gnomes.Measurements, + storageId = Storage.Quest.U11_50.DangerousDepths.Gnomes.Measurements, missionId = 10382, startValue = 1, endValue = 2, @@ -5507,11 +5507,11 @@ if not Quests then [1] = function(player) return ("Gnomus sent you on a mission to measure the rising heat from below.\n\nLocation A: \z %d/1\nLocation B: %d/1\nLocation C: %d/1\nLocation D: %d/1\nLocation E: %d/1"):format( - math.max(player:getStorageValue(Storage.DangerousDepths.Gnomes.LocationA), 0), - math.max(player:getStorageValue(Storage.DangerousDepths.Gnomes.LocationB), 0), - math.max(player:getStorageValue(Storage.DangerousDepths.Gnomes.LocationC), 0), - math.max(player:getStorageValue(Storage.DangerousDepths.Gnomes.LocationD), 0), - math.max(player:getStorageValue(Storage.DangerousDepths.Gnomes.LocationE), 0) + math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationA), 0), + math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationB), 0), + math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationC), 0), + math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationD), 0), + math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationE), 0) ) end, [2] = "You helped Lardoc Bashsmite fighting back the verminous growth in the northern mineshaft. \z @@ -5520,7 +5520,7 @@ if not Quests then }, [4] = { name = "Gnomes: Ordnance", - storageId = Storage.DangerousDepths.Gnomes.Ordnance, + storageId = Storage.Quest.U11_50.DangerousDepths.Gnomes.Ordnance, missionId = 10383, startValue = 1, endValue = 3, @@ -5529,7 +5529,7 @@ if not Quests then Travel to the east of the cave system and find the old gnome trail where reinforcements will arrive.", [2] = function(player) return ("You found the old gnome trail where ordnance for the gnome outpost arrive, escort them \z - and their pack animals to safety and return to Gnomus.\n\nRescued gnomes: %d/5\nRescued animals: %d/3"):format(math.max(player:getStorageValue(Storage.DangerousDepths.Gnomes.GnomesCount), 0), math.max(player:getStorageValue(Storage.DangerousDepths.Gnomes.CrawlersCount), 0)) + and their pack animals to safety and return to Gnomus.\n\nRescued gnomes: %d/5\nRescued animals: %d/3"):format(math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.GnomesCount), 0), math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.CrawlersCount), 0)) end, [3] = "You helped Lardoc Bashsmite fighting back the verminous growth in the northern mineshaft. \z Return to him later to see if he has more work for you.", @@ -5537,7 +5537,7 @@ if not Quests then }, [5] = { name = "Gnomes: Uncharted Territory", - storageId = Storage.DangerousDepths.Gnomes.Charting, + storageId = Storage.Quest.U11_50.DangerousDepths.Gnomes.Charting, missionId = 10384, startValue = 1, endValue = 2, @@ -5546,12 +5546,12 @@ if not Quests then return ("Chart the area around the deep base for Gnomus. Look for especific landmarks: \z \n\nOld Gate: %d/1\nThe Gaze: %d/1\nLost Ruin: %d/1\nOutpost: %d/1\nBastion: %d/1\nBroken Tower: \z %d/1"):format( - math.max(player:getStorageValue(Storage.DangerousDepths.Gnomes.OldGate), 0), - math.max(player:getStorageValue(Storage.DangerousDepths.Gnomes.TheGaze), 0), - math.max(player:getStorageValue(Storage.DangerousDepths.Gnomes.LostRuin), 0), - math.max(player:getStorageValue(Storage.DangerousDepths.Gnomes.Outpost), 0), - math.max(player:getStorageValue(Storage.DangerousDepths.Gnomes.Bastion), 0), - math.max(player:getStorageValue(Storage.DangerousDepths.Gnomes.BrokenTower), 0) + math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.OldGate), 0), + math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.TheGaze), 0), + math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LostRuin), 0), + math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Outpost), 0), + math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Bastion), 0), + math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.BrokenTower), 0) ) end, [2] = "You helped Gnomus chart the area around the deep base. You found traces of what \z @@ -5560,7 +5560,7 @@ if not Quests then }, [6] = { name = "Scouts: Explosive Growth", - storageId = Storage.DangerousDepths.Scouts.Growth, + storageId = Storage.Quest.U11_50.DangerousDepths.Scouts.Growth, missionId = 10385, startValue = 1, endValue = 2, @@ -5568,11 +5568,11 @@ if not Quests then [1] = function(player) return ("You found the mine shaft. Burn all the growth and report back to Lardoc Bashsmite! \z \n\nFirst Room: %d/1\nSecond room: %d/1\nThird room: %d/1\nFourth room: %d/1\nFifth room: %d/1"):format( - math.max(player:getStorageValue(Storage.DangerousDepths.Scouts.FirstBarrel), 0), - math.max(player:getStorageValue(Storage.DangerousDepths.Scouts.SecondBarrel), 0), - math.max(player:getStorageValue(Storage.DangerousDepths.Scouts.ThirdBarrel), 0), - math.max(player:getStorageValue(Storage.DangerousDepths.Scouts.FourthBarrel), 0), - math.max(player:getStorageValue(Storage.DangerousDepths.Scouts.FifthBarrel), 0) + math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.FirstBarrel), 0), + math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.SecondBarrel), 0), + math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.ThirdBarrel), 0), + math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.FourthBarrel), 0), + math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.FifthBarrel), 0) ) end, [2] = "You helped Lardoc Bashsmite fighting back the verminous growth in the northern mineshaft. \z @@ -5581,7 +5581,7 @@ if not Quests then }, [7] = { name = "Scouts: Pesticide", - storageId = Storage.DangerousDepths.Scouts.Diremaw, + storageId = Storage.Quest.U11_50.DangerousDepths.Scouts.Diremaw, missionId = 10386, startValue = 1, endValue = 2, @@ -5589,7 +5589,7 @@ if not Quests then [1] = function(player) return ("Lardoc asked you to follow a plan of the gnomes to stop the deep threat by trying to \z neutralise diremaw spawn with pesticies. Diremaws lay eggs inside corpses of their skin. \z - \n\nNeutralised: %d/20"):format(math.max(player:getStorageValue(Storage.DangerousDepths.Scouts.DiremawsCount), 0)) + \n\nNeutralised: %d/20"):format(math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.DiremawsCount), 0)) end, [2] = "You reported back to Lardoc Bashsmite to inform him that the gnome's plan to \z neutralise diremaw corpses seems to work.", @@ -6533,5 +6533,105 @@ if not Quests then }, }, }, + [49] = { + name = "The Secret Library", + startStorageId = Storage.Quest.U11_80.TheSecretLibrary.Questlog, + startStorageValue = 1, + missions = { + [1] = { + name = "High and Dry", + storageId = Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Questline, + missionId = 10451, + startValue = 1, + endValue = 4, + states = { + [1] = "Dedoras asked for you to talk to Charles, he knows about a small island.", + [2] = "You got stucked on this island. You must discover how to leave.", + [3] = "You successfully built a raft and used the starts to navigate your way through the waves. At least you found something of interest on the deserted island.", + [4] = "Congratulations you completed this mission.", + }, + }, + [2] = { + name = "Liquid Death", + storageId = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Questline, + missionId = 10452, + startValue = 1, + endValue = 8, + states = { + [1] = "Dedoras has sent you to Spectulus in the Edron Academy. Rumors of strange fishmen in the Tiquanda region may be connected to some of the research the scholar conducted.", + [2] = "Spectulus asked you to check out the rumors of strange fishmen in the jungles of northern Tiquanda. A small sunken temple is said to be overrun by strange fish-like creatures.", + [3] = "You found a sunken temple deep in the Tiquanda jungle. Spectulus will help you enter the vortex at the entrance. Find out what happened there and take care of the fish-like aggressors...", + [4] = "Part IV", + [5] = "Part V", + [6] = "Part VI", + [7] = "Brokul has been defeated and now you can talk with Spectulus or Dedoras what you found about the fishmen.", + [8] = "Congratulations you completed this mission.", + }, + }, + [3] = { + name = "Asuri - The Lament", + storageId = Storage.Quest.U11_80.TheSecretLibrary.Asuras.Questline, + missionId = 10453, + startValue = 1, + endValue = 7, + states = { + [1] = "Go to Asuri Palace in Port Hope.", + [2] = "You were able to assemble an instrument with the objects found. That should work for something.", + [3] = "You must talk with the dead girl's mother, Gail, at Port Hope.", + [4] = "With a heavy heart you used a piece of ebony as well as the skull and hair of an unfortunate maiden to create a bone fiddle. By playing the Peacock Ballad you should be able to open the viollet portal.", + [5] = "You may enter the viollet portal. Check if there is something useful there.", + [6] = "In a secret room of the Asura Palace you discovered an old writing desk that contains an ancient map. This must be a hint to the secret library you are searching for.", + [7] = "Congratulations you completed this mission.", + }, + }, + [4] = { + name = "The Order of the Falcon", + storageId = Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.Questline, + missionId = 10454, + startValue = 1, + endValue = 3, + states = { + [1] = "It is said that an ancient order of knights once resided in Edron. The knights disappeared a long time ago but their abandoned outpost is still there. The place may yet hold some surprises.", + [2] = "You defeated Grand Master Oberon in mortal combat. The Falcon Order may have lost its glory a long time ago but you are just getting started.", + [3] = "Congratulations you completed this mission.", + }, + }, + [5] = { + name = "The Path of Defiances", + storageId = Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline, + missionId = 10455, + startValue = 1, + endValue = 9, + states = { + [1] = "Go to Darashia and find out about the mysterious statue.", + [2] = "After you have been swallowed by a sand vortex you have entered a hidden desert area. A voice of unknown origin has informed you that you will have to face some challenges.", + [3] = "Part III", + [4] = "Part IV", + [5] = "Part V", + [6] = "Part VI", + [7] = "Part VII", + [8] = "You have found a book page in a chest. It seems to contain very useful information, but you are not capable to read it. At this point you have to speak with an expert.", + [9] = "Congratulations you completed this mission.", + }, + }, + [6] = { + name = "Bursting at the Seams", + storageId = Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline, + missionId = 10456, + startValue = 1, + endValue = 8, + states = { + [1] = "Dedoras told you where to search for leads to the Veiled Hoard: the museum in Thais, a fishmen temple in Tiquanda, an order fortress in Edron, the Asuri Palace and a deserted isle.", + [2] = "You talked with Gareth and now you may pass trougth the door.", + [3] = "Part III", + [4] = "Part IV", + [5] = "Part V", + [6] = "You successfully entered the Veiled Hoard of Zathroth. However, countless secret and vast knowledge still lie ahead.", + [7] = "You have reported Gareth about the enigmatic inscription you have found in the chamber of doom. He was able to decipher it for you and referred to Dedoras in Cormaya for further assistance.", + [8] = "Congratulations you completed this mission.", + }, + }, + }, + }, } end diff --git a/data-otservbr-global/lib/core/storages.lua b/data-otservbr-global/lib/core/storages.lua index 1c4033b09bc..4529c64f583 100644 --- a/data-otservbr-global/lib/core/storages.lua +++ b/data-otservbr-global/lib/core/storages.lua @@ -187,39 +187,6 @@ Storage = { AccessDoor = 50043, CatacombDoors = 55047, }, - TheSecretLibrary = { - -- Reserved storage from 50050 - 50074 - TheOrderOfTheFalcon = { - OberonTimer = 50050, - }, - LiquidDeath = 50051, - Mota = 50052, - MotaDoor = 50053, - BasinDoor = 50054, - SkullDoor = 50055, - TheLament = 50056, - GreenTel = 50057, - BlueTel = 50058, - BlackTel = 50059, - PinkTel = 50060, - Peacock = 50061, - HighDry = 50062, - FalconBastionAccess = 50063, - FalconBastionChestsTimer = { - Key0909 = 50064, - }, - MiniBosses = { - GrandCommanderSoeren = 50065, - PreceptorLazare = 50066, - GrandChaplainGaunder = 50067, - GrandCanonDominus = 50068, - DazedLeafGolem = 50069, - }, - LowerBastionAccess = 50070, - UndergroundBastionAccess = 50071, - OberonAccess = 50072, - ShortcutToBastion = 50073, - }, DeeplingBosses = { -- Reserved storage from 50075 - 50079 Jaul = 50075, @@ -227,193 +194,6 @@ Storage = { Obujos = 50077, DeeplingStatus = 50078, }, - DangerousDepths = { - -- Reserved storage from 50080 - 50199 - Questline = 50080, - Dwarves = { - Status = 50081, - Home = 50082, -- Mission - Subterranean = 50083, -- Mission - LostExiles = 50084, - Prisoners = 50085, - Organisms = 50086, - TimeTaskHome = 50087, - TimeTaskSubterranean = 50088, - }, - Scouts = { - Status = 50090, - Diremaw = 50091, -- Mission - Growth = 50092, -- Mission - DiremawsCount = 50093, - GnomishChest = 50094, - BarrelCount = 50095, - FirstBarrel = 50096, - SecondBarrel = 50097, - ThirdBarrel = 50098, - FourthBarrel = 50099, - FifthBarrel = 50100, - TimeTaskDiremaws = 50101, - TimeTaskGrowth = 50102, - Barrel = 50103, - BarrelTimer = 50104, - }, - Gnomes = { - Status = 50115, - Ordnance = 50116, -- Mission - Measurements = 50117, -- Mission - Charting = 50118, -- Mission - GnomeChartChest = 50119, -- Measurements - GnomeChartPaper = 50120, -- Charting - GnomesCount = 50121, -- Ordnance - CrawlersCount = 50122, -- Ordnance - LocationA = 50123, -- Measurements - LocationB = 50124, -- Measurements - LocationC = 50125, -- Measurements - LocationD = 50126, -- Measurements - LocationE = 50127, -- Measurements - LocationCount = 50128, -- Measurements - OldGate = 50129, -- Charting - TheGaze = 50130, -- Charting - LostRuin = 50131, -- Charting - Outpost = 50132, -- Charting - Bastion = 50133, - -- Charting - BrokenTower = 50134, -- Charting - ChartingCount = 50135, -- Contador - TimeTaskOrdnance = 50136, - TimeTaskMeasurements = 50137, - TimeTaskCharting = 50138, - }, - Access = { - LavaPumpWarzoneVI = 50139, - TimerWarzoneVI = 50140, - LavaPumpWarzoneV = 50141, - TimerWarzoneV = 50142, - LavaPumpWarzoneIV = 50143, - TimerWarzoneIV = 50144, - }, - Crystals = { - WarzoneVI = { - BigCrystal1 = 50155, - BigCrystal2 = 50156, - MediumCrystal1 = 50157, - MediumCrystal2 = 50158, - SmallCrystal1 = 50159, - SmallCrystal2 = 50160, - }, - WarzoneV = { - BigCrystal1 = 50165, - BigCrystal2 = 50166, - MediumCrystal1 = 50167, - MediumCrystal2 = 50168, - SmallCrystal1 = 50169, - SmallCrystal2 = 50170, - }, - WarzoneIV = { - BigCrystal1 = 50175, - BigCrystal2 = 50176, - MediumCrystal1 = 50177, - MediumCrystal2 = 50178, - SmallCrystal1 = 50179, - SmallCrystal2 = 50180, - }, - }, - Bosses = { - TheCountOfTheCore = 50185, - TheDukeOfTheDepths = 50186, - TheBaronFromBelow = 50187, - TheCountOfTheCoreAchiev = 50188, - TheDukeOfTheDepthsAchiev = 50189, - TheBaronFromBelowAchiev = 50190, - LastAchievement = 50191, - }, - }, - CultsOfTibia = { - -- Reserved storage from 50200 - 50269 - Questline = 50200, - Minotaurs = { - EntranceAccessDoor = 50201, - JamesfrancisTask = 50202, - Mission = 50203, - BossTimer = 50204, - AccessDoor = 50205, - }, - MotA = { - Mission = 50210, - Stone1 = 50211, - Stone2 = 50212, - Stone3 = 50213, - Answer = 50214, - QuestionId = 50215, - AccessDoorInvestigation = 50216, - AccessDoorGareth = 50217, - AccessDoorDenominator = 50218, - }, - Barkless = { - Mission = 50225, - sulphur = 50226, - Tar = 50227, - Ice = 50228, - Death = 50229, - Objects = 50230, - Temp = 50231, - BossTimer = 50232, - TrialAccessDoor = 50243, -- 50233 is used by an ore wagon - TarAccessDoor = 50234, - AccessDoor = 50235, - BossAccessDoor = 50236, - }, - Orcs = { - Mission = 50240, - LookType = 50241, - BossTimer = 50242, - }, - Life = { - Mission = 50245, - BossTimer = 50246, - AccessDoor = 50264, - }, - Humans = { - Mission = 50250, - Vaporized = 50251, - Decaying = 50252, - BossTimer = 50253, - }, - Misguided = { - Mission = 50255, - Monsters = 50256, - Exorcisms = 50257, - Time = 50258, - BossTimer = 50259, - AccessDoor = 50260, - }, - FinalBoss = { - Mission = 50261, - BossTimer = 50262, - AccessDoor = 50263, - }, - }, - FirstDragon = { - -- Reserved storage from 50350 - 50379 - Questline = 50350, - DragonCounter = 50351, - ChestCounter = 50352, - SecretsCounter = 50355, - GelidrazahAccess = 50356, - DesertTile = 50358, - StoneSculptureTile = 50359, - SuntowerTile = 50360, - Horn = 50362, - Scale = 50363, - Bones = 50364, - Tooth = 50365, - AccessCave = 50366, - SomewhatBeatable = 50367, - FirstDragonTimer = 50368, - RewardFeather = 50369, - RewardMask = 50370, - RewardBackpack = 50371, - }, HeroRathleton = { -- Reserved storage from 50400 - 50419 QuestLine = 50400, @@ -424,41 +204,6 @@ Storage = { AccessTeleport2 = 50405, AccessTeleport3 = 50406, }, - ForgottenKnowledge = { - -- Reserved storage from 50470 - 50519 - AccessDeath = 50470, - AccessViolet = 50471, - AccessEarth = 50472, - AccessFire = 50473, - AccessIce = 50474, - AccessGolden = 50475, - AccessLast = 50476, - OldDesk = 50477, - GirlPicture = 50478, - SilverKey = 50479, - Phial = 50480, - BirdCounter = 50481, - PlantCounter = 50482, - GoldenServantCounter = 50483, - DiamondServantCounter = 50484, - AccessPortals = 50485, - AccessMachine = 50486, - LadyTenebrisKilled = 50488, - LloydKilled = 50490, - ThornKnightKilled = 50492, - DragonkingKilled = 50494, - HorrorKilled = 50496, - TimeGuardianKilled = 50498, - LastLoreKilled = 50501, - BirdCage = 50502, - AccessLavaTeleport = 50503, - Ivalisse = 50504, - Chalice = 50505, - Tomes = 50506, - BabyDragon = 50507, - SpiderWeb = 50508, - LloydEvent = 50509, - }, SweetyCyclops = { -- Reserved storage from 50520 - 50529 AmuletTimer = 50520, @@ -2522,12 +2267,140 @@ Storage = { HeartOfDestruction = {}, }, U11_02 = { -- update 11.02 - Reserved Storages 45451 - 45650 - FestiveOutfits = {}, - FirstDragon = {}, - ForgottenKnowledge = {}, + FestiveOutfits = { + Addon1 = 45451, + Addon2 = 45452, + }, + TheFirstDragon = { + Questline = 45453, + DragonCounter = 45454, + ChestCounter = 45455, + SecretsCounter = 45456, + GelidrazahAccess = 45457, + DesertTile = 45458, + StoneSculptureTile = 45459, + SuntowerTile = 45460, + Horn = 45461, + Scale = 45462, + Bones = 45463, + Tooth = 45464, + AccessCave = 45465, + SomewhatBeatable = 45466, + FirstDragonTimer = 45467, + RewardFeather = 45468, + RewardMask = 45469, + RewardBackpack = 45470, + Feathers = 45471, + }, + ForgottenKnowledge = { + AccessDeath = 45472, + AccessViolet = 45473, + AccessEarth = 45474, + AccessFire = 45475, + AccessIce = 45476, + AccessGolden = 45477, + AccessLast = 45478, + OldDesk = 45479, + GirlPicture = 45480, + SilverKey = 45481, + Phial = 45482, + BirdCounter = 45483, + PlantCounter = 45484, + GoldenServantCounter = 45485, + DiamondServantCounter = 45486, + AccessPortals = 45487, + AccessMachine = 45488, + LadyTenebrisKilled = 45489, + LloydKilled = 45490, + ThornKnightKilled = 45491, + DragonkingKilled = 45492, + HorrorKilled = 45493, + TimeGuardianKilled = 45494, + LastLoreKilled = 45495, + BirdCage = 45496, + AccessLavaTeleport = 45497, + Ivalisse = 45498, + Chalice = 45499, + Tomes = 45500, + BabyDragon = 45501, + SpiderWeb = 45502, + LloydEvent = 45503, + ActiveTree = 45504, + MechanismGolden = 45505, + MechanismDiamond = 45506, + GoldenServant = 45507, + DiamondServant = 45508, + AstralPowerCounter = 45509, + AstralGlyph = 45510, + }, }, U11_40 = { -- update 11.40 - Reserved Storages 45651 - 45850 - CultsOfTibia = {}, -- 45651 - 45750 + CultsOfTibia = { -- 45651 - 45750 + Questline = 45651, + Minotaurs = { + BossAccessDoor = 45652, + JamesfrancisTask = 45653, + Mission = 45654, + BossTimer = 45655, + AccessDoor = 45656, + }, + MotA = { + Mission = 45657, + Stone1 = 45658, + Stone2 = 45659, + Stone3 = 45660, + Answer = 45661, + QuestionId = 45662, + AccessDoorInvestigation = 45663, + AccessDoorGareth = 45664, + AccessDoorDenominator = 45665, + }, + Barkless = { + Mission = 45666, + Sulphur = 45667, + Tar = 45668, + Ice = 45669, + Death = 45670, + Objects = 45671, + Temp = 45672, + BossTimer = 45673, + TrialAccessDoor = 45674, + TarAccessDoor = 45675, + AccessDoor = 45676, + BossAccessDoor = 45677, + SulphurAccessDoor = 45678, + }, + Orcs = { + Mission = 45679, + LookType = 45680, + BossTimer = 45681, + }, + Life = { + Mission = 45682, + BossTimer = 45683, + AccessDoor = 45684, + }, + Humans = { + Mission = 45685, + Vaporized = 45686, + Decaying = 45687, + BossTimer = 45688, + WallDecaying = 45689, + }, + Misguided = { + Mission = 45690, + Monsters = 45691, + Exorcisms = 45692, + Time = 45693, + BossTimer = 45694, + AccessDoor = 45695, + }, + FinalBoss = { + Mission = 45696, + BossTimer = 45697, + AccessDoor = 45698, + }, + }, ThreatenedDreams = { -- 45751 - 45850 QuestLine = 45751, Mission01 = { @@ -2589,7 +2462,110 @@ Storage = { }, }, U11_50 = { -- update 11.50 - Reserved Storages 45851 - 46000 - DangerousDepths = {}, + DangerousDepths = { + Questline = 45851, + Dwarves = { + Points = 45852, + Home = 45853, + Subterranean = 45854, + LostExiles = 45855, + Prisoners = 45856, + Organisms = 45857, + TimeTaskHome = 45858, + TimeTaskSubterranean = 45859, + }, + Scouts = { + Points = 45860, + Diremaw = 45861, + Growth = 45862, + DiremawsCount = 45863, + GnomishChest = 45864, + BarrelCount = 45865, + FirstBarrel = 45866, + SecondBarrel = 45867, + ThirdBarrel = 45868, + FourthBarrel = 45869, + FifthBarrel = 45870, + TimeTaskDiremaws = 45871, + TimeTaskGrowth = 45872, + Barrel = 45873, + BarrelTimer = 45874, + }, + Gnomes = { + Points = 45875, + Ordnance = 45876, + Measurements = 45877, + Charting = 45878, + GnomeChartChest = 45879, + GnomeChartPaper = 45880, + GnomesCount = 45881, + CrawlersCount = 45882, + LocationA = 45883, + LocationB = 45884, + LocationC = 45885, + LocationD = 45886, + LocationE = 45887, + LocationCount = 45888, + OldGate = 45889, + TheGaze = 45890, + LostRuin = 45891, + Outpost = 45892, + Bastion = 45893, + BrokenTower = 45894, + ChartingCount = 45895, + TimeTaskOrdnance = 45896, + TimeTaskMeasurements = 45897, + TimeTaskCharting = 45898, + }, + Access = { + LavaPumpWarzoneVI = 45899, + TimerWarzoneVI = 45900, + LavaPumpWarzoneV = 45901, + TimerWarzoneV = 45902, + LavaPumpWarzoneIV = 45903, + TimerWarzoneIV = 45904, + }, + Crystals = { + WarzoneVI = { + BigCrystal1 = 45905, + BigCrystal2 = 45906, + MediumCrystal1 = 45907, + MediumCrystal2 = 45908, + SmallCrystal1 = 45909, + SmallCrystal2 = 45910, + }, + WarzoneV = { + BigCrystal1 = 45911, + BigCrystal2 = 45912, + MediumCrystal1 = 45913, + MediumCrystal2 = 45914, + SmallCrystal1 = 45915, + SmallCrystal2 = 45916, + }, + WarzoneIV = { + BigCrystal1 = 45917, + BigCrystal2 = 45918, + MediumCrystal1 = 45919, + MediumCrystal2 = 45920, + SmallCrystal1 = 45921, + SmallCrystal2 = 45922, + }, + }, + Bosses = { + TheCountOfTheCore = 45923, + TheDukeOfTheDepths = 45924, + TheBaronFromBelow = 45925, + TheCountOfTheCoreAchiev = 45926, + TheDukeOfTheDepthsAchiev = 45927, + TheBaronFromBelowAchiev = 45928, + LastAchievement = 45929, + }, + Geodes = { + WarzoneVI = 45930, + WarzoneV = 45931, + WarzoneIV = 45932, + }, + }, HiddenThreats = { QuestLine = 45971, RatterDoor = 45972, @@ -2612,33 +2588,128 @@ Storage = { CorymRescued08 = 45987, CorymRescueMission = 45988, }, - MakeshiftWarriorOutfits = {}, }, U11_80 = { -- update 11.80 - Reserved Storages 46001 - 46300 - BattleMageOutfits = {}, - DiscovererOutfits = {}, - MeasuringTibia = {}, TheSecretLibrary = { - FalconBastionChestsTimer = { - Coast = 46281, - Island = 46282, - ThroneHall = 46283, - Shortcut = 46284, - LowerBastion = 46285, - UndergroundBastion = 46286, - }, - GhuloshTimer = 46015, - GhuloshKilled = 46016, - GorzindelTimer = 46017, - GorzindelKilled = 46018, - LokathmorTimer = 46019, - LokathmorKilled = 46020, - MazzinorTimer = 46021, - MazzinorKilled = 46022, - ScourgeOfOblivionDoor = 46023, - ScourgeOfOblivionTimer = 46024, - ScourgeOfOblivionKilled = 46025, + Questlog = 46001, + Asuras = { + Questline = 46002, + FlammingOrchid = 46003, + SkeletonNotes = 46004, + StrandHair = 46005, + LotusKey = 46006, + EyeKey = 46007, + ScribbledNotes = 46008, + EbonyPiece = 46009, + PeacockBallad = 46010, + BlackSkull = 46011, + SilverChimes = 46012, + DiamondTimer = 46013, + BlazingTimer = 46014, + DarkTimer = 46015, + Fragrance = 46016, + }, + FalconBastion = { + Questline = 46017, + KillingBosses = 46018, + OberonTimer = 46019, + OberonHeal = 46020, + LastBossMsgInterval = 46021, + OberonSay = 46022, + ChestsTimer = { + Coast = 46023, + Island = 46024, + ThroneHall = 46025, + Shortcut = 46026, + LowerBastion = 46027, + UndergroundBastion = 46028, + Key0909 = 46029, + }, + }, + Darashia = { + Questline = 46030, + PuzzleSqm = 46031, + FirstChest = 46032, + SecondChest = 46033, + ThirdChest = 46034, + FourthChest = 46035, + RedColor = 46036, + GreenColor = 46037, + BlueColor = 46038, + ColorPuzzle = 46039, + EatenFood = 46040, + FirstTotem = 46041, + SecondTotem = 46042, + ThirdTotem = 46043, + FourthTotem = 46044, + FifthChest = 46045, + ScorpionTimer = 46046, + }, + LiquidDeath = { + Questline = 46047, + Statue1 = 46048, + Statue2 = 46049, + Statue3 = 46050, + Statue4 = 46051, + Statue5 = 46052, + Statue6 = 46053, + Statue7 = 46054, + Statue8 = 46055, + Statue9 = 46056, + StatueCount = 46057, + BrokulTimer = 46058, + BrokulTimerGlobal = 46059, + }, + MoTA = { + Questline = 46060, + LeverPermission = 46061, + SampleBlood = 46062, + BonyRod = 46063, + BrokenCompass = 46064, + Crystal1 = 46065, + Crystal2 = 46066, + Crystal3 = 46067, + Crystal4 = 46068, + Crystal5 = 46069, + Crystal6 = 46070, + Crystal7 = 46071, + Crystal8 = 46072, + YellowGem = 46073, + GreenGem = 46074, + RedGem = 46075, + FinalBasin = 46076, + SkullSample = 46077, + TakenRod = 46078, + TrialTimer = 46079, + MuseumTimer = 46080, + }, + SmallIslands = { + Questline = 46081, + Parchment = 46082, + Sapphire = 46083, + Shovel = 46084, + Fishing = 46085, + RaxiasTimer = 46086, + BoatStages = 46087, + Hawser = 46088, + Turtle = 46089, + }, + LibraryPermission = 46090, + Library = { + Questline = 46091, + MazzinorTimer = 46092, + LokathmorTimer = 46093, + GhuloshTimer = 46094, + GorzindelTimer = 46095, + Ghulosh = 46096, + }, + }, + BattleMageOutfits = { + Addon1 = 46097, + Addon2 = 46098, }, + DiscovererOutfits = {}, + MeasuringTibia = {}, }, U12_00 = { -- update 12.00 - Reserved Storages 46301 - 46600 DreamWarriorOutfits = {}, @@ -2811,20 +2882,11 @@ Storage = { } GlobalStorage = { - DangerousDepths = { - -- Reserved storage from 60001 - 60009 - Geodes = { - WarzoneVI = 60001, - WarzoneV = 60002, - WarzoneIV = 60003, - }, - }, Feroxa = { -- Reserved storage from 60020 - 60029 Chance = 60020, Active = 60021, }, - HeroRathleton = { -- Reserved storage from 60070 - 60089 FirstMachines = 60070, @@ -2842,16 +2904,6 @@ GlobalStorage = { LavaChange = 60082, FastWay = 24867, }, - ForgottenKnowledge = { - -- Reserved storage from 60090 - 60099 - ActiveTree = 60090, - MechanismGolden = 60091, - MechanismDiamond = 60092, - GoldenServant = 60093, - DiamondServant = 60094, - AstralPowerCounter = 60095, - AstralGlyph = 60096, - }, TheOrderOfTheLion = { -- Reserved storage from 60170 - 60171 Drume = { diff --git a/data-otservbr-global/monster/bosses/the_book_of_death.lua b/data-otservbr-global/monster/bosses/the_book_of_death.lua index 6cea3d844aa..4f2d3288447 100644 --- a/data-otservbr-global/monster/bosses/the_book_of_death.lua +++ b/data-otservbr-global/monster/bosses/the_book_of_death.lua @@ -20,6 +20,10 @@ monster.corpse = 0 monster.speed = 39 monster.manaCost = 0 +monster.events = { + "ghuloshDeath", +} + monster.changeTarget = { interval = 5000, chance = 8, diff --git a/data-otservbr-global/monster/bosses/the_flaming_orchid.lua b/data-otservbr-global/monster/bosses/the_flaming_orchid.lua new file mode 100644 index 00000000000..4ab6508475e --- /dev/null +++ b/data-otservbr-global/monster/bosses/the_flaming_orchid.lua @@ -0,0 +1,126 @@ +local mType = Game.createMonsterType("The Flaming Orchid") +local monster = {} + +monster.description = "a flaming orchid" +monster.experience = 8500 +monster.outfit = { + lookType = 150, + lookHead = 114, + lookBody = 94, + lookLegs = 78, + lookFeet = 79, + lookAddons = 1, + lookMount = 0, +} + +monster.health = 4000 +monster.maxHealth = 4000 +monster.race = "blood" +monster.corpse = 21987 -- review later +monster.speed = 210 +monster.manaCost = 0 + +monster.events = { + "killingLibrary", +} + +monster.changeTarget = { + interval = 4000, + chance = 10, +} + +monster.strategiesTarget = { + nearest = 100, +} + +monster.flags = { + summonable = false, + attackable = true, + hostile = true, + convinceable = false, + pushable = false, + rewardBoss = false, + illusionable = true, + canPushItems = true, + canPushCreatures = true, + staticAttackChance = 80, + targetDistance = 1, + runHealth = 0, + healthHidden = false, + isBlockable = false, + canWalkOnEnergy = true, + canWalkOnFire = true, + canWalkOnPoison = true, +} + +monster.light = { + level = 0, + color = 0, +} + +monster.voices = { + interval = 2000, + chance = 7, + { text = "I will end your torment. Do not run, little mortal.", yell = true }, + { text = "*SNIFF* *SNIFF* BLOOD! I CAN SMELL YOU, MORTAL!!", yell = true }, +} + +monster.loot = { + { name = "gold coin", chance = 100000, maxCount = 250 }, + { name = "platinum coin", chance = 9240, maxCount = 10 }, + { id = 3039, chance = 18200 }, -- red gem + { name = "gold ingot", chance = 29700, maxCount = 5 }, + { name = "assassin star", chance = 29700, maxCount = 13 }, + { name = "demonic essence", chance = 330 }, + { name = "great spirit potion", chance = 330 }, + { name = "soul orb", chance = 19530 }, + { name = "small amethyst", chance = 8310, maxCount = 2 }, + { name = "small amethyst", chance = 8310, maxCount = 2 }, + { name = "ultimate health potion", chance = 700, maxCount = 4 }, + { name = "golden lotus brooch", chance = 19740 }, + { name = "green gem", chance = 15780 }, + { name = "magma coat", chance = 1050 }, + { name = "moonlight rod", chance = 1050 }, + { name = "necrotic rod", chance = 1050 }, + { name = "oriental shoes", chance = 490 }, + { name = "peacock feather fan", chance = 16870 }, +} + +monster.attacks = { + { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -25 }, + { name = "combat", interval = 1000, chance = 10, type = COMBAT_DEATHDAMAGE, minDamage = -400, maxDamage = -700, range = 7, effect = CONST_ANI_DEATH, target = true }, + { name = "Ignite", interval = 2000, chance = 20, range = 7, radius = 1, target = true, shootEffect = CONST_ANI_FIRE }, + { name = "big death wave", interval = 4000, chance = 18, minDamage = 0, maxDamage = -500 }, -- review later + { name = "aggressivelavawave", interval = 5000, chance = 19, minDamage = 0, maxDamage = -200 }, -- review later + { name = "combat", interval = 6000, chance = 20, type = COMBAT_FIREDAMAGE, range = 5, radius = 7, target = true, minDamage = -100, maxDamage = -250, shootEffect = CONST_ANI_FIRE, effect = CONST_ME_FIREAREA, target = false }, +} + +monster.defenses = { + defense = 55, + armor = 55, + { name = "combat", interval = 2000, chance = 15, type = COMBAT_HEALING, minDamage = 50, maxDamage = 280, effect = CONST_ME_MAGIC_BLUE, target = false }, + { name = "speed", interval = 2000, chance = 15, speedChange = 320, duration = 5000, areaEffect = CONST_ME_MAGIC_RED }, + { name = "invisible", interval = 1000, chance = 100, duration = 10000, areaEffect = CONST_ME_MAGIC_BLUE }, +} + +monster.elements = { + { type = COMBAT_PHYSICALDAMAGE, percent = 0 }, + { type = COMBAT_ENERGYDAMAGE, percent = 0 }, + { type = COMBAT_EARTHDAMAGE, percent = 0 }, + { type = COMBAT_FIREDAMAGE, percent = 0 }, + { type = COMBAT_LIFEDRAIN, percent = 0 }, + { type = COMBAT_MANADRAIN, percent = 0 }, + { type = COMBAT_DROWNDAMAGE, percent = 0 }, + { type = COMBAT_ICEDAMAGE, percent = 0 }, + { type = COMBAT_HOLYDAMAGE, percent = 0 }, + { type = COMBAT_DEATHDAMAGE, percent = 0 }, +} + +monster.immunities = { + { type = "paralyze", condition = true }, + { type = "outfit", condition = false }, + { type = "invisible", condition = true }, + { type = "bleed", condition = false }, +} + +mType:register(monster) diff --git a/data-otservbr-global/monster/quests/cults_of_tibia/misguided_bully.lua b/data-otservbr-global/monster/quests/cults_of_tibia/misguided_bully.lua index 5cf1c3b8582..0798c14fe70 100644 --- a/data-otservbr-global/monster/quests/cults_of_tibia/misguided_bully.lua +++ b/data-otservbr-global/monster/quests/cults_of_tibia/misguided_bully.lua @@ -13,10 +13,6 @@ monster.outfit = { lookMount = 0, } -monster.events = { - "GlowingRubbishAmuletDeath", -} - monster.raceId = 1412 monster.Bestiary = { class = "Humanoid", @@ -49,6 +45,10 @@ monster.strategiesTarget = { random = 10, } +monster.events = { + "GlowingRubbishAmuletDeath", +} + monster.flags = { summonable = false, attackable = true, diff --git a/data-otservbr-global/monster/quests/cults_of_tibia/misguided_shadow.lua b/data-otservbr-global/monster/quests/cults_of_tibia/misguided_shadow.lua index 547c67f7cf1..b854cf01fe6 100644 --- a/data-otservbr-global/monster/quests/cults_of_tibia/misguided_shadow.lua +++ b/data-otservbr-global/monster/quests/cults_of_tibia/misguided_shadow.lua @@ -13,10 +13,6 @@ monster.outfit = { lookMount = 0, } -monster.events = { - "GlowingRubbishAmuletDeath", -} - monster.health = 3000 monster.maxHealth = 3000 monster.race = "blood" @@ -36,6 +32,10 @@ monster.strategiesTarget = { random = 10, } +monster.events = { + "GlowingRubbishAmuletDeath", +} + monster.flags = { summonable = false, attackable = true, diff --git a/data-otservbr-global/monster/quests/cults_of_tibia/misguided_thief.lua b/data-otservbr-global/monster/quests/cults_of_tibia/misguided_thief.lua index 68ed4699b15..a753fcaaab0 100644 --- a/data-otservbr-global/monster/quests/cults_of_tibia/misguided_thief.lua +++ b/data-otservbr-global/monster/quests/cults_of_tibia/misguided_thief.lua @@ -45,6 +45,10 @@ monster.strategiesTarget = { random = 10, } +monster.events = { + "GlowingRubbishAmuletDeath", +} + monster.flags = { summonable = false, attackable = true, diff --git a/data-otservbr-global/monster/quests/the_first_dragon/angry_plant.lua b/data-otservbr-global/monster/quests/the_first_dragon/angry_plant.lua index 7ecc32947fb..97f702b6639 100644 --- a/data-otservbr-global/monster/quests/the_first_dragon/angry_plant.lua +++ b/data-otservbr-global/monster/quests/the_first_dragon/angry_plant.lua @@ -20,6 +20,10 @@ monster.corpse = 2983 monster.speed = 55 monster.manaCost = 0 +monster.events = { + "AngryPlantDeath", +} + monster.changeTarget = { interval = 2000, chance = 5, diff --git a/data-otservbr-global/monster/quests/the_first_dragon/bosses/fallen_challenger.lua b/data-otservbr-global/monster/quests/the_first_dragon/bosses/fallen_challenger.lua index 1e050fdfd65..3ac0bb8f18f 100644 --- a/data-otservbr-global/monster/quests/the_first_dragon/bosses/fallen_challenger.lua +++ b/data-otservbr-global/monster/quests/the_first_dragon/bosses/fallen_challenger.lua @@ -20,6 +20,10 @@ monster.corpse = 775 monster.speed = 150 monster.manaCost = 0 +monster.events = { + "FallenDeath", +} + monster.changeTarget = { interval = 5000, chance = 8, diff --git a/data-otservbr-global/monster/quests/the_first_dragon/dragon_essence.lua b/data-otservbr-global/monster/quests/the_first_dragon/dragon_essence.lua index 629b187cf93..a91ecae04c5 100644 --- a/data-otservbr-global/monster/quests/the_first_dragon/dragon_essence.lua +++ b/data-otservbr-global/monster/quests/the_first_dragon/dragon_essence.lua @@ -20,6 +20,10 @@ monster.corpse = 0 monster.speed = 125 monster.manaCost = 0 +monster.events = { + "DragonEssenceDeath", +} + monster.changeTarget = { interval = 2000, chance = 5, diff --git a/data-otservbr-global/monster/quests/the_first_dragon/somewhat_beatable.lua b/data-otservbr-global/monster/quests/the_first_dragon/somewhat_beatable.lua index 8829b1415d9..9c0ace92c1f 100644 --- a/data-otservbr-global/monster/quests/the_first_dragon/somewhat_beatable.lua +++ b/data-otservbr-global/monster/quests/the_first_dragon/somewhat_beatable.lua @@ -53,7 +53,7 @@ monster.flags = { } monster.events = { - "Somewhat Beatable Death", + "SomewhatBeatableDeath", } monster.light = { diff --git a/data-otservbr-global/monster/quests/the_secret_library/bosses/brokul.lua b/data-otservbr-global/monster/quests/the_secret_library/bosses/brokul.lua index 727249e61d0..ffe52519e85 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/bosses/brokul.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/bosses/brokul.lua @@ -25,6 +25,10 @@ monster.corpse = 28635 monster.speed = 105 monster.manaCost = 0 +monster.events = { + "killingLibrary", +} + monster.changeTarget = { interval = 1000, chance = 10, diff --git a/data-otservbr-global/monster/quests/the_secret_library/bosses/ghulosh.lua b/data-otservbr-global/monster/quests/the_secret_library/bosses/ghulosh.lua index 09d4abb0935..f9d04e40db4 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/bosses/ghulosh.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/bosses/ghulosh.lua @@ -14,7 +14,7 @@ monster.outfit = { } monster.events = { - "SecretLibraryBossDeath", + "ghuloshThink", } monster.bosstiary = { diff --git a/data-otservbr-global/monster/quests/the_secret_library/bosses/gorzindel.lua b/data-otservbr-global/monster/quests/the_secret_library/bosses/gorzindel.lua index e5a1e3d6424..4d549175bab 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/bosses/gorzindel.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/bosses/gorzindel.lua @@ -14,7 +14,7 @@ monster.outfit = { } monster.events = { - "SecretLibraryBossDeath", + "gorzindelHealth", } monster.bosstiary = { diff --git a/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_canon_dominus.lua b/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_canon_dominus.lua index f6976c6d732..23390f0145f 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_canon_dominus.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_canon_dominus.lua @@ -26,7 +26,7 @@ monster.speed = 105 monster.manaCost = 0 monster.events = { - "GrandCanonDominusDeath", + "killingLibrary", } monster.changeTarget = { diff --git a/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_chaplain_gaunder.lua b/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_chaplain_gaunder.lua index a4d42046c58..57589cb7b17 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_chaplain_gaunder.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_chaplain_gaunder.lua @@ -26,7 +26,7 @@ monster.speed = 105 monster.manaCost = 0 monster.events = { - "GrandChaplainGaunderDeath", + "killingLibrary", } monster.changeTarget = { diff --git a/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_commander_soeren.lua b/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_commander_soeren.lua index e79984a5317..685b1f10f07 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_commander_soeren.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_commander_soeren.lua @@ -26,7 +26,7 @@ monster.speed = 105 monster.manaCost = 0 monster.events = { - "GrandCommanderSoerenDeath", + "killingLibrary", } monster.changeTarget = { diff --git a/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_master_oberon.lua b/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_master_oberon.lua index dc5121d128a..c57bf0815ae 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_master_oberon.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_master_oberon.lua @@ -25,6 +25,11 @@ monster.corpse = 28625 monster.speed = 115 monster.manaCost = 0 +monster.events = { + "killingLibrary", + "oberonImmune", +} + monster.changeTarget = { interval = 4000, chance = 10, diff --git a/data-otservbr-global/monster/quests/the_secret_library/bosses/preceptor_lazare.lua b/data-otservbr-global/monster/quests/the_secret_library/bosses/preceptor_lazare.lua index 64dddc6adf2..c9db8d4446c 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/bosses/preceptor_lazare.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/bosses/preceptor_lazare.lua @@ -26,7 +26,7 @@ monster.speed = 105 monster.manaCost = 0 monster.events = { - "PreceptorLazareDeath", + "killingLibrary", } monster.changeTarget = { diff --git a/data-otservbr-global/monster/quests/the_secret_library/concentrated_death.lua b/data-otservbr-global/monster/quests/the_secret_library/concentrated_death.lua index d83987622d8..fb7ce671970 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/concentrated_death.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/concentrated_death.lua @@ -20,6 +20,10 @@ monster.corpse = 28601 monster.speed = 175 monster.manaCost = 0 +monster.events = { + "ghuloshDeath", +} + monster.changeTarget = { interval = 5000, chance = 8, diff --git a/data-otservbr-global/monster/quests/the_secret_library/dazed_leaf_golem.lua b/data-otservbr-global/monster/quests/the_secret_library/dazed_leaf_golem.lua index 06961bae6cd..03c34c04c65 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/dazed_leaf_golem.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/dazed_leaf_golem.lua @@ -21,7 +21,7 @@ monster.speed = 175 monster.manaCost = 0 monster.events = { - "DazedLeafGolemDeath", + "killingLibrary", } monster.changeTarget = { diff --git a/data-otservbr-global/monster/quests/the_secret_library/lokathmor.lua b/data-otservbr-global/monster/quests/the_secret_library/lokathmor.lua index 621717cd277..699f0550d52 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/lokathmor.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/lokathmor.lua @@ -14,7 +14,7 @@ monster.outfit = { } monster.events = { - "SecretLibraryBossDeath", + "lokathmorDeath", } monster.bosstiary = { diff --git a/data-otservbr-global/monster/quests/the_secret_library/mazzinor.lua b/data-otservbr-global/monster/quests/the_secret_library/mazzinor.lua index b5521c45429..f482dfe4b9e 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/mazzinor.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/mazzinor.lua @@ -14,7 +14,8 @@ monster.outfit = { } monster.events = { - "SecretLibraryBossDeath", + "mazzinorDeath", + "mazzinorHealth", } monster.bosstiary = { diff --git a/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_armor.lua b/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_armor.lua index 1905e8a29a9..6397d663071 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_armor.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_armor.lua @@ -20,6 +20,10 @@ monster.corpse = 0 monster.speed = 75 monster.manaCost = 0 +monster.events = { + "gorzindelDeath", +} + monster.changeTarget = { interval = 5000, chance = 8, diff --git a/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_healing.lua b/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_healing.lua index ffe609dc45a..84c63db20ce 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_healing.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_healing.lua @@ -20,6 +20,10 @@ monster.corpse = 0 monster.speed = 75 monster.manaCost = 0 +monster.events = { + "gorzindelDeath", +} + monster.changeTarget = { interval = 5000, chance = 8, diff --git a/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_lifesteal.lua b/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_lifesteal.lua index ec3ccaefa97..f93849fbbde 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_lifesteal.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_lifesteal.lua @@ -20,6 +20,10 @@ monster.corpse = 0 monster.speed = 75 monster.manaCost = 0 +monster.events = { + "gorzindelDeath", +} + monster.changeTarget = { interval = 5000, chance = 8, diff --git a/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_spells.lua b/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_spells.lua index 58fff59a202..bdbe4118398 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_spells.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_spells.lua @@ -20,6 +20,10 @@ monster.corpse = 0 monster.speed = 75 monster.manaCost = 0 +monster.events = { + "gorzindelDeath", +} + monster.changeTarget = { interval = 5000, chance = 8, diff --git a/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_summoning.lua b/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_summoning.lua index 87c05596b01..ecd11fc0b46 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_summoning.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/stolen_knowledge_of_summoning.lua @@ -20,6 +20,10 @@ monster.corpse = 0 monster.speed = 75 monster.manaCost = 0 +monster.events = { + "gorzindelDeath", +} + monster.changeTarget = { interval = 5000, chance = 8, diff --git a/data-otservbr-global/monster/quests/the_secret_library/the_blazing_rose.lua b/data-otservbr-global/monster/quests/the_secret_library/the_blazing_rose.lua index 3eb95e76a26..569c69affe7 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/the_blazing_rose.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/the_blazing_rose.lua @@ -25,6 +25,10 @@ monster.corpse = 28794 monster.speed = 175 monster.manaCost = 0 +monster.events = { + "AsurasMechanic", +} + monster.changeTarget = { interval = 5000, chance = 8, diff --git a/data-otservbr-global/monster/quests/the_secret_library/the_diamond_blossom.lua b/data-otservbr-global/monster/quests/the_secret_library/the_diamond_blossom.lua index 03fc38bffc8..99dd4fd9d33 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/the_diamond_blossom.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/the_diamond_blossom.lua @@ -25,6 +25,10 @@ monster.corpse = 28802 monster.speed = 175 monster.manaCost = 0 +monster.events = { + "AsurasMechanic", +} + monster.changeTarget = { interval = 5000, chance = 8, diff --git a/data-otservbr-global/monster/quests/the_secret_library/the_lily_of_night.lua b/data-otservbr-global/monster/quests/the_secret_library/the_lily_of_night.lua index 9bf43393315..5b147dee5ed 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/the_lily_of_night.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/the_lily_of_night.lua @@ -25,6 +25,10 @@ monster.corpse = 28802 monster.speed = 175 monster.manaCost = 0 +monster.events = { + "AsurasMechanic", +} + monster.changeTarget = { interval = 5000, chance = 8, diff --git a/data-otservbr-global/npc/a_dragon_mother.lua b/data-otservbr-global/npc/a_dragon_mother.lua index ec4653828c6..cfb788c6902 100644 --- a/data-otservbr-global/npc/a_dragon_mother.lua +++ b/data-otservbr-global/npc/a_dragon_mother.lua @@ -54,16 +54,16 @@ local function greetCallback(npc, creature) local player = Player(creature) local playerId = player:getId() - if player:getStorageValue(Storage.ForgottenKnowledge.BabyDragon) < 1 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.BabyDragon) < 1 then npcHandler:setMessage(MESSAGE_GREET, "Greetings humans! Consider yourselfs lucky, I'm in need of {help}.") npcHandler:setTopic(playerId, 1) return true - elseif player:getStorageValue(Storage.ForgottenKnowledge.AccessMachine) == 1 then + elseif player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AccessMachine) == 1 then npcHandler:setMessage(MESSAGE_GREET, "Grrr.") return true - elseif player:getStorageValue(Storage.ForgottenKnowledge.HorrorKilled) >= 1 then + elseif player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.HorrorKilled) >= 1 then npcHandler:setMessage(MESSAGE_GREET, "You have done me a favour and the knowledge you are seeking shall be yours. I melted the ice for you, you can pass now.") - player:setStorageValue(Storage.ForgottenKnowledge.AccessMachine, 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AccessMachine, 1) end return true end @@ -95,7 +95,7 @@ local function creatureSayCallback(npc, creature, type, message) "So return to the upper tunnels where cultists and ice golems dwell. Somewhere in these tunnels you will find a small prison haunted by a ghost. South of this prison cell there is a tunnel that will lead you eastwards. ...", "Follow the tunnel until you reach a small cave. Step down and down until you see a blue energy field. It will lead you to my egg. It is sealed so that not everyone may enter the room. But you have the permission now.", }, npc, creature) - player:setStorageValue(Storage.ForgottenKnowledge.BabyDragon, 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.BabyDragon, 1) npcHandler:setTopic(playerId, 4) end elseif MsgContains(message, "no") then @@ -113,7 +113,7 @@ local function creatureSayCallback(npc, creature, type, message) "As I told you, fiendish ice creatures dragged my egg into the lower caves. ...", " Without enough heat the egg will die soon. Venture there and save my hatchling and the knowledge you seeek shall be yours!", }, npc, creature) - player:setStorageValue(Storage.ForgottenKnowledge.BabyDragon, 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.BabyDragon, 1) end end return true diff --git a/data-otservbr-global/npc/a_strange_chalice.lua b/data-otservbr-global/npc/a_strange_chalice.lua index 4b1c8f1d701..c0523cb09e3 100644 --- a/data-otservbr-global/npc/a_strange_chalice.lua +++ b/data-otservbr-global/npc/a_strange_chalice.lua @@ -57,10 +57,8 @@ local function creatureSayCallback(npc, creature, type, message) return false end - if MsgContains(message, "chalice") and player:getStorageValue(Storage.ForgottenKnowledge.Chalice) == 1 then - npcHandler:say({ - "Finally. That's what I... oh wait, you're still talking to me - you will blow my cover! What do you want? Oh wait, did my {daughter} send you? It has been some time now, indeed.", - }, npc, creature) + if MsgContains(message, "chalice") and player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.Chalice) == 1 then + npcHandler:say("Finally. That's what I... oh wait, you're still talking to me - you will blow my cover! What do you want? Oh wait, did my {daughter} send you? It has been some time now, indeed.", npc, creature) npcHandler:setTopic(playerId, 1) end @@ -111,11 +109,13 @@ local function creatureSayCallback(npc, creature, type, message) " I already know how to enter it, you need to step in and yell 'zzubaran'. Unfortunately they never took me with them. ...", " I heard them say it once, when a seemingly drunk guard yelled it in front of a wall mounted torch, hitting his head against it afterwards. He spilt all my contents on the floor, hmpf.", }, npc, creature) - player:setStorageValue(Storage.ForgottenKnowledge.AccessLavaTeleport, 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AccessLavaTeleport, 1) end return true end +npcHandler:setMessage(MESSAGE_GREET, "Hi? What do you mean 'hi' - can't you see I am a... I have to lower my voice. Did it occur to you, that this 'chalice' does not want to be disturbed?! Leave me alone!") + npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback) npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true) diff --git a/data-otservbr-global/npc/a_weakened_forest_fury.lua b/data-otservbr-global/npc/a_weakened_forest_fury.lua index 1d9a8a4ccdc..3bd1cdc49f2 100644 --- a/data-otservbr-global/npc/a_weakened_forest_fury.lua +++ b/data-otservbr-global/npc/a_weakened_forest_fury.lua @@ -50,13 +50,6 @@ npcType.onCloseChannel = function(npc, creature) npcHandler:onCloseChannel(npc, creature) end --- Don't forget npcHandler = npcHandler in the parameters. It is required for all StdModule functions! -keywordHandler:addKeyword({ "name" }, StdModule.say, { npcHandler = npcHandler, text = " My name is now known only to the wind and it shall remain like this until I will return to my kin." }) -keywordHandler:addKeyword({ "job" }, StdModule.say, { npcHandler = npcHandler, text = "I was a guardian of this glade. I am the last one... everyone had to leave." }) -keywordHandler:addKeyword({ "time" }, StdModule.say, { npcHandler = npcHandler, text = "This glade's time is growing short if nothing will be done soon." }) -keywordHandler:addKeyword({ "forest fury" }, StdModule.say, { npcHandler = npcHandler, text = "Take care, guardian." }) -keywordHandler:addKeyword({ "orclops" }, StdModule.say, { npcHandler = npcHandler, text = "Cruel beings. Large and monstrous, with a single eye, staring at their prey. " }) - local function creatureSayCallback(npc, creature, type, message) local player = Player(creature) local playerId = player:getId() @@ -81,59 +74,62 @@ local function creatureSayCallback(npc, creature, type, message) "Indeed, you will. Take one of these cages, which have been crafted generations ago to rob a creature of its freedom for that it may earn it again truthfully. Return the birds back to their home in the glade. ...", "You will find {phials} for water near this sacred well which will take you safely to the glade. No seeds are left, they are in the hands of the intruders now. Have faith in yourself, guardian.", }, npc, creature) - player:setStorageValue(Storage.ForgottenKnowledge.BirdCage, 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.BirdCage, 1) player:addItem(23812, 1) + npcHandler:setTopic(playerId, 0) end elseif MsgContains(message, "seeds") then if npcHandler:getTopic(playerId) == 1 then - npcHandler:say({ - "Seeds to give life to strong trees, blooming and proud. The {intruders} robbed us from them.", - }, npc, creature) + npcHandler:say("Seeds to give life to strong trees, blooming and proud. The {intruders} robbed us from them.", npc, creature) + npcHandler:setTopic(playerId, 0) end elseif MsgContains(message, "intruders") then if npcHandler:getTopic(playerId) == 1 then - npcHandler:say({ - "The intruders appeared in the blink of an eye. Out of thin air, as if they came from nowhere. They overrun the glade within ours and drove away what was remaining from us within the day.", - }, npc, creature) + npcHandler:say("The intruders appeared in the blink of an eye. Out of thin air, as if they came from nowhere. They overrun the glade within ours and drove away what was remaining from us within the day.", npc, creature) + npcHandler:setTopic(playerId, 0) end elseif MsgContains(message, "water") then if npcHandler:getTopic(playerId) == 1 then - npcHandler:say({ - "The purest water flows through this well. For centuries we concealed it, for other beings to not lay their eyes on it.", - }, npc, creature) + npcHandler:say("The purest water flows through this well. For centuries we concealed it, for other beings to not lay their eyes on it.", npc, creature) + npcHandler:setTopic(playerId, 0) end elseif MsgContains(message, "birds") then if npcHandler:getTopic(playerId) == 1 then - npcHandler:say({ - "Take care, guardian.", - }, npc, creature) + npcHandler:say("Take care, guardian.", npc, creature) + npcHandler:setTopic(playerId, 0) end elseif MsgContains(message, "phials") then if npcHandler:getTopic(playerId) == 1 then - npcHandler:say({ - "Phials for the purest water from our sacred well. They are finely crafted and very fragile. We keep a small supply up here around the well. Probably the only thing the intruders did not care for.", - }, npc, creature) + npcHandler:say("Phials for the purest water from our sacred well. They are finely crafted and very fragile. We keep a small supply up here around the well. Probably the only thing the intruders did not care for.", npc, creature) + npcHandler:setTopic(playerId, 0) end - end - if MsgContains(message, "cages") and player:getStorageValue(Storage.ForgottenKnowledge.BirdCage) == 1 then + elseif MsgContains(message, "cages") and player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.BirdCage) == 1 then npcHandler:say({ "Crafted generations ago to rob a creature of its freedom for that it may earn it again truthfully. You will need them if you plan on returning the birds to their rightful home in the glade. ... ", "Are you in need of another one? ", }, npc, creature) npcHandler:setTopic(playerId, 2) - end - if MsgContains(message, "yes") then + elseif MsgContains(message, "yes") then if npcHandler:getTopic(playerId) == 2 then - npcHandler:say({ - "I already handed a cage to you. If you are in need of another one, you will have to return to me later.", - }, npc, creature) + npcHandler:say("I already handed a cage to you. If you are in need of another one, you will have to return to me later.", npc, creature) + npcHandler:setTopic(playerId, 0) end end + return true end -npcHandler:setCallback(CALLBACK_GREET, greetCallback) +npcHandler:setMessage(MESSAGE_GREET, "I greet you, human. This is a time of distress, more than ever are we in need of guardians to protect us and our world.") + npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback) + +-- Don't forget npcHandler = npcHandler in the parameters. It is required for all StdModule functions! +keywordHandler:addKeyword({ "name" }, StdModule.say, { npcHandler = npcHandler, text = "My name is now known only to the wind and it shall remain like this until I will return to my kin." }) +keywordHandler:addKeyword({ "job" }, StdModule.say, { npcHandler = npcHandler, text = "I was a guardian of this glade. I am the last one... everyone had to leave." }) +keywordHandler:addKeyword({ "time" }, StdModule.say, { npcHandler = npcHandler, text = "This glade's time is growing short if nothing will be done soon." }) +keywordHandler:addKeyword({ "forest fury" }, StdModule.say, { npcHandler = npcHandler, text = "Take care, guardian." }) +keywordHandler:addKeyword({ "orclops" }, StdModule.say, { npcHandler = npcHandler, text = "Cruel beings. Large and monstrous, with a single eye, staring at their prey. " }) + npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true) -- npcType registering the npcConfig table diff --git a/data-otservbr-global/npc/ahmet.lua b/data-otservbr-global/npc/ahmet.lua index 1e1795f6af9..2720daea276 100644 --- a/data-otservbr-global/npc/ahmet.lua +++ b/data-otservbr-global/npc/ahmet.lua @@ -179,7 +179,7 @@ local function creatureSayCallback(npc, creature, type, message) return true end -npcHandler:setMessage(MESSAGE_GREET, "Be mourned pilgrim in flesh. I'm selling general goods.") +npcHandler:setMessage(MESSAGE_GREET, "Be mourned pilgrim in flesh. I'm selling general goods. Just ask me for a {trade}.") npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback) diff --git a/data-otservbr-global/npc/albinius.lua b/data-otservbr-global/npc/albinius.lua index ca04c785848..57106ef9df0 100644 --- a/data-otservbr-global/npc/albinius.lua +++ b/data-otservbr-global/npc/albinius.lua @@ -61,10 +61,6 @@ npcType.onThink = function(npc, interval) npcHandler:onThink(npc, interval) end -keywordHandler:addKeyword({ "name" }, StdModule.say, { npcHandler = npcHandler, text = "I am Albinius, a worshipper of the {Astral Shapers}." }) -keywordHandler:addKeyword({ "time" }, StdModule.say, { npcHandler = npcHandler, text = "Precisely time." }) -keywordHandler:addKeyword({ "job" }, StdModule.say, { npcHandler = npcHandler, text = "I find ways to unveil the secrets of the stars. Judging by this question, I doubt you follow my weekly publications concerning this research." }) - local runes = { { runeid = 24954 }, { runeid = 24955 }, @@ -97,20 +93,18 @@ local function creatureSayCallback(npc, creature, type, message) end if MsgContains(message, "temple") then - npcHandler:say({ - "The temple has been restored to its former glory, yet we strife to live and praise in the {Shaper} ways. Do you still need me to take some old {tomes} from you my child?", - }, npc, creature) + npcHandler:say("The temple has been restored to its former glory, yet we strife to live and praise in the {Shaper} ways. Do you still need me to take some old {tomes} from you my child?", npc, creature) npcHandler:setTopic(playerId, 1) end if MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 1 then - if player:getStorageValue(Storage.ForgottenKnowledge.Tomes) == 1 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.Tomes) == 1 then npcHandler:say("You already offered enough tomes for us to study and rebuild this temple. Thank you, my child.", npc, creature) npcHandler:setTopic(playerId, 0) else if player:getItemCount(23986) >= 5 then player:removeItem(23986, 5) npcHandler:say("Thank you very much for your contribution, child. Your first step in the ways of the {Shapers} has been taken.", npc, creature) - player:setStorageValue(Storage.ForgottenKnowledge.Tomes, 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.Tomes, 1) else npcHandler:say("You need 5 heavy old tome.", npc, creature) end @@ -120,10 +114,8 @@ local function creatureSayCallback(npc, creature, type, message) npcHandler:removeInteraction(npc, creature) end - if MsgContains(message, "tomes") and player:getStorageValue(Storage.ForgottenKnowledge.Tomes) < 1 then - npcHandler:say({ - "If you have some old shaper tomes I would {buy} them.", - }, npc, creature) + if MsgContains(message, "tomes") and player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.Tomes) < 1 then + npcHandler:say("If you have some old shaper tomes I would {buy} them.", npc, creature) npcHandler:setTopic(playerId, 7) end @@ -134,7 +126,7 @@ local function creatureSayCallback(npc, creature, type, message) --- ##Astral Shaper Rune## if MsgContains(message, "astral shaper rune") then - if player:getStorageValue(Storage.ForgottenKnowledge.LastLoreKilled) >= 1 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.LastLoreKilled) >= 1 then npcHandler:say("Do you wish to merge your rune parts into an astral shaper rune?", npc, creature) npcHandler:setTopic(playerId, 8) else @@ -162,21 +154,19 @@ local function creatureSayCallback(npc, creature, type, message) --- ####PORTALS### -- Ice Portal if MsgContains(message, "ice portal") then - if player:getStorageValue(Storage.ForgottenKnowledge.Tomes) == 1 then - npcHandler:say({ - "You may pass this portal if you have 50 fish as offering. Do you have the fish with you?", - }, npc, creature) + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.Tomes) == 1 and player:getStorageValue(Storage.Quest.U8_0.TheIceIslands.FormorgarMinesDoor) == 1 then + npcHandler:say("You may pass this portal if you have 50 fish as offering. Do you have the fish with you?", npc, creature) npcHandler:setTopic(playerId, 2) else - npcHandler:say("Sorry, first you need to bring my Heavy Old Tomes.", npc, creature) + npcHandler:say("Sorry, you first need to bring my Heavy Old Tomes or do the quest before continuing.", npc, creature) end end if MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 2 then - if player:getStorageValue(Storage.ForgottenKnowledge.AccessIce) < 1 and player:getItemCount(3578) >= 50 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AccessIce) < 1 and player:getItemCount(3578) >= 50 then player:removeItem(3578, 50) npcHandler:say("Thank you for your offering. You may pass the Portal to the Powers of Ice now.", npc, creature) - player:setStorageValue(Storage.ForgottenKnowledge.AccessIce, 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AccessIce, 1) else npcHandler:say("I'm sorry, you don't have enough fish. Return if you can offer fifty of them.", npc, creature) end @@ -186,10 +176,8 @@ local function creatureSayCallback(npc, creature, type, message) -- Holy Portal if MsgContains(message, "holy portal") then - if player:getStorageValue(Storage.ForgottenKnowledge.Tomes) == 1 then - npcHandler:say({ - "You may pass this portal if you have 50 incantation notes as offering. Do you have the incantation notes with you?", - }, npc, creature) + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.Tomes) == 1 then + npcHandler:say("You may pass this portal if you have 50 incantation notes as offering. Do you have the incantation notes with you?", npc, creature) npcHandler:setTopic(playerId, 3) else npcHandler:say("Sorry, first you need to bring my Heavy Old Tomes.", npc, creature) @@ -197,10 +185,10 @@ local function creatureSayCallback(npc, creature, type, message) end if MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 3 then - if player:getStorageValue(Storage.ForgottenKnowledge.AccessGolden) < 1 and player:getItemCount(18929) >= 50 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AccessGolden) < 1 and player:getItemCount(18929) >= 50 then player:removeItem(18929, 50) npcHandler:say("Thank you for your offering. You may pass the Portal to the Powers of Holy now.", npc, creature) - player:setStorageValue(Storage.ForgottenKnowledge.AccessGolden, 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AccessGolden, 1) else npcHandler:say("I'm sorry, you don't have enough incantation notes. Return if you can offer fifty of them.", npc, creature) end @@ -210,10 +198,8 @@ local function creatureSayCallback(npc, creature, type, message) -- Energy Portal if MsgContains(message, "energy portal") then - if player:getStorageValue(Storage.ForgottenKnowledge.Tomes) == 1 then - npcHandler:say({ - "You may pass this portal if you have 50 marsh stalker feathers as offering. Do you have the marsh stalker feathers with you?", - }, npc, creature) + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.Tomes) == 1 then + npcHandler:say("You may pass this portal if you have 50 marsh stalker feathers as offering. Do you have the marsh stalker feathers with you?", npc, creature) npcHandler:setTopic(playerId, 4) else npcHandler:say("Sorry, first you need to bring my Heavy Old Tomes.", npc, creature) @@ -221,10 +207,10 @@ local function creatureSayCallback(npc, creature, type, message) end if MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 4 then - if player:getStorageValue(Storage.ForgottenKnowledge.AccessViolet) < 1 and player:getItemCount(17462) >= 50 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AccessViolet) < 1 and player:getItemCount(17462) >= 50 then player:removeItem(17462, 50) npcHandler:say("Thank you for your offering. You may pass the Portal to the Powers of Energy now.", npc, creature) - player:setStorageValue(Storage.ForgottenKnowledge.AccessViolet, 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AccessViolet, 1) else npcHandler:say("I'm sorry, you don't have enough marsh stalker feathers. Return if you can offer fifty of them.", npc, creature) end @@ -234,10 +220,8 @@ local function creatureSayCallback(npc, creature, type, message) -- Earth Portal if MsgContains(message, "earth portal") then - if player:getStorageValue(Storage.ForgottenKnowledge.Tomes) == 1 then - npcHandler:say({ - "You may pass this portal if you have 50 acorns as offering. Do you have the acorns with you?", - }, npc, creature) + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.Tomes) == 1 then + npcHandler:say("You may pass this portal if you have 50 acorns as offering. Do you have the acorns with you?", npc, creature) npcHandler:setTopic(playerId, 5) else npcHandler:say("Sorry, first you need to bring my Heavy Old Tomes.", npc, creature) @@ -245,10 +229,10 @@ local function creatureSayCallback(npc, creature, type, message) end if MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 5 then - if player:getStorageValue(Storage.ForgottenKnowledge.AccessEarth) < 1 and player:getItemCount(10296) >= 50 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AccessEarth) < 1 and player:getItemCount(10296) >= 50 then player:removeItem(10296, 50) npcHandler:say("Thank you for your offering. You may pass the Portal to the Powers of Earth now.", npc, creature) - player:setStorageValue(Storage.ForgottenKnowledge.AccessEarth, 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AccessEarth, 1) else npcHandler:say("I'm sorry, you don't have enough acorns. Return if you can offer fifty of them.", npc, creature) end @@ -258,10 +242,8 @@ local function creatureSayCallback(npc, creature, type, message) -- Death Portal if MsgContains(message, "death portal") then - if player:getStorageValue(Storage.ForgottenKnowledge.Tomes) == 1 then - npcHandler:say({ - "You may pass this portal if you have 50 pelvis bones as offering. Do you have the pelvis bones with you?", - }, npc, creature) + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.Tomes) == 1 then + npcHandler:say("You may pass this portal if you have 50 pelvis bones as offering. Do you have the pelvis bones with you?", npc, creature) npcHandler:setTopic(playerId, 6) else npcHandler:say("Sorry, first you need to bring my Heavy Old Tomes.", npc, creature) @@ -269,10 +251,10 @@ local function creatureSayCallback(npc, creature, type, message) end if MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 6 then - if player:getStorageValue(Storage.ForgottenKnowledge.AccessDeath) < 1 and player:getItemCount(11481) >= 50 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AccessDeath) < 1 and player:getItemCount(11481) >= 50 then player:removeItem(11481, 50) npcHandler:say("Thank you for your offering. You may pass the Portal to the Powers of Death now.", npc, creature) - player:setStorageValue(Storage.ForgottenKnowledge.AccessDeath, 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AccessDeath, 1) else npcHandler:say("I'm sorry, you don't have enough pelvis bones. Return if you can offer fifty of them.", npc, creature) end @@ -286,6 +268,11 @@ npcHandler:setMessage(MESSAGE_GREET, "Greetings, pilgrim. Welcome to the halls o npcHandler:setMessage(MESSAGE_WALKAWAY, "Oh... farewell, child.") npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback) + +keywordHandler:addKeyword({ "name" }, StdModule.say, { npcHandler = npcHandler, text = "I am Albinius, a worshipper of the {Astral Shapers}." }) +keywordHandler:addKeyword({ "time" }, StdModule.say, { npcHandler = npcHandler, text = "Precisely time." }) +keywordHandler:addKeyword({ "job" }, StdModule.say, { npcHandler = npcHandler, text = "I find ways to unveil the secrets of the stars. Judging by this question, I doubt you follow my weekly publications concerning this research." }) + npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true) npcType:register(npcConfig) diff --git a/data-otservbr-global/npc/alkestios.lua b/data-otservbr-global/npc/alkestios.lua index 4b7e593316d..00794577cf1 100644 --- a/data-otservbr-global/npc/alkestios.lua +++ b/data-otservbr-global/npc/alkestios.lua @@ -61,13 +61,9 @@ local function creatureSayCallback(npc, creature, type, message) if MsgContains(message, "mission") then if player:getStorageValue(ThreatenedDreams.Mission01[1]) == 1 and player:getStorageValue(ThreatenedDreams.Mission01.PoacherChest) == 1 then - npcHandler:say({ - "Uhmn.. Maybe Ahmet in Ankrahmun can help we to fake this book.", - }, npc, creature) + npcHandler:say("Uhmn.. Maybe Ahmet in Ankrahmun can help we to fake this book.", npc, creature) elseif player:getStorageValue(ThreatenedDreams.Mission01[1]) == 2 then - npcHandler:say({ - "The poachers are still chasing me. Please hurry and find a way to help me.", - }, npc, creature) + npcHandler:say("The poachers are still chasing me. Please hurry and find a way to help me.", npc, creature) elseif player:getStorageValue(ThreatenedDreams.Mission01[1]) == 3 then npcHandler:say({ "You succeeded! It seems the poachers have read your little faked story about killing white deer and the ensuing doom. They stopped chasing me. Thank you! ...", @@ -80,13 +76,13 @@ local function creatureSayCallback(npc, creature, type, message) "There are fire, ice, energy and earth shrines. If you don't know their locations you can also reach them by most temples in this world. The elemental shrines will transport you to Feyrist now that you bear the magical seal.", }, npc, creature) player:setStorageValue(ThreatenedDreams.Mission01[1], 16) - else - npcHandler:say({ - "I indeed have some troubles since I'm travelling this part of the world. When I took over the body of a white deer I wasn't aware that such an animal is a sought after quarry for hunters and poachers. ...", - "Now I'm living in the constant danger of being caught and killed. Of course, I could just take over another animal but this deer has really grown on me. I'd like to help this beautiful stag but I need your assistance. Are you willing to help me?", - }, npc, creature) - npcHandler:setTopic(playerId, 1) end + elseif MsgContains(message, "help") and player:getStorageValue(ThreatenedDreams.Mission01[1]) < 1 then + npcHandler:say({ + "I indeed have some troubles since I'm travelling this part of the world. When I took over the body of a white deer I wasn't aware that such an animal is a sought after quarry for hunters and poachers. ...", + "Now I'm living in the constant danger of being caught and killed. Of course, I could just take over another animal but this deer has really grown on me. I'd like to help this beautiful stag but I need your assistance. Are you willing to help me?", + }, npc, creature) + npcHandler:setTopic(playerId, 1) elseif MsgContains(message, "yes") then if npcHandler:getTopic(playerId) == 1 then if player:getStorageValue(ThreatenedDreams.QuestLine) == 1 then @@ -131,7 +127,7 @@ keywordHandler:addKeyword({ "fae" }, StdModule.say, { }, }) -npcHandler:setMessage(MESSAGE_GREET, "Nature's blessing, traveller! |PLAYERNAME|!") +npcHandler:setMessage(MESSAGE_GREET, "Nature's blessing, traveller! May you not be affected by any sinister force.") npcHandler:setMessage(MESSAGE_FAREWELL, "May your path always be even.") npcHandler:setMessage(MESSAGE_WALKAWAY, "May your path always be even.") diff --git a/data-otservbr-global/npc/angelo.lua b/data-otservbr-global/npc/angelo.lua index ca73a5a6544..e770704a75a 100644 --- a/data-otservbr-global/npc/angelo.lua +++ b/data-otservbr-global/npc/angelo.lua @@ -52,18 +52,18 @@ end local function greetCallback(npc, creature) local playerId = creature:getId() - local player = Player(creature) - -- Se estiver na 1º missão - if player:getStorageValue(Storage.CultsOfTibia.Life.Mission) == 1 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission) == 1 then npcHandler:setMessage(MESSAGE_GREET, "The Druid of Crunor? He told you that a new cave appeared here? That's right. I'm the head of a {project} that tries to find out more about this new {area}.") - elseif player:getStorageValue(Storage.CultsOfTibia.Life.Mission) == 9 then + elseif player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission) == 9 then npcHandler:setMessage(MESSAGE_GREET, "Just get out of my way! You killed this beautiful creature. I have nothing more to say. Damn druid of Crunor!") - -- Se já tiver após a 1º missão - elseif player:getStorageValue(Storage.CultsOfTibia.Life.Mission) > 1 then + elseif player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission) > 1 then npcHandler:setMessage(MESSAGE_GREET, "How is your {mission} going?") + elseif player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission) < 1 then + npcHandler:setMessage(MESSAGE_GREET, "The Druid of Crunor? He told you that a new cave appeared here? That's right. I'm the head of a project that tries to find out more about this new area.") end + return true end local function creatureSayCallback(npc, creature, type, message) @@ -74,88 +74,79 @@ local function creatureSayCallback(npc, creature, type, message) return false end - -- Sequência para pegar a quest - if MsgContains(message, "project") then - npcHandler:say({ "The project is called 'Sandy {Cave} Project' and is funded by the {MoTA}. Its goal is the investigation of this {cave}." }, npc, creature) - npcHandler:setTopic(playerId, 2) - elseif npcHandler:getTopic(playerId) == 2 and MsgContains(message, "mota") then - npcHandler:say({ "MoTA is short for the recently founded Museum of Tibian Arts. We work together in close collaboration. New {results} are communicated to the museum instantly." }, npc, creature) - npcHandler:setTopic(playerId, 3) - elseif npcHandler:getTopic(playerId) == 3 and MsgContains(message, "results") then - npcHandler:say({ "We have no scientific results so far to reach our {goal}, because my workers aren't back yet. Should I be {worried}?" }, npc, creature) - npcHandler:setTopic(playerId, 4) - elseif npcHandler:getTopic(playerId) == 4 and MsgContains(message, "yes") then - npcHandler:say({ "Alright. I have to find out why they don't return. But I'm old and my back aches. Would you like to go there and look for my workers?" }, npc, creature) - npcHandler:setTopic(playerId, 5) - elseif npcHandler:getTopic(playerId) == 5 and MsgContains(message, "yes") then - npcHandler:say({ "Fantastic! Go there and then tell me what you've seen. I've oppened the door for you. Take care of yourself!" }, npc, creature) - player:setStorageValue(Storage.CultsOfTibia.Life.Mission, 2) - player:setStorageValue(Storage.CultsOfTibia.Life.AccessDoor, 1) - npcHandler:setTopic(playerId, 0) - - -- Inútil - elseif npcHandler:getTopic(playerId) == 2 and MsgContains(message, "cave") then - npcHandler:say({ "We don't know exactly why this cave has now exposed an entry via the {dark pyramid}. It seems that the cave already existed for a long time, however, without a connection to our world. Maybe some smaller earth movements have changed the situation." }, npc, creature) - npcHandler:setTopic(playerId, 11) - elseif npcHandler:getTopic(playerId) == 11 and MsgContains(message, "dark pyramid") then - npcHandler:say({ "We don't know yet to wich extent the cave and the dark pyramid belong together. Thisi s what we try to find out. Maybe the history of this place has to be rewritten." }, npc, creature) - npcHandler:setTopic(playerId, 0) - end - - -- Depois de encontrar o Oasis - if player:getStorageValue(Storage.CultsOfTibia.Life.Mission) == 3 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission) == 3 then if MsgContains(message, "mission") then - npcHandler:say({ "The scientists are still missing? You just found some strange green shining mummies and a big oasis? I give you this analysis tool for the water of the oasis. Maybe that's the key. Could you bring me a sample of this water?" }, npc, creature) + npcHandler:say("The scientists are still missing? You just found some strange green shining mummies and a big oasis? I give you this analysis tool for the water of the oasis. Maybe that's the key. Could you bring me a sample of this water?", npc, creature) npcHandler:setTopic(playerId, 15) elseif MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 15 then - npcHandler:say({ "Very good. Hopefully analysing this sample will get us closer to the solution of this mistery." }, npc, creature) + npcHandler:say("Very good. Hopefully analysing this sample will get us closer to the solution of this mistery.", npc, creature) player:addItem(25305, 1) - player:setStorageValue(Storage.CultsOfTibia.Life.Mission, 4) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission, 4) end end - -- Depois de usar o analyzing tool - if player:getStorageValue(Storage.CultsOfTibia.Life.Mission) == 5 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission) == 5 then if MsgContains(message, "mission") then - npcHandler:say({ "Do you have the sample I asked you for?" }, npc, creature) + npcHandler:say("Do you have the sample I asked you for?", npc, creature) npcHandler:setTopic(playerId, 16) elseif MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 16 then - npcHandler:say({ "Thanks a lot. Let me check the result. Well, I think you need the counteragent. Please apply it to the oasis!" }, npc, creature) + npcHandler:say("Thanks a lot. Let me check the result. Well, I think you need the counteragent. Please apply it to the oasis!", npc, creature) player:addItem(25304, 1) - player:setStorageValue(Storage.CultsOfTibia.Life.Mission, 6) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission, 6) end end - -- Depois de usar o conteragent - if player:getStorageValue(Storage.CultsOfTibia.Life.Mission) == 7 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission) == 7 then if MsgContains(message, "mission") then - npcHandler:say({ "What has happened? You applied the counteragent to the oasis and then it was destroyed by a sandstorm? Keep on investigating the place." }, npc, creature) + npcHandler:say("What has happened? You applied the counteragent to the oasis and then it was destroyed by a sandstorm? Keep on investigating the place.", npc, creature) npcHandler:setTopic(playerId, 17) end end - -- after killing the boss the sandking - if player:getStorageValue(Storage.CultsOfTibia.Life.Mission) == 8 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission) == 8 then npcHandler:say("Just get out of my way! You killed this beautiful creature. I have nothing more to say. Damn druid of Crunor!", npc, creature) - player:setStorageValue(Storage.CultsOfTibia.Life.Mission, 9) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission, 9) end - ----------------------------------------- MOTA ------------------------------- - -- Pedindo o Magnifier de Gareth - if player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) == 6 then + + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) == 6 then if MsgContains(message, "magnifier") then - npcHandler:say({ "{Gareth} told you that there are rumours about fake artefacts in the MoTA? And it is your task to check that with a magnifier? I see. I don't need one right now, so you can have one of mine. You find one in the crate over there." }, npc, creature) - player:setStorageValue(Storage.CultsOfTibia.MotA.Mission, 7) + npcHandler:say("{Gareth} told you that there are rumours about fake artefacts in the MoTA? And it is your task to check that with a magnifier? I see. I don't need one right now, so you can have one of mine. You find one in the crate over there.", npc, creature) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission, 7) end end - -- Pedindo a pintura de Gareth para Angelo - if player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) == 10 then - if MsgContains(message, "picture") then - npcHandler:say({ "So you found out that one artefact in the MoTA is fake? And {Gareth} sent you to me to get a new artefact as a replacement? Sorry, I hardly know you so I don't trust you. I won't help you with that!" }, npc, creature) - player:setStorageValue(Storage.CultsOfTibia.MotA.Mission, 11) + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) == 10 then + if MsgContains(message, "artefact") then + npcHandler:say("So you found out that one artefact in the MoTA is fake? And {Gareth} sent you to me to get a new artefact as a replacement? Sorry, I hardly know you so I don't trust you. I won't help you with that!", npc, creature) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission, 11) end end + if MsgContains(message, "project") then + npcHandler:say("The project is called 'Sandy {Cave} Project' and is funded by the {MoTA}. Its goal is the investigation of this {cave}.", npc, creature) + npcHandler:setTopic(playerId, 2) + elseif npcHandler:getTopic(playerId) == 2 and MsgContains(message, "mota") then + npcHandler:say("MoTA is short for the recently founded Museum of Tibian Arts. We work together in close collaboration. New {results} are communicated to the museum instantly.", npc, creature) + npcHandler:setTopic(playerId, 3) + elseif npcHandler:getTopic(playerId) == 3 and MsgContains(message, "results") then + npcHandler:say("We have no scientific results so far to reach our {goal}, because my workers aren't back yet. Should I be {worried}?", npc, creature) + npcHandler:setTopic(playerId, 4) + elseif npcHandler:getTopic(playerId) == 4 and MsgContains(message, "worried") then + npcHandler:say("Then I have to find out why they don't return. But I'm old and my back aches. Would you like to go there and look for my workers?", npc, creature) + npcHandler:setTopic(playerId, 5) + elseif npcHandler:getTopic(playerId) == 5 and MsgContains(message, "yes") then + npcHandler:say("Fantastic! Go there and then tell me what you've seen. I've opened the door for you. Take care of yourself!", npc, creature) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission, 2) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.AccessDoor, 1) + npcHandler:setTopic(playerId, 0) + elseif npcHandler:getTopic(playerId) == 2 and MsgContains(message, "cave") then + npcHandler:say("We don't know exactly why this cave has now exposed an entry via the {dark pyramid}. It seems that the cave already existed for a long time, however, without a connection to our world. Maybe some smaller earth movements have changed the situation.", npc, creature) + npcHandler:setTopic(playerId, 11) + elseif npcHandler:getTopic(playerId) == 11 and MsgContains(message, "dark pyramid") then + npcHandler:say("We don't know yet to wich extent the cave and the dark pyramid belong together. Thisi s what we try to find out. Maybe the history of this place has to be rewritten.", npc, creature) + npcHandler:setTopic(playerId, 0) + end + return true end diff --git a/data-otservbr-global/npc/buddel_helheim.lua b/data-otservbr-global/npc/buddel_helheim.lua index 88f94ccfa27..c1b526270f5 100644 --- a/data-otservbr-global/npc/buddel_helheim.lua +++ b/data-otservbr-global/npc/buddel_helheim.lua @@ -79,7 +79,7 @@ addTravelKeyword("svargrond", "You know a town nicer than this? NICER DICER! Apr end, function() return math.random(5) > 1 end, function(player) - return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) < 8 + return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) == 8 end, function(player) return player:getItemCount(3097) > 0 end, function(player) @@ -90,7 +90,7 @@ addTravelKeyword("okolnir", "It's nice there. Except of the ice dragons which ar end, function() return math.random(5) > 1 end, function(player) - return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) < 8 + return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) == 8 end, function(player) return player:getItemCount(3097) > 0 end, function(player) @@ -101,7 +101,7 @@ addTravelKeyword("tyrsung", "*HICKS* Big, big island east of here. Venorian hunt end, function() return math.random(5) > 1 end, function(player) - return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) < 8 + return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) == 8 end, function(player) return player:getItemCount(3097) > 0 end, function(player) @@ -112,7 +112,7 @@ addTravelKeyword("camp", "Both of you look like you could defend yourself! If yo end, function() return math.random(5) > 1 end, function(player) - return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) < 8 + return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) == 8 end, function(player) return player:getItemCount(3097) > 0 end, function(player) diff --git a/data-otservbr-global/npc/buddel_okolnir.lua b/data-otservbr-global/npc/buddel_okolnir.lua index f21ff049f4b..e4c9ea84f4a 100644 --- a/data-otservbr-global/npc/buddel_okolnir.lua +++ b/data-otservbr-global/npc/buddel_okolnir.lua @@ -82,7 +82,7 @@ addTravelKeyword("svargrond", "You know a town nicer than this? NICER DICER! Apr end, function() return math.random(5) > 1 end, function(player) - return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) < 8 + return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) == 8 end, function(player) return player:getItemCount(3097) > 0 end, function(player) @@ -93,7 +93,7 @@ addTravelKeyword("camp", "Both of you look like you could defend yourself! If yo end, function() return math.random(5) > 1 end, function(player) - return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) < 8 + return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) == 8 end, function(player) return player:getItemCount(3097) > 0 end, function(player) @@ -104,7 +104,7 @@ addTravelKeyword("helheim", "T'at is a small island to the east.", Position(3246 end, function() return math.random(5) > 1 end, function(player) - return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) < 8 + return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) == 8 end, function(player) return player:getItemCount(3097) > 0 end, function(player) @@ -117,7 +117,7 @@ addTravelKeyword("tyrsung", "*HICKS* Big, big island east of here. Venorian hunt end, function() return math.random(5) > 1 end, function(player) - return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) < 8 + return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) == 8 end, function(player) return player:getItemCount(3097) > 0 end, function(player) diff --git a/data-otservbr-global/npc/buddel_raider_camp.lua b/data-otservbr-global/npc/buddel_raider_camp.lua index 06b69175967..11cc062a2b6 100644 --- a/data-otservbr-global/npc/buddel_raider_camp.lua +++ b/data-otservbr-global/npc/buddel_raider_camp.lua @@ -82,7 +82,7 @@ addTravelKeyword("svargrond", "You know a town nicer than this? NICER DICER! Apr end, function() return math.random(5) > 1 end, function(player) - return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) < 8 + return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) == 8 end, function(player) return player:getItemCount(3097) > 0 end, function(player) @@ -93,7 +93,7 @@ addTravelKeyword("okolnir", "It's nice there. Except of the ice dragons which ar end, function() return math.random(5) > 1 end, function(player) - return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) < 8 + return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) == 8 end, function(player) return player:getItemCount(3097) > 0 end, function(player) @@ -104,7 +104,7 @@ addTravelKeyword("helheim", "T'at is a small island to the east.", Position(3246 end, function() return math.random(5) > 1 end, function(player) - return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) < 8 + return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) == 8 end, function(player) return player:getItemCount(3097) > 0 end, function(player) @@ -117,7 +117,7 @@ addTravelKeyword("tyrsung", "*HICKS* Big, big island east of here. Venorian hunt end, function() return math.random(5) > 1 end, function(player) - return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) < 8 + return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) == 8 end, function(player) return player:getItemCount(3097) > 0 end, function(player) diff --git a/data-otservbr-global/npc/buddel_tyrsung.lua b/data-otservbr-global/npc/buddel_tyrsung.lua index cb9025b1a91..65cbaf73066 100644 --- a/data-otservbr-global/npc/buddel_tyrsung.lua +++ b/data-otservbr-global/npc/buddel_tyrsung.lua @@ -82,7 +82,7 @@ addTravelKeyword("svargrond", "You know a town nicer than this? NICER DICER! Apr end, function() return math.random(5) > 1 end, function(player) - return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) < 8 + return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) == 8 end, function(player) return player:getItemCount(3097) > 0 end, function(player) @@ -93,7 +93,7 @@ addTravelKeyword("okolnir", "It's nice there. Except of the ice dragons which ar end, function() return math.random(5) > 1 end, function(player) - return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) < 8 + return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) == 8 end, function(player) return player:getItemCount(3097) > 0 end, function(player) @@ -104,7 +104,7 @@ addTravelKeyword("helheim", "T'at is a small island to the east.", Position(3246 end, function() return math.random(5) > 1 end, function(player) - return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) < 8 + return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) == 8 end, function(player) return player:getItemCount(3097) > 0 end, function(player) @@ -117,7 +117,7 @@ addTravelKeyword("camp", "Both of you look like you could defend yourself! If yo end, function() return math.random(5) > 1 end, function(player) - return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) < 8 + return player:getStorageValue(Storage.Quest.U8_0.BarbarianTest.Questline) == 8 end, function(player) return player:getItemCount(3097) > 0 end, function(player) diff --git a/data-otservbr-global/npc/charles.lua b/data-otservbr-global/npc/charles.lua index 9622620006a..3eb9fe2b82c 100644 --- a/data-otservbr-global/npc/charles.lua +++ b/data-otservbr-global/npc/charles.lua @@ -56,35 +56,81 @@ npcType.onCloseChannel = function(npc, creature) npcHandler:onCloseChannel(npc, creature) end --- Travel -local function addTravelKeyword(keyword, cost, destination, condition) - if condition then - keywordHandler:addKeyword({ keyword }, StdModule.say, { npcHandler = npcHandler, text = "I'm sorry but I don't sail there." }, condition) +local shortcuts = { + ["thais"] = { price = 100, position = Position(32310, 32210, 6) }, + ["edron"] = { price = 90, position = Position(33173, 31764, 6) }, + ["liberty bay"] = { price = 20, position = Position(32285, 32891, 6) }, + ["yalahar"] = { price = 200, position = Position(32816, 31272, 6) }, +} + +local isles = { + [1] = { isMission = true, position = Position(32031, 32463, 7) }, + [2] = { isMission = false, position = Position(33454, 32160, 7) }, + [3] = { isMission = false, position = Position(32112, 31745, 7) }, + [4] = { isMission = false, position = Position(32457, 32937, 7) }, +} + +local function creatureSayCallback(npc, creature, type, message) + local player = Player(creature) + local playerId = player:getId() + + if not npcHandler:checkInteraction(npc, creature) then + return false end - local travelKeyword = keywordHandler:addKeyword({ keyword }, StdModule.say, { npcHandler = npcHandler, text = "Do you seek a passage to " .. keyword:titleCase() .. " for |TRAVELCOST|?", cost = cost, discount = "postman" }) - travelKeyword:addChildKeyword({ "yes" }, StdModule.travel, { npcHandler = npcHandler, premium = false, cost = cost, discount = "postman", destination = destination }) - travelKeyword:addChildKeyword({ "no" }, StdModule.say, { npcHandler = npcHandler, text = "We would like to serve you some time.", reset = true }) + if MsgContains(message, "shortcut") and player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Questline) >= 1 then + npcHandler:say({ + "This passage is shorter and costs less gold - but on the other hand it is also a bit riskier. On this route there are frequent tempests and the severe winds may blow a passenger over board. ...", + "Few ship captains would sail this route. But if you want to take the risk, I can bring you to Thais, Edron, Liberty Bay or Yalahar for less gold than usual. Interested?", + }, npc, creature) + npcHandler:setTopic(playerId, 5) + elseif MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 5 then + npcHandler:say("Do you seek a shortcut passage to {Thais} for 100 gold, to {Edron} for 90 gold, to {Liberty Bay} for 20 gold or to {Yalahar} for 200 gold?", npc, creature) + npcHandler:setTopic(playerId, 6) + elseif npcHandler:getTopic(playerId) == 6 then + local travelTo = shortcuts[message:lower()] + if travelTo then + if player:removeMoney(travelTo.price) then + local r = math.random(1, #isles) + local chance = math.random(1, 10) + if chance <= 3 then + player:teleportTo(travelTo.position) + else + player:teleportTo(isles[r].position) + if isles[r].isMission and player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Questline) < 2 then + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Questline, 2) + end + end + npcHandler:say("Set the sails and good luck to us!", npc, creature) + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + return true + else + npcHandler:say("You don't have enough money.", npc, creature) + end + end + else + local function addTravelKeyword(keyword, cost, destination, condition) + if condition then + keywordHandler:addKeyword({ keyword }, StdModule.say, { npcHandler = npcHandler, text = "I'm sorry but I don't sail there." }, condition) + end + local travelKeyword = keywordHandler:addKeyword({ keyword }, StdModule.say, { npcHandler = npcHandler, text = "Do you seek a passage to " .. keyword:titleCase() .. " for |TRAVELCOST|?", cost = cost, discount = "postman" }) + travelKeyword:addChildKeyword({ "yes" }, StdModule.travel, { npcHandler = npcHandler, premium = false, cost = cost, discount = "postman", destination = destination }) + travelKeyword:addChildKeyword({ "no" }, StdModule.say, { npcHandler = npcHandler, text = "We would like to serve you some time.", reset = true }) + end + addTravelKeyword("edron", 150, Position(33173, 31764, 6)) + addTravelKeyword("venore", 160, Position(32954, 32022, 6)) + addTravelKeyword("yalahar", 260, Position(32816, 31272, 6), function(player) + return player:getStorageValue(Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.PortHope) ~= 1 and player:getStorageValue(Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.TownsCounter) < 5 + end) + addTravelKeyword("ankrahmun", 110, Position(33092, 32883, 6)) + addTravelKeyword("darashia", 180, Position(33289, 32480, 6)) + addTravelKeyword("thais", 160, Position(32310, 32210, 6)) + addTravelKeyword("liberty bay", 50, Position(32285, 32892, 6)) + addTravelKeyword("carlin", 120, Position(32387, 31820, 6)) + end end -addTravelKeyword("edron", 150, Position(33173, 31764, 6)) -addTravelKeyword("venore", 160, Position(32954, 32022, 6)) -addTravelKeyword("yalahar", 260, Position(32816, 31272, 6), function(player) - return player:getStorageValue(Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.PortHope) ~= 1 and player:getStorageValue(Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.TownsCounter) < 5 -end) -addTravelKeyword("ankrahmun", 110, Position(33092, 32883, 6)) -addTravelKeyword("darashia", 180, Position(33289, 32480, 6)) -addTravelKeyword("thais", 160, Position(32310, 32210, 6)) -addTravelKeyword("liberty bay", 50, Position(32285, 32892, 6)) -addTravelKeyword("carlin", 120, Position(32387, 31820, 6)) -addTravelKeyword("shortcut", 100, Position(32029, 32466, 7), function(player) - return player:getStorageValue(Storage.TheSecretLibrary.PinkTel) == 2 and player:getStorageValue(Storage.TheSecretLibrary.Mota) == 12 -end) - --- Kick keywordHandler:addKeyword({ "kick" }, StdModule.kick, { npcHandler = npcHandler, destination = { Position(32535, 32792, 6), Position(32536, 32778, 6) } }) - --- Basic keywordHandler:addKeyword({ "sail" }, StdModule.say, { npcHandler = npcHandler, text = "Where do you want to go - {Thais}, {Darashia}, {Venore}, {Liberty Bay}, {Ankrahmun}, {Yalahar} or {Edron?}" }) keywordHandler:addKeyword({ "passage" }, StdModule.say, { npcHandler = npcHandler, text = "Where do you want to go - {Thais}, {Darashia}, {Venore}, {Liberty Bay}, {Ankrahmun}, {Yalahar} or {Edron?}" }) keywordHandler:addKeyword({ "job" }, StdModule.say, { npcHandler = npcHandler, text = "Im the captain of the Poodle, the proudest ship on all oceans." }) @@ -96,6 +142,9 @@ keywordHandler:addKeyword({ "svargrond" }, StdModule.say, { npcHandler = npcHand npcHandler:setMessage(MESSAGE_GREET, "Ahoy. Where can I sail you today?") npcHandler:setMessage(MESSAGE_FAREWELL, "Bye.") npcHandler:setMessage(MESSAGE_WALKAWAY, "Bye.") + +npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback) + npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true) -- npcType registering the npcConfig table diff --git a/data-otservbr-global/npc/dedoras.lua b/data-otservbr-global/npc/dedoras.lua index 79ac24a9678..1e77480a4b9 100644 --- a/data-otservbr-global/npc/dedoras.lua +++ b/data-otservbr-global/npc/dedoras.lua @@ -50,20 +50,35 @@ npcType.onCloseChannel = function(npc, creature) npcHandler:onCloseChannel(npc, creature) end -local function greetCallback(npc, creature) - local player = Player(creature) - local playerId = player:getId() +local quests = { + [1] = { stg = Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Questline, value = 4 }, + [2] = { stg = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Questline, value = 8 }, + [3] = { stg = Storage.Quest.U11_80.TheSecretLibrary.Asuras.Questline, value = 7 }, + [4] = { stg = Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.Questline, value = 3 }, + [5] = { stg = Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline, value = 9 }, + [6] = { stg = Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline, value = 8 }, +} - if player:getStorageValue(Storage.Kilmaresh.First.Access) < 1 then - npcHandler:setMessage(MESSAGE_GREET, "How could I help you?") -- It needs to be revised, it's not the same as the global - npcHandler:setTopic(playerId, 1) - elseif (player:getStorageValue(Storage.Kilmaresh.First.JamesfrancisTask) >= 0 and player:getStorageValue(Storage.Kilmaresh.First.JamesfrancisTask) <= 50) and player:getStorageValue(Storage.Kilmaresh.First.Mission) < 3 then - npcHandler:setMessage(MESSAGE_GREET, "How could I help you?") -- It needs to be revised, it's not the same as the global - npcHandler:setTopic(playerId, 15) - elseif player:getStorageValue(Storage.Kilmaresh.First.Mission) == 4 then - npcHandler:setMessage(MESSAGE_GREET, "How could I help you?") -- It needs to be revised, it's not the same as the global - player:setStorageValue(Storage.Kilmaresh.First.Mission, 5) - npcHandler:setTopic(playerId, 20) +local function startMission(pid, storage, value) + local player = Player(pid) + if player then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Questlog) < 1 then + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Questlog, 1) + end + if player:getStorageValue(storage) < value then + player:setStorageValue(storage, value) + end + end +end + +local function isQuestDone(pid) + local player = Player(pid) + if player then + for i = 1, #quests do + if player:getStorageValue(quests[i].stg) ~= quests[i].value then + return false + end + end end return true end @@ -76,36 +91,227 @@ local function creatureSayCallback(npc, creature, type, message) return false end - if MsgContains(message, "report") and player:getStorageValue(Storage.TheSecretLibrary.PinkTel) == 1 then - npcHandler:say({ "Talk to Captain Charles in Port Hope. He told me that he once ran ashore on a small island where he discovered a small ruin. The architecture was like nothing he had seen before." }, npc, creature) - player:setStorageValue(Storage.TheSecretLibrary.PinkTel, 2) - npcHandler:setTopic(playerId, 1) - npcHandler:setTopic(playerId, 1) + local currentStorage = player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.LibraryPermission) + if currentStorage < 0 then + currentStorage = 0 end - if MsgContains(message, "check") and player:getStorageValue(Storage.TheSecretLibrary.HighDry) == 5 then + if MsgContains(message, "search") then npcHandler:say({ - "Marvellous! With this information combined we have all that's needed! ...", - "So let me see. ...", - "Hmm, interesting. And we shouldn't forget about the chant! Yes, excellent! ...", - "So listen: To enter the veiled library, travel to the white raven monastery on the Isle of Kings and enter its main altar room. ...", - "There, use an ordinary scythe on the right of the two monuments, while concentrating on this glyph here and chant the words: Chamek Athra Thull Zathroth ...", - "Oh, and one other thing. For your efforts I want to reward you with one of my old outfits, back from my adventuring days. May it suit you well! ...", - "Hurry now my friend. Time is of essence!", + "I gathered some lore on my own, but I desperately need more information that you might provide me. ...", + "My leads are the {museum} in thais, something strange in the darashian {desert}, rumors about {fishmen}, an ancient {order}, the mysterious {asuri}, or a lost {isle}?", }, npc, creature) - player:setStorageValue(Storage.TheSecretLibrary.HighDry, 6) - npcHandler:setTopic(playerId, 1) + elseif MsgContains(message, "museum") then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline) == 7 then + npcHandler:say({ + "This is ...", + "An astonishing find to say the least! I'm certain it will help the efforts of accessing the library a lot!", + }, npc, creature) + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.LibraryPermission, currentStorage + 1) + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline, 8) + elseif player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline) < 1 then + npcHandler:say("I have heard that it was recently planned to expand the Museum of Tibian Arts. In the course of these activities unexpected difficulties occurred.", npc, creature) + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline, 1) + end + elseif MsgContains(message, "desert") then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline) == 8 then + npcHandler:say("That's simply a scientific sensation. It will provide me with lots of much needed knowledge!", npc, creature) + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.LibraryPermission, currentStorage + 1) + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline, 9) + elseif player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline) < 1 then + npcHandler:say("There are rumors of a mysterious statue in the desert next to Darashia. Nobody really knows the meaning of it.", npc, creature) + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline, 1) + end + elseif MsgContains(message, "fishmen") then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Questline) == 7 then + npcHandler:say("You brought incredible news. This book proves an invaluable clue!", npc, creature) + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.LibraryPermission, currentStorage + 1) + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Questline, 8) + elseif player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Questline) < 1 then + npcHandler:say({ + "Sightings of strange fishmen in Tiquanda are stirring up the region. You should be careful when investigating this. ...", + "As far as I know a scholar in Edron already dealt with fish-like creatures before.", + }, npc, creature) + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Questline, 1) + end + elseif MsgContains(message, "order") then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.Questline) == 2 then + npcHandler:say("You brought incredible news. This book proves an invaluable clue!", npc, creature) + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.LibraryPermission, currentStorage + 1) + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.Questline, 3) + elseif player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.Questline) < 1 then + npcHandler:say({ + "Our world has seen many noble knights and orders throughout the centuries. Most of them vanished a long time ago but only few under such mysterious circumstances as the Order of the Falcon. ...", + "This noble alliance of honourable knights once resided in Edron to serve the king. Legend has it they vanished practically over night. Rumor has it their disappearance is connected to a forbidden book.", + }, npc, creature) + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.Questline, 1) + end + elseif MsgContains(message, "asuri") then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Asuras.Questline) == 6 then + npcHandler:say("This is incredible! Thank you so much for digging out that hint!", npc, creature) + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.LibraryPermission, currentStorage + 1) + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.Asuras.Questline, 7) + elseif player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Asuras.Questline) < 1 then + npcHandler:say({ + "There's a beautiful but very dangerous palace in the Tiquandan jungle. The young women who live there are actually demons and they are luring unsuspecting mortals in there. ...", + "A lucky survivor told me about a portal at the very top of the palace that may lead to another asuri hideout.", + }, npc, creature) + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.Asuras.Questline, 1) + end + elseif MsgContains(message, "isle") then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Questline) == 3 then + npcHandler:say("Thank you so much for your efforts to provide this valuable piece of the puzzle!", npc, creature) + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.LibraryPermission, currentStorage + 1) + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Questline, 4) + elseif player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Questline) < 1 then + npcHandler:say("Talk to Captain Charles in Port Hope. He told me that he once ran ashore on a small island where he discovered a small ruin. The architecture was like nothing he had seen before.", npc, creature) + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Questline, 1) + end + elseif MsgContains(message, "progress") then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.LibraryPermission) < 6 then + npcHandler:say({ + "About what of your mission s do you want to report? The {museum}, the darashian {desert}, the rumors about strange {fishmen}, the ancient {order}, the mysterious {asuri}, or the lost {isle}? ...", + "Or shall me {check} how much information we acquired?", + }, npc, creature) + end + elseif MsgContains(message, "check") then + if isQuestDone(player:getId()) and player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.LibraryPermission) == 6 then + npcHandler:say({ + "Marvellous! With this information combined we have all that's needed! ...", + "So let me see. ...", + "Hmm, interesting. And we shouldn't forget about the chant! Yes, excellent! ...", + "So listen: To enter the veiled library, travel to the white raven monastery on the Isle of Kings and enter its main altar room. ...", + "There, use an ordinary scythe on the right of the two monuments, while concentrating on this glyph here and chant the words: Chamek Athra Thull Zathroth ...", + "Oh, and one other thing. For your efforts I want to reward you with one of my old outfits, back from my adventuring days. May it suit you well! ...", + "Hurry now my friend. Time is of essence!", + }, npc, creature) + player:addOutfit(1069, 0) + player:addOutfit(1070, 0) + player:addAchievement("Battle Mage") + startMission(player:getId(), Storage.Quest.U11_80.TheSecretLibrary.LibraryPermission, 7) + npcHandler:setTopic(playerId, 0) + else + npcHandler:say("You're still searching for informations.", npc, creature) + end + end + + if MsgContains(message, "addon") and player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.LibraryPermission) == 7 then + npcHandler:say("Are you interested in one or two addons to your battle mage outfit?", npc, creature) npcHandler:setTopic(playerId, 1) + elseif MsgContains(message, "book") and npcHandler:getTopic(playerId) == 3 then + if player:getStorageValue(Storage.Quest.U11_80.BattleMageOutfits.Addon1) < 1 and player:getItemCount(28792) > 5 then + player:removeItem(28792, 5) + player:addOutfit(1069, 1) + player:addOutfit(1070, 1) + npcHandler:say("Very good! You gained the first addon to the battle mage outfit.", npc, creature) + startMission(player:getId(), Storage.Quest.U11_80.BattleMageOutfits.Addon1, 1) + npcHandler:setTopic(playerId, 0) + elseif player:getStorageValue(Storage.Quest.U11_80.BattleMageOutfits.Addon2) < 1 and player:getItemCount(28793) > 20 then + player:removeItem(28793, 20) + player:addOutfit(1069, 2) + player:addOutfit(1070, 2) + npcHandler:say("Very good! You gained the second addon to the battle mage outfit.", npc, creature) + startMission(player:getId(), Storage.Quest.U11_80.BattleMageOutfits.Addon2, 1) + npcHandler:setTopic(playerId, 0) + end + elseif MsgContains(message, "yes") then + if npcHandler:getTopic(playerId) == 1 then + npcHandler:say("I provide two addons. For the first one I need you to bring me five sturdy books. For the second addon you need twenty epaulettes. Do you want one of these addons?", npc, creature) + npcHandler:setTopic(playerId, 2) + elseif npcHandler:getTopic(playerId) == 2 then + npcHandler:say("What do you have for me: the sturdy books or the epaulettes?", npc, creature) + npcHandler:setTopic(playerId, 3) + end end return true end +keywordHandler:addKeyword({ "looking" }, StdModule.say, { npcHandler = npcHandler, text = "I need the help of some competent {adventurers} to handle a threat to all creation." }) +keywordHandler:addKeyword({ "value" }, StdModule.say, { npcHandler = npcHandler, text = "This leaves us with no choice but to take action into our own {hands}." }) +keywordHandler:addKeyword({ "threat" }, StdModule.say, { npcHandler = npcHandler, text = "I guess you know about the {background} and there is no need to tell you that the forces from beyond managed to acquire the parts of the godbreaker in a coup." }) +keywordHandler:addKeyword({ "disassembled" }, StdModule.say, { npcHandler = npcHandler, text = "The secret locations of the godbreaker {parts} were revealed and due to trickery, the minions of Variphor aquired all of them." }) +keywordHandler:addKeyword({ "obscure" }, StdModule.say, { npcHandler = npcHandler, text = "Those pieces of knowledge come in several forms and shapes. For most I can give you more or less specific hints where to start your {search}." }) +keywordHandler:addKeyword({ "hands" }, StdModule.say, { npcHandler = npcHandler, text = "You have to {find} the veiled hoard of Zathroth, breach it and destroy the knowledge how to use the godbreaker." }) +keywordHandler:addKeyword( + { "adventurer" }, + StdModule.say, + { npcHandler = npcHandler, text = { + "Of course the first to ask would be the famous Avar Tar, but I heard he's already on a quest of his own and ...", + "Well, let's say our last collaboration did not end too well. In fact, I'd be not even surprised if he pretended to not even know me. ...", + "So I have to look elsewhere to handle this new {threat}.", + } } +) +keywordHandler:addKeyword({ "background" }, StdModule.say, { + npcHandler = npcHandler, + text = { + "The goodbreaker was created in ancient times, when the war between the gods and their minions was on its height. Its creation took aeons and incredible sacrifices. ...", + "Each part had to be crafted perfectly, to emulate the gods, so it would share 'the same place' with them. ...", + "Mere mortals can not even perceive it in his whole but only recognize the part of it that is the physical representation in our world. ...", + "If it was meant to be used as an actual weapon, as the ultimate threat, or if Zathroth was just tempted to use his knowledge in the ultimate way - to create something that could undo himself - we don't know. ...", + "However in the end even Zathroth deemed it too much of a threat but instead of destroying the contraption once and for all, it was {disassembled} and hidden away.", + }, +}) +keywordHandler:addKeyword( + { "parts" }, + StdModule.say, + { npcHandler = npcHandler, text = { + "The parts alone do them no good. To assemble the parts, great skill, immense power and forbidden knowledge are necessary. ...", + "The skill will be supplied by the fallen Yalahari and the power by Variphor itself. ...", + "The only thing they are still lacking is the knowledge to assemble and operate the {godbreaker}.", + } } +) +keywordHandler:addKeyword({ "godbreaker" }, StdModule.say, { + npcHandler = npcHandler, + text = { + "The godbreaker is a complex artifact. Incantation woven into incantation. The powers bound into it are so immense that the slightest mishandling could prove disastrous. ...", + "o figure out how it works, let alone how it can be operated safely, could require several centuries of tireless study. And even then this information would be only partial. ...", + "Yet the creation and operation of the godbreaker is just the kind of forbidden {knowledge} Zathroth values most, so it was compiled and stored.", + }, +}) +keywordHandler:addKeyword({ "knowledge" }, StdModule.say, { npcHandler = npcHandler, text = { + "Of course the dangers of such knowledge were obvious. It was hidden in a sacred place devoted to Zathroth and dangerous knowledge. ...", + "The hidden library, the forbidden hoard, the shrouded trove of knowledge or the veiled hoard of forbidden knowledge, the place has many names in many {myths}.", +} }) +keywordHandler:addKeyword({ "myths" }, StdModule.say, { + npcHandler = npcHandler, + text = { + "The myths agree that the place is well hidden, extremely guarded and contains some of the most powerful pieces of knowledge in this world and probably beyond. ...", + "However the knowledge about the godbreaker now poses a threat to all existence. In the hands of Variphor it can cause disaster in previously unknown ways. The gods themselves are in {peril}.", + }, +}) +keywordHandler:addKeyword({ "peril" }, StdModule.say, { npcHandler = npcHandler, text = { + "Regardless of the dangers, the cult of Zathroth refused to destroy the knowledge of the godbreaker for good. ...", + "They {value} dangerous knowledge that much, that they are unable to part from it, even when faced with the utter destruction of creation.", +} }) +keywordHandler:addKeyword( + { "find" }, + StdModule.say, + { npcHandler = npcHandler, text = { + "I know it's asked much but it's no longer a matter of choice. ...", + "The enemy is moving and I have reports that suggest the minions of Variphor are actively searching for Zathroth's library. They must not be allowed to succeed. ...", + "We must be the first to {reach} the hoard and make sure the enemy doesn't get the information he needs.", + } } +) +keywordHandler:addKeyword({ "reach" }, StdModule.say, { npcHandler = npcHandler, text = { + "I'd recommend to follow the few leads me and my associates could gather so far. ...", + "Old myths, some {rumors} about old texts and other pieces of knowledge that I could use to figure out where to locate the hidden library and how to enter it.", +} }) +keywordHandler:addKeyword({ "rumors" }, StdModule.say, { + npcHandler = npcHandler, + text = { + "Hints about the shrouded hoard are numerous, though most are general mentions in texts that deal with Zathroth. But there are other sources. ...", + "Like texts about ancient liturgies of Zathroth and historical documents that might give us clues. I already compiled everything of value from the sources that were openly available. ...", + "To gather the more {obscure} parts of knowledge, however, I'll need your help.", + }, +}) + +npcHandler:setMessage(MESSAGE_GREET, "Greetings seekers of knowledge. You seem to be just the person I'm {looking} for.") npcHandler:setMessage(MESSAGE_WALKAWAY, "Well, bye then.") -npcHandler:setCallback(CALLBACK_SET_INTERACTION, onAddFocus) -npcHandler:setCallback(CALLBACK_REMOVE_INTERACTION, onReleaseFocus) + npcHandler:setCallback(CALLBACK_GREET, greetCallback) npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback) + npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true) -- npcType registering the npcConfig table diff --git a/data-otservbr-global/npc/denominator.lua b/data-otservbr-global/npc/denominator.lua index e41ed2c051c..ab1e1c79667 100644 --- a/data-otservbr-global/npc/denominator.lua +++ b/data-otservbr-global/npc/denominator.lua @@ -46,15 +46,15 @@ npcType.onCloseChannel = function(npc, creature) end local playerLastResp = {} + local function greetCallback(npc, creature) local playerId = creature:getId() local player = Player(creature) local playerId = player:getId() - if player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) == 13 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) == 13 then npcHandler:setMessage(MESSAGE_GREET, "Enter answers for the following {questions}:") - npcHandler:setTopic(playerId, 1) else npcHandler:setMessage(MESSAGE_GREET, "Greetings.") end @@ -63,31 +63,31 @@ end local quiz1 = { [1] = { - p = "The sum of first and second digit?", + p = "The sum of the first and second digit?", r = function(player) - player:setStorageValue(Storage.CultsOfTibia.MotA.Answer, player:getStorageValue(Storage.CultsOfTibia.MotA.Stone1) + player:getStorageValue(Storage.CultsOfTibia.MotA.Stone2)) - return player:getStorageValue(Storage.CultsOfTibia.MotA.Answer) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Answer, player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Stone1) + player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Stone2)) + return player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Answer) end, }, [2] = { - p = "The sum of second and third digit?", + p = "The sum of the second and third digit?", r = function(player) - player:setStorageValue(Storage.CultsOfTibia.MotA.Answer, player:getStorageValue(Storage.CultsOfTibia.MotA.Stone2) + player:getStorageValue(Storage.CultsOfTibia.MotA.Stone3)) - return player:getStorageValue(Storage.CultsOfTibia.MotA.Answer) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Answer, player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Stone2) + player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Stone3)) + return player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Answer) end, }, [3] = { - p = "The sum of first and third digit?", + p = "The sum of the first and third digit?", r = function(player) - player:setStorageValue(Storage.CultsOfTibia.MotA.Answer, player:getStorageValue(Storage.CultsOfTibia.MotA.Stone1) + player:getStorageValue(Storage.CultsOfTibia.MotA.Stone3)) - return player:getStorageValue(Storage.CultsOfTibia.MotA.Answer) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Answer, player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Stone1) + player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Stone3)) + return player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Answer) end, }, [4] = { - p = "The digit sum?", + p = "The total digit sum?", r = function(player) - player:setStorageValue(Storage.CultsOfTibia.MotA.Answer, player:getStorageValue(Storage.CultsOfTibia.MotA.Stone1) + player:getStorageValue(Storage.CultsOfTibia.MotA.Stone2) + player:getStorageValue(Storage.CultsOfTibia.MotA.Stone3)) - return player:getStorageValue(Storage.CultsOfTibia.MotA.Answer) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Answer, player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Stone1) + player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Stone2) + player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Stone3)) + return player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Answer) end, }, } @@ -96,7 +96,7 @@ local quiz2 = { [1] = { p = "Is the number prime?", r = function(player) - local stg = player:getStorageValue(Storage.CultsOfTibia.MotA.Answer) + local stg = player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Answer) if stg < 1 then return 0 end @@ -113,9 +113,9 @@ local quiz2 = { end, }, [2] = { - p = "Does the number belong to a prime twing?", + p = "Does the number belong to a twin prime?", r = function(player) - local stg = player:getStorageValue(Storage.CultsOfTibia.MotA.Answer) + local stg = player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Answer) if stg < 2 then return 0 end @@ -131,23 +131,23 @@ local quiz2 = { return (incr == 2 and 1 or 0) end, }, - -- [2] = {p = "", r = ""} } local quiz3 = { [1] = { p = "Is the number divisible by 3?", r = function(player) - return (player:getStorageValue(Storage.CultsOfTibia.MotA.Answer) % 3 == 0 and 1 or 0) + return (player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Answer) % 3 == 0 and 1 or 0) end, }, [2] = { p = "Is the number divisible by 2?", r = function(player) - return (player:getStorageValue(Storage.CultsOfTibia.MotA.Answer) % 2 == 0 and 1 or 0) + return (player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Answer) % 2 == 0 and 1 or 0) end, }, } + local function creatureSayCallback(npc, creature, type, message) local player = Player(creature) local playerId = player:getId() @@ -156,26 +156,23 @@ local function creatureSayCallback(npc, creature, type, message) return false end - -- Começou a quest - if MsgContains(message, "questions") and npcHandler:getTopic(playerId) == 1 then + -- Quest started + if MsgContains(message, "questions") and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) == 13 then npcHandler:say("Ready to {start}?", npc, creature) npcHandler:setTopic(playerId, 2) - npcHandler:setTopic(playerId, 2) elseif MsgContains(message, "start") and npcHandler:getTopic(playerId) == 2 then - local perguntaid = math.random(#quiz1) - player:setStorageValue(Storage.CultsOfTibia.MotA.QuestionId, perguntaid) - npcHandler:say(quiz1[perguntaid].p, npc, creature) - npcHandler:setTopic(playerId, 3) + local questionId = math.random(#quiz1) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.QuestionId, questionId) + npcHandler:say(quiz1[questionId].p, npc, creature) npcHandler:setTopic(playerId, 3) elseif npcHandler:getTopic(playerId) == 3 then npcHandler:say(string.format("Your answer is %s, do you want to continue?", message), npc, creature) playerLastResp[playerId] = tonumber(message) npcHandler:setTopic(playerId, 4) - npcHandler:setTopic(playerId, 4) elseif npcHandler:getTopic(playerId) == 4 then if MsgContains(message, "yes") then - local resposta = quiz1[player:getStorageValue(Storage.CultsOfTibia.MotA.QuestionId)].r - if playerLastResp[playerId] ~= (tonumber(resposta(player))) then + local answer = quiz1[player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.QuestionId)].r + if playerLastResp[playerId] ~= (tonumber(answer(player))) then npcHandler:say("Wrong. SHUT DOWN.", npc, creature) npcHandler:resetNpc(creature) npcHandler:removeInteraction(npc, creature) @@ -183,7 +180,6 @@ local function creatureSayCallback(npc, creature, type, message) else npcHandler:say("Correct. {Next} question?", npc, creature) npcHandler:setTopic(playerId, 5) - npcHandler:setTopic(playerId, 5) end elseif MsgContains(message, "no") then npcHandler:say("SHUT DOWN.", npc, creature) @@ -192,23 +188,21 @@ local function creatureSayCallback(npc, creature, type, message) return false end elseif MsgContains(message, "next") and npcHandler:getTopic(playerId) == 5 then - local perguntaid = math.random(#quiz2) - player:setStorageValue(Storage.CultsOfTibia.MotA.QuestionId, perguntaid) - npcHandler:say(quiz2[perguntaid].p, npc, creature) - npcHandler:setTopic(playerId, 6) + local questionId = math.random(#quiz2) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.QuestionId, questionId) + npcHandler:say(quiz2[questionId].p, npc, creature) npcHandler:setTopic(playerId, 6) elseif npcHandler:getTopic(playerId) == 6 then - local resp = 0 + local response = 0 if MsgContains(message, "no") then - resp = 0 + response = 0 elseif MsgContains(message, "yes") then - resp = 1 + response = 1 end - local resposta = quiz2[player:getStorageValue(Storage.CultsOfTibia.MotA.QuestionId)].r - if resp == resposta(player) then + local answer = quiz2[player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.QuestionId)].r + if response == answer(player) then npcHandler:say("Correct. {Next} question?", npc, creature) npcHandler:setTopic(playerId, 7) - npcHandler:setTopic(playerId, 7) else npcHandler:say("Wrong. SHUT DOWN.", npc, creature) npcHandler:resetNpc(creature) @@ -216,23 +210,21 @@ local function creatureSayCallback(npc, creature, type, message) return false end elseif npcHandler:getTopic(playerId) == 7 and MsgContains(message, "next") then - local perguntaid = math.random(#quiz3) - player:setStorageValue(Storage.CultsOfTibia.MotA.QuestionId, perguntaid) - npcHandler:say(quiz3[perguntaid].p, npc, creature) - npcHandler:setTopic(playerId, 8) + local questionId = math.random(#quiz3) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.QuestionId, questionId) + npcHandler:say(quiz3[questionId].p, npc, creature) npcHandler:setTopic(playerId, 8) elseif npcHandler:getTopic(playerId) == 8 then - local resp = 0 + local response = 0 if MsgContains(message, "no") then - resp = 0 + response = 0 elseif MsgContains(message, "yes") then - resp = 1 + response = 1 end - local resposta = quiz3[player:getStorageValue(Storage.CultsOfTibia.MotA.QuestionId)].r - if resp == resposta(player) then + local answer = quiz3[player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.QuestionId)].r + if response == answer(player) then npcHandler:say("Correct. {Last} question?", npc, creature) npcHandler:setTopic(playerId, 9) - npcHandler:setTopic(playerId, 9) else npcHandler:say("Wrong. SHUT DOWN.", npc, creature) npcHandler:resetNpc(creature) @@ -242,15 +234,13 @@ local function creatureSayCallback(npc, creature, type, message) elseif npcHandler:getTopic(playerId) == 9 and MsgContains(message, "last") then npcHandler:say("Tell me the correct number?", npc, creature) npcHandler:setTopic(playerId, 10) - npcHandler:setTopic(playerId, 10) elseif npcHandler:getTopic(playerId) == 10 then npcHandler:say(string.format("Your answer is %s, do you want to continue?", message), npc, creature) playerLastResp[playerId] = tonumber(message) npcHandler:setTopic(playerId, 11) - npcHandler:setTopic(playerId, 11) elseif npcHandler:getTopic(playerId) == 11 then if MsgContains(message, "yes") then - local correct = string.format("%d%d%d", player:getStorageValue(Storage.CultsOfTibia.MotA.Stone1), player:getStorageValue(Storage.CultsOfTibia.MotA.Stone2), player:getStorageValue(Storage.CultsOfTibia.MotA.Stone3)) + local correct = string.format("%d%d%d", player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Stone1), player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Stone2), player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Stone3)) if tonumber(playerLastResp[playerId]) ~= (tonumber(correct)) then npcHandler:say("Wrong. SHUT DOWN.", npc, creature) npcHandler:resetNpc(creature) @@ -258,8 +248,8 @@ local function creatureSayCallback(npc, creature, type, message) return false else npcHandler:say("Correct. The lower door is now open. The druid of Crunor lies.", npc, creature) - player:setStorageValue(Storage.CultsOfTibia.MotA.Mission, player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) + 1) - player:setStorageValue(Storage.CultsOfTibia.MotA.AccessDoorDenominator) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission, player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) + 1) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.AccessDoorDenominator) end elseif MsgContains(message, "no") then npcHandler:say("SHUT DOWN.", npc, creature) diff --git a/data-otservbr-global/npc/gail.lua b/data-otservbr-global/npc/gail.lua index 1c61a2cbc6d..e0b6062908b 100644 --- a/data-otservbr-global/npc/gail.lua +++ b/data-otservbr-global/npc/gail.lua @@ -76,10 +76,9 @@ local function creatureSayCallback(npc, creature, type, message) return false end - if MsgContains(message, "daughter") and player:getStorageValue(Storage.TheSecretLibrary.Peacock) == 1 then - npcHandler:say({ "I always feared that I lost her. And yet, all those years, I still had a gleam of hope. I'm devastated to learn about her fate - but at least I have certainty now. Thank you for telling me." }, npc, creature) - player:setStorageValue(Storage.TheSecretLibrary.Peacock, 2) - npcHandler:setTopic(playerId, 1) + if MsgContains(message, "daughter") and player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Asuras.Questline) == 3 then + npcHandler:say("I always feared that I lost her. And yet, all those years, I still had a gleam of hope. I'm devastated to learn about her fate - but at least I have certainty now. Thank you for telling me.", npc, creature) + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Asuras.Questline, 4) npcHandler:setTopic(playerId, 1) end diff --git a/data-otservbr-global/npc/gareth.lua b/data-otservbr-global/npc/gareth.lua index d50f86f11ae..5f783c04cba 100644 --- a/data-otservbr-global/npc/gareth.lua +++ b/data-otservbr-global/npc/gareth.lua @@ -55,14 +55,16 @@ local function greetCallback(npc, creature) local player = Player(creature) - if player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) == 1 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) < 1 or player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) > 14 then + npcHandler:setMessage(MESSAGE_GREET, "Welcome to the wonderful and only recently opened Museum of Tibian Arts! Free entrance for everybody, but patrons of the arts are wanted and favoured.") + elseif player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) == 1 then npcHandler:setMessage(MESSAGE_GREET, "The Druid of Crunor has sent you? He seems to know that this new museum shines like a diamond. Enjoy your stay! If you like to {support} this place, talk to me.") - elseif player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) > 1 and player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) < 14 then + elseif player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) > 1 and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) < 14 then npcHandler:setMessage(MESSAGE_GREET, "How is your {mission} going?") - elseif player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) == 14 then + elseif player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) == 14 then npcHandler:setMessage(MESSAGE_GREET, "You again? How could you flee from the last floor. The cultists should have 'dealt' with you! That beats me. You have to leave this place right now. There's nothing more to say.") - npcHandler:setTopic(playerId, 0) end + return true end @@ -74,64 +76,68 @@ local function creatureSayCallback(npc, creature, type, message) return false end - local valor = 10000 + local value = 10000 - -- Começou a quest - if player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) < 2 then - if MsgContains(message, "support") and npcHandler:getTopic(playerId) == 1 then - npcHandler:say({ "If you like to, you can pay some gold to become a patron of the arts for this wonderful museum. The price is 10,000 gold. Your personal gain will be priceless. Do you want to pay?" }, npc, creature) + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) < 2 then + if MsgContains(message, "patrons") then + npcHandler:say("If you like to, you can pay some gold to become a patron of the arts for this wonderful museum. The price is 10,000 gold. Your personal gain will be priceless. Do you want to {pay}?", npc, creature) npcHandler:setTopic(playerId, 2) - elseif MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 2 then - if (player:getMoney() + player:getBankBalance()) >= valor then - npcHandler:say({ "This is a very wise decision. You won't regret it. Congratulations! As your first task I like you to investigate the crime scene of a theft wich occurred last night. ...", "A very varuable artefact has been stolen. I open the door for you. You can find the room on the same floor as we are right now." }, npc, creature) - npcHandler:setTopic(playerId, 3) - player:removeMoneyBank(valor) + elseif MsgContains(message, "pay") and npcHandler:getTopic(playerId) == 2 then + if (player:getMoney() + player:getBankBalance()) >= value then + npcHandler:say({ + "This is a very wise decision. You won't regret it. Congratulations! As your first task I like you to investigate the crime scene of a theft wich occurred last night. ...", + "A very varuable artefact has been stolen. I open the door for you. You can find the room on the same floor as we are right now.", + }, npc, creature) + player:removeMoneyBank(value) player:addItem(25689, 1) - player:setStorageValue(Storage.CultsOfTibia.MotA.Mission, 2) - player:setStorageValue(Storage.CultsOfTibia.MotA.AccessDoorInvestigation, 1) - if player:getStorageValue(Storage.CultsOfTibia.Questline) < 1 then - player:setStorageValue(Storage.CultsOfTibia.Questline, 1) - end + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission, 2) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.AccessDoorInvestigation, 1) + npcHandler:setTopic(playerId, 3) else - npcHandler:say({ "You don't have enough money." }, npc, creature) + npcHandler:say("You don't have enough money.", npc, creature) npcHandler:setTopic(playerId, 1) end end - - -- Reportando sobre o document - elseif MsgContains(message, "mission") and player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) == 3 then - npcHandler:say({ "They want us to buy the picture back. Unfortunately this artefact is so important that I don't see an alternative. Please got to Iwar in Kazordoon and pay the money." }, npc, creature) - player:setStorageValue(Storage.CultsOfTibia.MotA.Mission, 4) + elseif MsgContains(message, "mission") and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) == 3 then + npcHandler:say("They want us to buy the picture back. Unfortunately this artefact is so important that I don't see an alternative. Please got to Iwar in Kazordoon and pay the money.", npc, creature) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission, 4) npcHandler:setTopic(playerId, 1) - - -- Depois de ter pago o Iwar - elseif MsgContains(message, "mission") and player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) == 5 then + elseif MsgContains(message, "mission") and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) == 5 then npcHandler:say({ "Nice! I'm really happy to have the picture back. First of all I have to check if everything's fine. Then I'll put it back on its place. For now, I'd like you to find out if some rumours about fake pictures in the MOTA are true. ...", "Some say one of the small pictures in the entrance hall here is fake. For this reason you have to go to my friend {Angelo} and ask him to get a {magnifier} for the investigation.", "Then do your job here in the museum and come back.", }, npc, creature) - player:setStorageValue(Storage.CultsOfTibia.MotA.Mission, 6) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission, 6) npcHandler:setTopic(playerId, 1) - - -- Depois de ter visto a pintura falsa - elseif MsgContains(message, "mission") and player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) == 9 then - npcHandler:say({ "So the rumours are true. How could this happen? I'll keep the picture at its place until we've got a replacement. Please fo to {Angelo} and ask him if he has a new artefact for our museum." }, npc, creature) - player:setStorageValue(Storage.CultsOfTibia.MotA.Mission, 10) - elseif MsgContains(message, "mission") and player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) == 11 then - npcHandler:say({ "You're back, nice. Angelo's team hasn't found an artefact yet? I thought the progress would be faster. Anyway thanks for you efforts. ...", "I have no work for you right now. If you like to, you can have a look at the last floor. I open the door for you." }, npc, creature) - player:setStorageValue(Storage.CultsOfTibia.MotA.Mission, 12) - player:setStorageValue(Storage.CultsOfTibia.MotA.AccessDoorGareth, 1) - - --------------------------------------- FALHAS ---------------------------------------------------------- - -- Se ainda não tiver visto a pintura falsa - elseif MsgContains(message, "mission") and player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) == 8 then - npcHandler:say({ "You didn't investigate the pictures yet. Do your job and then come back." }, npc, creature) + elseif MsgContains(message, "mission") and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) == 9 then + npcHandler:say("So the rumours are true. How could this happen? I'll keep the picture at its place until we've got a replacement. Please fo to {Angelo} and ask him if he has a new artefact for our museum.", npc, creature) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission, 10) + npcHandler:setTopic(playerId, 0) + elseif MsgContains(message, "mission") and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) == 11 then + npcHandler:say({ + "You're back, nice. Angelo's team hasn't found an artefact yet? I thought the progress would be faster. Anyway thanks for you efforts. ...", + "I have no work for you right now. If you like to, you can have a look at the last floor. I open the door for you.", + }, npc, creature) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission, 12) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.AccessDoorGareth, 1) + npcHandler:setTopic(playerId, 0) + elseif MsgContains(message, "mission") and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) == 8 then + npcHandler:say("You didn't investigate the pictures yet. Do your job and then come back.", npc, creature) + npcHandler:setTopic(playerId, 0) end - if MsgContains(message, "extension") and player:getStorageValue(Storage.TheSecretLibrary.LiquidDeath) == 11 then - if player:getStorageValue(Storage.TheSecretLibrary.LiquidDeath) == 11 then - npcHandler:say({ "It is planned to extend the MOTA. But this will take time, because our workers have faced a little problem." }, npc, creature) + if MsgContains(message, "extension") then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline) == 6 then + npcHandler:say({ + "You have found an inscription I would like to translate for you. The tibianus cipher was used: ...", + "Those who are accorded the honour to visit this exclusive place will smash their blindness and face the truth. ...", + "Astonishingly, Dedoras from Cormaya has recently asked me for these kinds of inscriptions. For sure he is able to bring light into the darkness. You should visit him. ", + }, npc, creature) + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline, 7) + npcHandler:setTopic(playerId, 0) + elseif player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline) == 1 then + npcHandler:say("It is planned to extend the MOTA. But this will take time, because our workers have faced a little {problem}.", npc, creature) npcHandler:setTopic(playerId, 11) end elseif MsgContains(message, "problem") and npcHandler:getTopic(playerId) == 11 then @@ -144,28 +150,19 @@ local function creatureSayCallback(npc, creature, type, message) end elseif MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 12 then if npcHandler:getTopic(playerId) == 12 then - npcHandler:say({ "You are a true patron of the arts! I have opened the construction site for you. Start your work right now!" }, npc, creature) - player:setStorageValue(Storage.TheSecretLibrary.Mota, 1) - player:setStorageValue(Storage.TheSecretLibrary.LiquidDeath, 12) - npcHandler:setTopic(playerId, 13) + npcHandler:say("You are a true patron of the arts! I have opened the construction site for you. Start your work right now!", npc, creature) + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline, 2) + npcHandler:setTopic(playerId, 0) end end - if MsgContains(message, "bone") and player:getStorageValue(Storage.TheSecretLibrary.Mota) == 2 then - npcHandler:say({ "Hmm, interesting. Several years ago I have read some books dealing with strange locking mechanisms. I think what you have found here is a bone lever of category 3. ...", "Normally this is not used because it is not secure. The production failed and the lever can always be activated as follows: back, back, up, right, left. Just have a try, it should work." }, npc, creature) - player:setStorageValue(Storage.TheSecretLibrary.Mota, 3) - npcHandler:setTopic(playerId, 14) - end - - if MsgContains(message, "extension") and player:getStorageValue(Storage.TheSecretLibrary.Mota) == 11 then - npcHandler:say( - { "You have found an inscription I would like to translate for you. The tibianus cipher was used: ...", "Those who are accorded the honour to visit this exclusive place will smash their blindness and face the truth. ...", "Astonishingly, Dedoras from Cormaya has recently asked me for these kinds of inscriptions. For sure he is able to bring light into the darkness. You should visit him. " }, - npc, - creature - ) - player:setStorageValue(Storage.TheSecretLibrary.Mota, 12) - player:setStorageValue(Storage.TheSecretLibrary.TheLament, 1) - npcHandler:setTopic(playerId, 15) + if MsgContains(message, "bone") and player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline) == 4 then + npcHandler:say({ + "Hmm, interesting. Several years ago I have read some books dealing with strange locking mechanisms. I think what you have found here is a bone lever of category 3. ...", + "Normally this is not used because it is not secure. The production failed and the lever can always be activated as follows: back, back, up, right, left. Just have a try, it should work.", + }, npc, creature) + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline, 5) + npcHandler:setTopic(playerId, 0) end return true diff --git a/data-otservbr-global/npc/gelidrazahs_thirst.lua b/data-otservbr-global/npc/gelidrazahs_thirst.lua index a051dfb791e..beff8d7c6b9 100644 --- a/data-otservbr-global/npc/gelidrazahs_thirst.lua +++ b/data-otservbr-global/npc/gelidrazahs_thirst.lua @@ -72,7 +72,7 @@ local function creatureSayCallback(npc, creature, type, message) elseif MsgContains(message, "Svir") and npcHandler:getTopic(playerId) == 4 then npcHandler:say("That is correct. You satisfactorily answered all questions. You may pass and enter Gelidrazah's lair.", npc, creature) npcHandler:setTopic(playerId, 0) - player:setStorageValue(Storage.FirstDragon.GelidrazahAccess, 1) + player:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.GelidrazahAccess, 1) else npcHandler:say("I don't know what you are talking about.", npc, creature) end diff --git a/data-otservbr-global/npc/gerimor.lua b/data-otservbr-global/npc/gerimor.lua index 1c7d4c8443e..0e8b0c0988d 100644 --- a/data-otservbr-global/npc/gerimor.lua +++ b/data-otservbr-global/npc/gerimor.lua @@ -55,12 +55,9 @@ local function greetCallback(npc, creature) local playerId = player:getId() if player then - npcHandler:setMessage( - MESSAGE_GREET, - "Greeting, |PLAYERNAME|! I welcome you to this sacred {place}. \z - If you are interested in {missions} just ask." - ) + npcHandler:setMessage(MESSAGE_GREET, "Greeting, |PLAYERNAME|! I welcome you to this sacred {place}. If you are interested in {missions} just ask.") end + return true end @@ -73,10 +70,8 @@ keywordHandler:addKeyword({ "place" }, StdModule.say, { keywordHandler:addKeyword({ "me" }, StdModule.say, { npcHandler = npcHandler, text = { - "I'm a member of a circle of persons, that joined wisdom and resources for a common purpose. \z - Let's say, we have an eye on the greater picture in the matters of our world. ...", - "We are watching and evaluating what is happening in our world. \z - Trying to avert the worst and offering a helping hand where we deem it needed. ...", + "I'm a member of a circle of persons, that joined wisdom and resources for a common purpose. Let's say, we have an eye on the greater picture in the matters of our world. ...", + "We are watching and evaluating what is happening in our world. Trying to avert the worst and offering a helping hand where we deem it needed. ...", "We usually avoid to interfere directly in the affairs of the world and vain politics are not our concern at all.", }, }) @@ -89,36 +84,30 @@ keywordHandler:addKeyword({ "circle" }, StdModule.say, { keywordHandler:addKeyword({ "persons" }, StdModule.say, { npcHandler = npcHandler, text = { - "Well, while I focus more on the matters of life, some of my peers have different approaches \z - and emphasize other aspects of the world more in their observations. ...", + "Well, while I focus more on the matters of life, some of my peers have different approaches and emphasize other aspects of the world more in their observations. ...", "Regardless we share a common goal of balance and harmony.", }, }) keywordHandler:addKeyword({ "approaches" }, StdModule.say, { npcHandler = npcHandler, - text = "We might not be many but we are diverse. \z - Our rather informal order came together in the dawn of time, when the wars of the gods ravaged the world.", + text = "We might not be many but we are diverse. Our rather informal order came together in the dawn of time, when the wars of the gods ravaged the world.", }) keywordHandler:addKeyword({ "dawn" }, StdModule.say, { npcHandler = npcHandler, text = { "Even we know the individual that was somewhat of our funder, only as the wise man. ...", - "He was the first to bring bright and dedicated minds together, \z - to bring at least a little order and guidance into troubled and chaotic times. ...", - "The order predates mankind and never bothered to give itself a name. \z - Such assumptions of pretence and vanity have no place in our mindset.", + "He was the first to bring bright and dedicated minds together, to bring at least a little order and guidance into troubled and chaotic times. ...", + "The order predates mankind and never bothered to give itself a name. Such assumptions of pretence and vanity have no place in our mindset.", }, }) keywordHandler:addKeyword({ "guidance" }, StdModule.say, { npcHandler = npcHandler, text = { - "Most times we are silent watchers and keeper of knowledge that share what they have learned with each other. \z - We are more concerned about knowledge and wisdom and power means little to us. ...", - "To solve problems we usually try to convince the right people to do the right thing. \z - We usually even lack the means for a more direct interference.", + "Most times we are silent watchers and keeper of knowledge that share what they have learned with each other. We are more concerned about knowledge and wisdom and power means little to us. ...", + "To solve problems we usually try to convince the right people to do the right thing. We usually even lack the means for a more direct interference.", }, }) @@ -126,113 +115,84 @@ keywordHandler:addKeyword({ "direct" }, StdModule.say, { npcHandler = npcHandler, text = { "Sometimes it's necessary to do something about a situation that became threatening to the world itself. ...", - "It is gladly a rare occurrence and usually it is sufficient to somewhat offer a guiding hand to \z - avert a course that would lead to more dire consequences. Nonetheless sometimes we have to interfere.", + "It is gladly a rare occurrence and usually it is sufficient to somewhat offer a guiding hand to avert a course that would lead to more dire consequences. Nonetheless sometimes we have to interfere.", }, }) keywordHandler:addKeyword({ "interfere" }, StdModule.say, { npcHandler = npcHandler, - text = "Interference comes in different forms. \z - In this particular case there is sadly little time for subtlety and a more direct approach is necessary.", + text = "Interference comes in different forms. In this particular case there is sadly little time for subtlety and a more direct approach is necessary.", }) keywordHandler:addKeyword({ "feyrist" }, StdModule.say, { npcHandler = npcHandler, - text = "The fae granted me permission to enter their hidden realm. \z - As a druid I'm in close touch with nature so I could gain their trust. \z - The nature spirits are inhabiting this peninsula for ages.", + text = "The fae granted me permission to enter their hidden realm. As a druid I'm in close touch with nature so I could gain their trust. The nature spirits are inhabiting this peninsula for ages.", }) keywordHandler:addKeyword({ "fae" }, StdModule.say, { npcHandler = npcHandler, text = { - "The fae vary greatly in size and appearance. \z - There are different kinds of fae like fauns, pixies, pookas, swan maidens, nymphs and boogies. \z - Those mystical creatures are wielding power in magic and elementals. ...", - "Most of them are rather reclusive and live peaceful lives in their secret realm. \z - Sometimes they are called the 'children of dreams' or 'the dream born' \z - because the fae are born from the mortals' dreams.", + "The fae vary greatly in size and appearance. There are different kinds of fae like fauns, pixies, pookas, swan maidens, nymphs and boogies. Those mystical creatures are wielding power in magic and elementals. ...", + "Most of them are rather reclusive and live peaceful lives in their secret realm. Sometimes they are called the 'children of dreams' or 'the dream born' because the fae are born from the mortals' dreams.", }, }) keywordHandler:addKeyword({ "fauns" }, StdModule.say, { npcHandler = npcHandler, text = { - "Fauns are half-human, half-beast nature spirits inhabiting the woods and mountains of Feyrist. \z - They are a slightly roguish but cheerful folk, lovers of wine and dancing. ...", - "Fauns show a youthful and graceful aspect but they are also brave and fearless \z - when it comes to defend themselves. As Maelyrra told me, they emerge from mortals' \z - dreams about celebrations, music and dancing. ...", - "Lately, some fauns on Feyrist are tainted by the mysterious, sinister force that is \z - threatening Feyrist as well as the rest of Tibia.", + "Fauns are half-human, half-beast nature spirits inhabiting the woods and mountains of Feyrist. They are a slightly roguish but cheerful folk, lovers of wine and dancing. ...", + "Fauns show a youthful and graceful aspect but they are also brave and fearless when it comes to defend themselves. As Maelyrra told me, they emerge from mortals' dreams about celebrations, music and dancing. ...", + "Lately, some fauns on Feyrist are tainted by the mysterious, sinister force that is threatening Feyrist as well as the rest of Tibia.", }, }) keywordHandler:addKeyword({ "pixies" }, StdModule.say, { npcHandler = npcHandler, text = { - "Pixies are small nature spirits and mythical creatures inhabiting the forests and plains of Feyrist. \z - They are generally benign, but at times, they may also display mischievous traits. ...", - "Like most of the fae, pixies love dancing and are often gathering in larger groups \z - to dance on secluded glades. Pixies love flowers, butterflies, shimmering beetles, \z - gems and other colourful things. ...", - "They also love the taste of honey, sweetened oat and ripe grapes. \z - As Maelyrra told me, pixies emerge from mortals' dreams about friends and family.", + "Pixies are small nature spirits and mythical creatures inhabiting the forests and plains of Feyrist. They are generally benign, but at times, they may also display mischievous traits. ...", + "Like most of the fae, pixies love dancing and are often gathering in larger groups to dance on secluded glades. Pixies love flowers, butterflies, shimmering beetles, gems and other colourful things. ...", + "They also love the taste of honey, sweetened oat and ripe grapes. As Maelyrra told me, pixies emerge from mortals' dreams about friends and family.", }, }) keywordHandler:addKeyword({ "pookas" }, StdModule.say, { npcHandler = npcHandler, text = { - "Pookas are nature spirits in animal form, looking like big hares with a faintly glittering fur. \z - They are benign but mischievous, for sure with good reason regarded as the tricksters among the fae. ...", - "Pookas love to play pranks on others, snitching and hiding things or telling made-up stories. \z - They are capricious and fickle creatures. \z - Pookas emerge from mortals' dreams about gems, treasures and gold. ...", - "Lately, some pookas on Feyrist are tainted by a mysterious, sinister force that is \z - threatening Feyrist as well as the rest of Tibia.", + "Pookas are nature spirits in animal form, looking like big hares with a faintly glittering fur. They are benign but mischievous, for sure with good reason regarded as the tricksters among the fae. ...", + "Pookas love to play pranks on others, snitching and hiding things or telling made-up stories. They are capricious and fickle creatures. Pookas emerge from mortals' dreams about gems, treasures and gold. ...", + "Lately, some pookas on Feyrist are tainted by a mysterious, sinister force that is threatening Feyrist as well as the rest of Tibia.", }, }) keywordHandler:addKeyword({ "swan maidens" }, StdModule.say, { npcHandler = npcHandler, text = { - "Swan maidens are fae who can shapeshift from human form to swan form. \z - The magical item allowing this transformation is a swan feather cloak, a garment with swan feathers attached. ...", - "Here on Feyrist it is always hard to tell whether a swan swimming on a lake \z - is an ordinary animal or a swan maiden in her bird shape. ...", - "They protect the wilds of their secret realm from every intruder and live in small \z - flocks along secluded lakeshores. As Maelyrra told me, swan maidens emerge from mortals' dreams about flying.", + "Swan maidens are fae who can shapeshift from human form to swan form. The magical item allowing this transformation is a swan feather cloak, a garment with swan feathers attached. ...", + "Here on Feyrist it is always hard to tell whether a swan swimming on a lake is an ordinary animal or a swan maiden in her bird shape. ...", + "They protect the wilds of their secret realm from every intruder and live in small flocks along secluded lakeshores. As Maelyrra told me, swan maidens emerge from mortals' dreams about flying.", }, }) keywordHandler:addKeyword({ "nymphs" }, StdModule.say, { npcHandler = npcHandler, text = { - "Nymphs are female nature spirits and usually take the form of beautiful, \z - young maidens who love to dance and sing. They dwell in the hills and forests of Feyrist, \z - often near lakes and streams and they can't die of old age nor illness. ...", - "They have a special, strong bond to the plants and animals of their domain and are very \z - protective of Feyrist's flora and fauna. As Maelyrra told me, nymphs emerge from mortals' dreams about love.", + "Nymphs are female nature spirits and usually take the form of beautiful, young maidens who love to dance and sing. They dwell in the hills and forests of Feyrist, often near lakes and streams and they can't die of old age nor illness. ...", + "They have a special, strong bond to the plants and animals of their domain and are very protective of Feyrist's flora and fauna. As Maelyrra told me, nymphs emerge from mortals' dreams about love.", }, }) keywordHandler:addKeyword({ "boogies" }, StdModule.say, { npcHandler = npcHandler, text = { - "Boogies are a rather twisted kind of fae. Other than pixies, nymphs or fauns they favour \z - underground caves and tunnels over forests or lush meadows. ...", - "Only at night, they are roaming the surface, chasing other fae and visitors to Feyrist alike. \z - They were once clumsy yet peaceful fae, but they are now twisted and tainted by a mysterious, sinister force.", + "Boogies are a rather twisted kind of fae. Other than pixies, nymphs or fauns they favour underground caves and tunnels over forests or lush meadows. ...", + "Only at night, they are roaming the surface, chasing other fae and visitors to Feyrist alike. They were once clumsy yet peaceful fae, but they are now twisted and tainted by a mysterious, sinister force.", }, }) keywordHandler:addKeyword({ "maelyrra" }, StdModule.say, { npcHandler = npcHandler, text = { - "She's the queen of a fae court. You can find her on a glade in the deep forest. \z - It was queen Maelyrra who granted me permission to stay here in Feyrist. ...", + "She's the queen of a fae court. You can find her on a glade in the deep forest. It was queen Maelyrra who granted me permission to stay here in Feyrist. ...", "I promised to inform her about anything I find out about the abominable force that threatens this world.", }, }) @@ -240,65 +200,49 @@ keywordHandler:addKeyword({ "maelyrra" }, StdModule.say, { keywordHandler:addKeyword({ "fae court" }, StdModule.say, { npcHandler = npcHandler, text = { - "The fae vary greatly in size and appearance. \z - There are different kinds of fae like fauns, pixies, pookas, swan maidens, nymphs and boogies. \z - Those mystical creatures are wielding power in magic and elementals. ...", - "Most of them are rather reclusive and live peaceful lives in their secret realm. \z - Sometimes they are called the ,children of dreams' or ,the dream born' \z - because the fae are born from the mortals' dreams.", + "The fae vary greatly in size and appearance. There are different kinds of fae like fauns, pixies, pookas, swan maidens, nymphs and boogies. Those mystical creatures are wielding power in magic and elementals. ...", + "Most of them are rather reclusive and live peaceful lives in their secret realm. Sometimes they are called the ,children of dreams' or ,the dream born' because the fae are born from the mortals' dreams.", }, }) keywordHandler:addKeyword({ "cults" }, StdModule.say, { npcHandler = npcHandler, text = { - "It doesn't seem that the cults share a common theme or object of reverence but there has to \z - be some connection beyond being at the centre of culminations of disruptive power from beyond. ...", - "The connection is of second thought though. Connected or not, they further the death of our world. \z - That alone makes it imperative to dig those cults out and destroy their cores. ...", - "We won't be able to rout our each and any movement but if we manage to neutralize the worst, \z - we gain some time and deny the enemy much of its leverage on the future of our world.", + "It doesn't seem that the cults share a common theme or object of reverence but there has to be some connection beyond being at the centre of culminations of disruptive power from beyond. ...", + "The connection is of second thought though. Connected or not, they further the death of our world. That alone makes it imperative to dig those cults out and destroy their cores. ...", + "We won't be able to rout our each and any movement but if we manage to neutralize the worst, we gain some time and deny the enemy much of its leverage on the future of our world.", }, }) keywordHandler:addKeyword({ "worst" }, StdModule.say, { npcHandler = npcHandler, text = { - "We have located some of the worst culminations of otherworldly presence and our sources \z - returned information about them with different results of success. ...", - "Some information I can provide you will be rather sparse and much is left to speculation \z - but you should have at least some lead where to go and investigate.", + "We have located some of the worst culminations of otherworldly presence and our sources returned information about them with different results of success. ...", + "Some information I can provide you will be rather sparse and much is left to speculation but you should have at least some lead where to go and investigate.", }, }) keywordHandler:addKeyword({ "investigate" }, StdModule.say, { npcHandler = npcHandler, - text = "Those cults have to be stopped by any means possible. \z - These are desperate times and they demand desperate actions.", + text = "Those cults have to be stopped by any means possible. These are desperate times and they demand desperate actions.", }) keywordHandler:addKeyword({ "actions" }, StdModule.say, { npcHandler = npcHandler, - text = "Spare lives where you see it fit but the cults may not be allowed to exist and disrupt \z - the fabric of the world even more.", + text = "Spare lives where you see it fit but the cults may not be allowed to exist and disrupt the fabric of the world even more.", }) keywordHandler:addKeyword({ "fabric" }, StdModule.say, { npcHandler = npcHandler, - text = "The weakened fabric of our reality still repels the unnatural intruder. The cults provide the \z - thing a hold and supply it with more power, even if we couldn't figure out yet, how this works at all.", + text = "The weakened fabric of our reality still repels the unnatural intruder. The cults provide the thing a hold and supply it with more power, even if we couldn't figure out yet, how this works at all.", }) keywordHandler:addKeyword({ "works" }, StdModule.say, { npcHandler = npcHandler, text = { - "We haven't completely figured out what our enemy exactly is. \z - For one, this thing defies all laws of nature and comprehension, ...", - "that understanding it is either impossible or twist a mind in ways that are not meant to be. \z - Also the Yalahari who figured out way too much about the thing, became tainted and changed by this knowledge ...", - "And ultimately not only fell and became his, they also provided the thing with something of their own, \z - be it knowledge, understanding or even direction, purpose. ...", + "We haven't completely figured out what our enemy exactly is. For one, this thing defies all laws of nature and comprehension, ...", + "that understanding it is either impossible or twist a mind in ways that are not meant to be. Also the Yalahari who figured out way too much about the thing, became tainted and changed by this knowledge ...", + "And ultimately not only fell and became his, they also provided the thing with something of their own, be it knowledge, understanding or even direction, purpose. ...", "In some way their tainted knowledge brought the unthinkable into a resemblance of existence. ...", - "That is why we cant dabble too much in figuring this out and rather concentrate on our \z - fight to severe its ties to our world.", + "That is why we cant dabble too much in figuring this out and rather concentrate on our fight to severe its ties to our world.", }, }) @@ -311,95 +255,86 @@ local config = { missions = { ["minotaurs"] = { text = { - "This is an animal-like cult. Only minotaurs can be found there, \z - but no idea what there are expecting and what they are worshipping. ...", + "This is an animal-like cult. Only minotaurs can be found there, but no idea what there are expecting and what they are worshipping. ...", "Maybe they are a bit different to the creatures you already know. Would you like to dinf out more for me?", }, completeText = { - "Then you got it. Thank you. Here is your reward.", + "You have found the source of power which strengthened the minotaurs. Thanks a lot! Here your reward.", }, - storage = Storage.CultsOfTibia.Minotaurs.Mission, + storage = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.Mission, value = 5, rewardExp = 25000, }, ["prosperity"] = { text = { - "The alleged incentive to follow this cult is infinite prosperity. \z - Therefore most of the worshippers are already very rich citizens of Tibia. ...", - "This cult is abandoned in the recently opened new museum in Thais. \z - This can be entered in the Thais exhibition. Would you like to have a look at this cult?", + "The alleged incentive to follow this cult is infinite prosperity. Therefore most of the worshippers are already very rich citizens of Tibia. ...", + "This cult is abandoned in the recently opened new museum in Thais. This can be entered in the Thais exhibition. Would you like to have a look at this cult?", }, completeText = { - "Then you got it. Thank you. Here is your reward.", + "Thanks a lot. As I already supposed the museum is just a disguise. ...", + 'You found out the true meaning. As you have described their cult object the AM on the floor might stand for "Aurea Manus". Here is your reward for your effort.', }, - storage = Storage.CultsOfTibia.MotA.Mission, + storage = Storage.Quest.U11_40.CultsOfTibia.MotA.Mission, value = 14, rewardExp = 50000, }, ["barkless"] = { text = { - "However, recently they became more prominent as their leader seems to hava taken a turn for the worse. \z - Rumors of violent acts and disappearing people are linked to this cult. ...", + "However, recently they became more prominent as their leader seems to hava taken a turn for the worse. Rumors of violent acts and disappearing people are linked to this cult. ...", "Someone... Should look into that, don't you think?", }, completeText = { - "Then you got it. Thank you. Here is your reward.", + "The Penitent, thats what they called him? If their spiritual leader and creator of this following changed so radically...", + "Something far more dangerous must have stood behind this.", }, - storage = Storage.CultsOfTibia.Barkless.Mission, + storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.Mission, value = 6, rewardExp = 50000, }, ["orcs"] = { text = { - "Several Edron orcs have taken to a dangerous idol it seems. \z - It may not be too late to stop them if you act quickly. \z - A powerful cult of orcs with a broad following could prove unsurmountable in the end. ...", + "Several Edron orcs have taken to a dangerous idol it seems. It may not be too late to stop them if you act quickly. A powerful cult of orcs with a broad following could prove unsurmountable in the end. ...", }, completeText = { - "Then you got it. Thank you. Here is your reward.", - "Are you prepared to investigate?", + "That was no god - yet you rid the world of a being which, without the help of one, would not even have been here in the first place. Nicely done.", }, - storage = Storage.CultsOfTibia.Orcs.Mission, + storage = Storage.Quest.U11_40.CultsOfTibia.Orcs.Mission, value = 2, rewardExp = 25000, }, ["life"] = { text = { - "Its worshippers wish for eternal, life free of pain and sorrow. \z - The entrance to this cult can be found in the dark pyramid. Would you life to investigate it for me?", + "Its worshippers wish for eternal, life free of pain and sorrow. The entrance to this cult can be found in the dark pyramid. Would you life to investigate it for me?", }, completeText = { - "Then you got it. Thank you. Here is your reward.", + "Thanks a lot. You have revealed the mystery of this cult and killed the sandking. ...", + "The signature AM you have seen, could stand for 'Aeterna Exsistentia' regarding the eternal life. As a reward I give this to you.", }, - storage = Storage.CultsOfTibia.Life.Mission, + storage = Storage.Quest.U11_40.CultsOfTibia.Life.Mission, value = 9, rewardExp = 50000, }, ["misguided"] = { text = { - "There's a camp of outlaws to the east of Thais. \z - Rumour has it that people are going missing but it's not linked to the bandits. ...", - "Lights have been seen at night in the abandoned ruin in the vicinity of the camp, \z - somewhere to the south-west. Brave enough to check it out?", + "There's a camp of outlaws to the east of Thais. Rumour has it that people are going missing but it's not linked to the bandits. ...", + "Lights have been seen at night in the abandoned ruin in the vicinity of the camp, somewhere to the south-west. Brave enough to check it out?", }, completeText = { - "Then you got it. Thank you. Here is your reward.", + "So the leader of these... Misguided was actually controlled and not the other way round? Whatever is behind all this, that's some first-rate irony right there.", }, - storage = Storage.CultsOfTibia.Misguided.Mission, + storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.Mission, value = 4, rewardExp = 50000, }, ["humans"] = { text = { - "It's a forbidden and abandoned place but... There is an ancient temple of Zathroth beneath Carlin. \z - Some say it's not that abandoned anymore. ...", - "Voices, flickering lights in the dead of night, and even a strange gate like sphere wich leads to \z - who knows where. I can't really request this from you but... Someone should take a look, or not?", + "It's a forbidden and abandoned place but... There is an ancient temple of Zathroth beneath Carlin. Some say it's not that abandoned anymore. ...", + "Voices, flickering lights in the dead of night, and even a strange gate like sphere wich leads to who knows where. I can't really request this from you but... Someone should take a look, or not?", }, completeText = { - "Then you got it. Thank you. Here is your reward.", + "Zathroth wasn't behind this after all. That's good... what's not good is that we have to deal with an unknown power now, let's hope for the best.", }, - storage = Storage.CultsOfTibia.Humans.Mission, + storage = Storage.Quest.U11_40.CultsOfTibia.Humans.Mission, value = 2, rewardExp = 25000, }, @@ -419,75 +354,37 @@ local function creatureSayCallback(npc, creature, type, message) end if MsgContains(message, "missions") then - -- Final boss check - if player:getStorageValue(Storage.CultsOfTibia.FinalBoss.Mission) > 2 then - npcHandler:say( - "You have already fulfilled your job to my full satisfaction. \z - The cults are investigated and the final boss is eliminated. \z - I have nothing more for you to do. Fare you well!", - npc, - creature - ) + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.FinalBoss.Mission) > 2 then + npcHandler:say("You have already fulfilled your job to my full satisfaction. The cults are investigated and the final boss is eliminated. I have nothing more for you to do. Fare you well!", npc, creature) npcHandler:setTopic(playerId, 0) elseif - player:getStorageValue(Storage.CultsOfTibia.Minotaurs.Mission) == 6 - and player:getStorageValue(Storage.CultsOfTibia.Life.Mission) == 10 - and player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) == 15 - and player:getStorageValue(Storage.CultsOfTibia.Barkless.Mission) == 7 - and player:getStorageValue(Storage.CultsOfTibia.Misguided.Mission) == 5 - and player:getStorageValue(Storage.CultsOfTibia.Orcs.Mission) == 3 - and player:getStorageValue(Storage.CultsOfTibia.Humans.Mission) == 3 - and player:getStorageValue(Storage.CultsOfTibia.FinalBoss.Mission) < 2 + player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Minotaurs.Mission) == 6 + and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission) == 10 + and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) == 15 + and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Mission) == 7 + and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.Mission) == 5 + and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Orcs.Mission) == 3 + and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Humans.Mission) == 3 + and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.FinalBoss.Mission) < 2 then - npcHandler:say( - "It seems to me that you have done all the missions I gave you. \z - All the cults have been revealed and now you can kill their leader, the final boss, \z - to save the world from a possible catastrophe.", - npc, - creature - ) + npcHandler:say({ + "Your actions have weakened the worldly anchors of the enemy and unveiled the source they use to strengthen their cults. ...", + "Our circle has used this opportunity to breach their protective shroud and aim a teleporter to this source. I would like to ask you to use it, to travel to this source and destroy it. ...", + "But be warned, you will need a group twice as great compared to those with which you defeated the cults. Go now, with my blessings.", + }, npc, creature) npcHandler:setTopic(playerId, 0) - if player:getStorageValue(Storage.CultsOfTibia.FinalBoss.Mission) < 1 then - player:setStorageValue(Storage.CultsOfTibia.FinalBoss.Mission, 1) - player:setStorageValue(Storage.CultsOfTibia.FinalBoss.AccessDoor, 1) - end - elseif player:getStorageValue(Storage.CultsOfTibia.FinalBoss.Mission) == 2 then - npcHandler:say("You did it! You put an end to the cults, and as a return, here's your reward.", npc, creature) - npcHandler:setTopic(playerId, 9) - local item = "" - if player:getVocation():getBaseId() == VOCATION.BASE_ID.SORCERER then - player:addItem(26190) - item = "reflecting crown" - end - if player:getVocation():getBaseId() == VOCATION.BASE_ID.DRUID then - player:addItem(26187) - item = "leaf crown" - end - if player:getVocation():getBaseId() == VOCATION.BASE_ID.PALADIN then - player:addItem(26189) - item = "incandescent crown" - end - if player:getVocation():getBaseId() == VOCATION.BASE_ID.KNIGHT then - player:addItem(26188) - item = "iron crown" + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.FinalBoss.Mission) < 1 then + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.FinalBoss.Mission, 1) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.FinalBoss.AccessDoor, 1) end - player:addExperience(50000) - player:addItem(26186) - player:setStorageValue(Storage.CultsOfTibia.FinalBoss.Mission, 3) - player:sendTextMessage(MESSAGE_EXPERIENCE, "You gained 50000 experience points.") - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You gained a mystery box.") - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You gained a " .. item .. ".") - else - npcHandler:say( - "In wich of the following topics are you interested in? Cult of {Life}, \z - Cult of {Prosperity}, Cult of the {Minotaurs}, Cult of the {Barkless}, Cult of the {Misguided}, \z - Cult of {Orcs} or Cult of the {Humans}?", - npc, - creature - ) - npcHandler:setTopic(playerId, 2) + elseif player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.FinalBoss.Mission) == 2 then + npcHandler:say({ + "You have done our world a great favour and reason enough to be proud of yourself. ...", + "Although we could not rout out each and every cult and they will soon find another source to fuel their evil, we have dealt the enemy a vital blow that will take time and resources to recouperate from. ...", + "You have undoubtedly bought your world some valuable time and weakened the enemy. Take my thanks in behalf of the world and keep up your heroic work. For your reward you must have two free slots. Are you ready to receive it?", + }, npc, creature) + npcHandler:setTopic(playerId, 4) end - -- General elseif npcHandler:getTopic(playerId) == 2 then local missionsTable = config.missions[message:lower()] if missionsTable then @@ -501,28 +398,51 @@ local function creatureSayCallback(npc, creature, type, message) player:sendTextMessage(MESSAGE_EXPERIENCE, "You gained " .. rewardExperience[playerId] .. " experience points.") npcHandler:setTopic(playerId, 0) elseif player:getStorageValue(storage[playerId]) > 0 and player:getStorageValue(storage[playerId]) > value[playerId] then - npcHandler:say({ "You already done this mission." }, npc, creature) + npcHandler:say("You already done this mission.", npc, creature) npcHandler:setTopic(playerId, 2) else npcHandler:say(missionsTable.text, npc, creature) npcHandler:setTopic(playerId, 3) end end - -- Accept mission - elseif MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 3 then - if player:getStorageValue(storage[playerId]) < 1 then - npcHandler:say("Very nice! Come back if you have found what's going on in this cult.", npc, creature) - player:setStorageValue(storage[playerId], 1) - if player:getStorageValue(Storage.CultsOfTibia.Questline) < 1 then - player:setStorageValue(Storage.CultsOfTibia.Questline, 1) + elseif MsgContains(message, "yes") then + if npcHandler:getTopic(playerId) == 3 then + if player:getStorageValue(storage[playerId]) < 1 then + npcHandler:say("Very nice! Come back if you have found what's going on in this cult.", npc, creature) + player:setStorageValue(storage[playerId], 1) + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Questline) < 1 then + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Questline, 1) + end + npcHandler:setTopic(playerId, 2) + elseif player:getStorageValue(storage[playerId]) > 0 then + npcHandler:say("You have not finished your work yet. Come back when you're done.", npc, creature) + npcHandler:setTopic(playerId, 2) end - npcHandler:setTopic(playerId, 2) - elseif player:getStorageValue(storage[playerId]) > 0 then - npcHandler:say("You have not finished your work yet. Come back when you're done.", npc, creature) - npcHandler:setTopic(playerId, 2) + elseif npcHandler:getTopic(playerId) == 4 then + local vocationRewards = { + [VOCATION.BASE_ID.SORCERER] = { itemId = 26190, itemName = "reflecting crown" }, + [VOCATION.BASE_ID.DRUID] = { itemId = 26187, itemName = "leaf crown" }, + [VOCATION.BASE_ID.PALADIN] = { itemId = 26189, itemName = "incandescent crown" }, + [VOCATION.BASE_ID.KNIGHT] = { itemId = 26188, itemName = "iron crown" }, + } + local vocationId = player:getVocation():getBaseId() + local reward = vocationRewards[vocationId] + local item = "" + if reward then + player:addItem(reward.itemId) + item = reward.itemName + end + player:addExperience(50000) + player:addItem(26186) + player:addAchievement("Corruption Contained") + player:sendTextMessage(MESSAGE_EXPERIENCE, "You gained 50000 experience points.") + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You gained a mystery box.") + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You gained a " .. item .. ".") + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.FinalBoss.Mission, 3) + npcHandler:say("Here's your reward. Thank you and farewell!", npc, creature) + npcHandler:setTopic(playerId, 0) end - -- Recuse mission - elseif MsgContains(message, "no") and npcHandler:getTopic(playerId) == 3 then + elseif MsgContains(message, "no") then npcHandler:say("What a pitty! You can come back, when ever you want, if you have changed your opinion.", npc, creature) npcHandler:setTopic(playerId, 0) end diff --git a/data-otservbr-global/npc/gnomus.lua b/data-otservbr-global/npc/gnomus.lua index f12104ca8e0..20b44ef0ba1 100644 --- a/data-otservbr-global/npc/gnomus.lua +++ b/data-otservbr-global/npc/gnomus.lua @@ -50,38 +50,7 @@ npcType.onCloseChannel = function(npc, creature) npcHandler:onCloseChannel(npc, creature) end -local quantidade = {} - -local function greetCallback(npc, creature) - local player = Player(creature) - local playerId = player:getId() - - if player then - npcHandler:setMessage(MESSAGE_GREET, { "Greetings, member of the Bigfoot Brigade. We could really use some {help} from you right now. You should prove {worthy} to our alliance." }) - npcHandler:setTopic(playerId, 1) - end - return true -end - -keywordHandler:addKeyword({ "help" }, StdModule.say, { npcHandler = npcHandler, text = "If you're willing to help us, we could need an escort for arriving {ordnance}, help with {charting} the cave system and someone needs to get some heat {measurements} fast." }) -keywordHandler:addKeyword({ "worthy" }, StdModule.say, { - npcHandler = npcHandler, - text = { - "You're already known amongst the gnomes, member of the Bigfoot Brigade. I will make sure that the alliance learns of your deeds but you'll still need to help the dwarves and gnomes of this outpost to show your worth. ...", - "We also found {suspicious devices} carried by all kinds of creatures down here. Down here, they are of extreme worth to us since they could contain the key to what's happening all around us. ...", - "If you can aquire any, return them to me and I make sure to tell the others of your generosity. Return to me afterwards to check on your current {status}.", - }, -}) -keywordHandler:addKeyword({ "base" }, StdModule.say, { - npcHandler = npcHandler, - text = { "Gnomish supplies and ingenuity have helped to establish and fortify this outpost. ...", "Our knowledge of the enemy and it's tactics would be of more use if the dwarves would listen to us somewhat more. But gnomes have learned to live with the imperfection of the other races." }, -}) -keywordHandler:addKeyword({ "efforts" }, StdModule.say, { - npcHandler = npcHandler, - text = { "Our surveys of the area showed us some spikes in heat and seismic activity at very specific places. ...", "We conclude this is no coincidence and the enemy is using devices to pump up the lava to flood the area. We have seen it before and had to retreat each time. ...", "This time though we might have a counter prepared - given me manage to pierce their defences." }, -}) -keywordHandler:addKeyword({ "name" }, StdModule.say, { npcHandler = npcHandler, text = "My name is Gnomus." }) -keywordHandler:addKeyword({ "job" }, StdModule.say, { npcHandler = npcHandler, text = "I'm the main gnomish contact for this base. I coordinate our efforts with those of the dwarves to ensure everything is running smoothly." }) +local amount = {} local function creatureSayCallback(npc, creature, type, message) local player = Player(creature) @@ -91,15 +60,14 @@ local function creatureSayCallback(npc, creature, type, message) return false end - local time = configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN) + local time = 20 * 60 * 60 -- 20 hours - -- missão measurements - if MsgContains(message, "measurements") and npcHandler:getTopic(playerId) == 1 then - if player:getStorageValue(Storage.DangerousDepths.Gnomes.Measurements) == 2 and player:getStorageValue(Storage.DangerousDepths.Gnomes.TimeTaskMeasurements) > 0 then -- Ainda não se passaram as 20h - npcHandler:say({ "I don't need your help for now. Come back later." }, npc, creature) + if MsgContains(message, "measurements") then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Measurements) == 2 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.TimeTaskMeasurements) > 0 then + npcHandler:say("I don't need your help for now. Come back later.", npc, creature) npcHandler:setTopic(playerId, 1) end - if player:getStorageValue(Storage.DangerousDepths.Gnomes.Measurements) == 2 and player:getStorageValue(Storage.DangerousDepths.Gnomes.TimeTaskMeasurements) <= 0 then -- Vai fazer a missão após 20h + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Measurements) == 2 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.TimeTaskMeasurements) <= 0 then npcHandler:say({ "The heat down here is not the only problem we have but one of our greatest concerns. Not only is it almost unbearable for us, it also seems to be rising. ...", "We need to find out if this is true and what that means for this place - and for us gnomes. You can help us do this by grabbing one of our trignometres and collecting as much as data from the heat in this area as possible. ...", @@ -107,47 +75,53 @@ local function creatureSayCallback(npc, creature, type, message) }, npc, creature) npcHandler:setTopic(playerId, 2) end - if player:getStorageValue(Storage.DangerousDepths.Gnomes.Measurements) < 1 then -- Não possuía a missão, agora possui! + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Measurements) < 1 then npcHandler:say({ "The heat down here is not the only problem we have but one of our greatest concerns. Not only is it almost unbearable for us, it also seems to be rising. ...", "We need to find out if this is true and what that means for this place - and for us gnomes. You can help us do this by grabbing one of our trignometres and collecting as much as data from the heat in this area as possible. ...", "We'd need at least 5 measurements. Are you willing to do this?", }, npc, creature) npcHandler:setTopic(playerId, 2) - elseif (player:getStorageValue(Storage.DangerousDepths.Gnomes.Measurements) == 1) and (player:getStorageValue(Storage.DangerousDepths.Gnomes.LocationCount) < 5) then -- Está na missão porém não terminou a task! - npcHandler:say({ "Come back when you have finished your job." }, npc, creature) + elseif (player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Measurements) == 1) and (player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationCount) < 5) then + npcHandler:say("Come back when you have finished your job.", npc, creature) npcHandler:setTopic(playerId, 1) - elseif player:getStorageValue(Storage.DangerousDepths.Gnomes.Measurements) == 1 and player:getStorageValue(Storage.DangerousDepths.Gnomes.LocationCount) == 5 then -- Não possuía a missão, agora possui! - npcHandler:say({ "Excellent, you returned with more data! Let me see... hmm. ...", "Well, we need more data on this but first I will have to show this to our grand horticulturist. Thank you for getting this for us!" }, npc, creature) - player:setStorageValue(Storage.DangerousDepths.Gnomes.TimeTaskMeasurements, os.time() + time) + elseif player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Measurements) == 1 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationCount) == 5 then + npcHandler:say({ + "Excellent, you returned with more data! Let me see... hmm. ...", + "Well, we need more data on this but first I will have to show this to our grand horticulturist. Thank you for getting this for us!", + }, npc, creature) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.TimeTaskMeasurements, os.time() + time) player:addItem(27654, 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.Status, player:getStorageValue(Storage.DangerousDepths.Gnomes.Status) + 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.Measurements, 2) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points) + 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Measurements, 2) npcHandler:setTopic(playerId, 1) end elseif npcHandler:getTopic(playerId) == 2 and MsgContains(message, "yes") then - npcHandler:say({ "How fortunate! There are some trignometres lying around next to that device behind me. Take one and hold it next to high temperature heat sources. ...", "If you gathered enough data, you will actually smell it from the device. ...", "Return to me with the results afterwards. Best of luck, we count on you!" }, npc, creature) - if player:getStorageValue(Storage.DangerousDepths.Questline) < 1 then - player:setStorageValue(Storage.DangerousDepths.Questline, 1) + npcHandler:say({ + "How fortunate! There are some trignometres lying around next to that device behind me. Take one and hold it next to high temperature heat sources. ...", + "If you gathered enough data, you will actually smell it from the device. ...", + "Return to me with the results afterwards. Best of luck, we count on you!", + }, npc, creature) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Questline) < 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Questline, 1) end - player:setStorageValue(Storage.DangerousDepths.Gnomes.Measurements, 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.GnomeChartChest, 1) -- Permissão para usar o baú - player:setStorageValue(Storage.DangerousDepths.Gnomes.LocationCount, 0) -- Garantindo que a task não inicie com -1 - player:setStorageValue(Storage.DangerousDepths.Gnomes.LocationA, 0) -- Garantindo que a task não inicie com -1 - player:setStorageValue(Storage.DangerousDepths.Gnomes.LocationB, 0) -- Garantindo que a task não inicie com -1 - player:setStorageValue(Storage.DangerousDepths.Gnomes.LocationC, 0) -- Garantindo que a task não inicie com -1 - player:setStorageValue(Storage.DangerousDepths.Gnomes.LocationD, 0) -- Garantindo que a task não inicie com -1 - player:setStorageValue(Storage.DangerousDepths.Gnomes.LocationE, 0) -- Garantindo que a task não inicie com -1 + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Measurements, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.GnomeChartChest, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationCount, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationA, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationB, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationC, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationD, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationE, 0) npcHandler:setTopic(playerId, 1) end - -- missão ordnance - if MsgContains(message, "ordnance") and npcHandler:getTopic(playerId) == 1 then - if player:getStorageValue(Storage.DangerousDepths.Gnomes.Ordnance) == 3 and player:getStorageValue(Storage.DangerousDepths.Gnomes.TimeTaskOrdnance) > 0 then -- Ainda não se passaram as 20h - npcHandler:say({ "I don't need your help for now. Come back later." }, npc, creature) + if MsgContains(message, "ordnance") then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Ordnance) == 3 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.TimeTaskOrdnance) > 0 then + npcHandler:say("I don't need your help for now. Come back later.", npc, creature) npcHandler:setTopic(playerId, 1) end - if player:getStorageValue(Storage.DangerousDepths.Gnomes.Ordnance) == 3 and player:getStorageValue(Storage.DangerousDepths.Gnomes.TimeTaskOrdnance) <= 0 then -- Vai fazer a missão após 20h + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Ordnance) == 3 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.TimeTaskOrdnance) <= 0 then npcHandler:say({ "I am constantly waiting for ordnance to arrive. A lot of gnomes intend to travel out here to help us but the main access path to our base is not safe anymore. ...", "Tragically we lost several gnomes after an outbreak of what I can only describe as a force from below. We were completely surprised by their onslaught and retreated to this outpost. ...", @@ -157,7 +131,7 @@ local function creatureSayCallback(npc, creature, type, message) }, npc, creature) npcHandler:setTopic(playerId, 22) end - if player:getStorageValue(Storage.DangerousDepths.Gnomes.Ordnance) < 1 then -- Não possuía a missão, agora possui! + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Ordnance) < 1 then npcHandler:say({ "I am constantly waiting for ordnance to arrive. A lot of gnomes intend to travel out here to help us but the main access path to our base is not safe anymore. ...", "Tragically we lost several gnomes after an outbreak of what I can only describe as a force from below. We were completely surprised by their onslaught and retreated to this outpost. ...", @@ -166,47 +140,48 @@ local function creatureSayCallback(npc, creature, type, message) "Are you willing to help?", }, npc, creature) npcHandler:setTopic(playerId, 22) - elseif (player:getStorageValue(Storage.DangerousDepths.Gnomes.Ordnance) == 1) or (player:getStorageValue(Storage.DangerousDepths.Gnomes.Ordnance) == 2 and player:getStorageValue(Storage.DangerousDepths.Gnomes.GnomesCount) < 5) then -- Está na missão porém não terminou a task! - npcHandler:say({ "Come back when you have finished your job." }, npc, creature) + elseif (player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Ordnance) == 1) or (player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Ordnance) == 2 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.GnomesCount) < 5) then + npcHandler:say("Come back when you have finished your job.", npc, creature) npcHandler:setTopic(playerId, 1) - elseif player:getStorageValue(Storage.DangerousDepths.Gnomes.Ordnance) == 2 and player:getStorageValue(Storage.DangerousDepths.Gnomes.GnomesCount) >= 5 then -- Não possuía a missão, agora possui! - if player:getStorageValue(Storage.DangerousDepths.Gnomes.CrawlersCount) >= 3 then - npcHandler:say({ "AMAZING! Not only did you salve all our friends - you also rescued the animals! Here is your reward and bonus! ...", "The other are already telling stories about you. Please return to me later if you want to help out some more!" }, npc, creature) - player:setStorageValue(Storage.DangerousDepths.Gnomes.TimeTaskOrdnance, os.time() + time) + elseif player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Ordnance) == 2 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.GnomesCount) >= 5 then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.CrawlersCount) >= 3 then + npcHandler:say({ + "AMAZING! Not only did you salve all our friends - you also rescued the animals! Here is your reward and bonus! ...", + "The other are already telling stories about you. Please return to me later if you want to help out some more!", + }, npc, creature) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.TimeTaskOrdnance, os.time() + time) player:addItem(27654, 2) - player:setStorageValue(Storage.DangerousDepths.Gnomes.Status, player:getStorageValue(Storage.DangerousDepths.Gnomes.Status) + 2) - player:setStorageValue(Storage.DangerousDepths.Gnomes.Ordnance, 3) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points) + 2) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Ordnance, 3) else - npcHandler:say({ "The other are already telling stories about you. Please return to me later if you want to help out some more!" }, npc, creature) - player:setStorageValue(Storage.DangerousDepths.Gnomes.TimeTaskOrdnance, os.time() + time) + npcHandler:say("The other are already telling stories about you. Please return to me later if you want to help out some more!", npc, creature) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.TimeTaskOrdnance, os.time() + time) player:addItem(27654, 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.Status, player:getStorageValue(Storage.DangerousDepths.Gnomes.Status) + 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.Ordnance, 3) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points) + 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Ordnance, 3) end npcHandler:setTopic(playerId, 1) end elseif npcHandler:getTopic(playerId) == 22 and MsgContains(message, "yes") then - npcHandler:say( - { "Excellent, just follow the path to east until you reach a dead end, there is a hole that leads to a small cave underneath which will bring you right to the old trail. ...", "Help whoever you can and return them to the save cave exit - oh, and while you're at it... some of them will have pack animals. If you can rescue those as well, I'll hand you a bonus. Good luck!" }, - npc, - creature - ) - if player:getStorageValue(Storage.DangerousDepths.Questline) < 1 then - player:setStorageValue(Storage.DangerousDepths.Questline, 1) + npcHandler:say({ + "Excellent, just follow the path to east until you reach a dead end, there is a hole that leads to a small cave underneath which will bring you right to the old trail. ...", + "Help whoever you can and return them to the save cave exit - oh, and while you're at it... some of them will have pack animals. If you can rescue those as well, I'll hand you a bonus. Good luck!", + }, npc, creature) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Questline) < 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Questline, 1) end - player:setStorageValue(Storage.DangerousDepths.Gnomes.Ordnance, 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.GnomesCount, 0) -- Garantindo que a task não inicie com -1 - player:setStorageValue(Storage.DangerousDepths.Gnomes.CrawlersCount, 0) -- Garantindo que a task não inicie com -1 + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Ordnance, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.GnomesCount, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.CrawlersCount, 0) npcHandler:setTopic(playerId, 1) end - -- missão charting - if MsgContains(message, "charting") and npcHandler:getTopic(playerId) == 1 then - if player:getStorageValue(Storage.DangerousDepths.Gnomes.Charting) == 2 and player:getStorageValue(Storage.DangerousDepths.Gnomes.TimeTaskCharting) > 0 then -- Ainda não se passaram as 20h - npcHandler:say({ "I don't need your help for now. Come back later." }, npc, creature) + if MsgContains(message, "charting") then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Charting) == 2 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.TimeTaskCharting) > 0 then + npcHandler:say("I don't need your help for now. Come back later.", npc, creature) npcHandler:setTopic(playerId, 1) end - if player:getStorageValue(Storage.DangerousDepths.Gnomes.Charting) == 2 and player:getStorageValue(Storage.DangerousDepths.Gnomes.TimeTaskCharting) <= 0 then -- Vai fazer a missão após 20h + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Charting) == 2 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.TimeTaskCharting) <= 0 then npcHandler:say({ "While exploring these caves to find places to collect spores and grow mushrooms, we found several strange structures. I am convinced that this system was once home to intelligent beings. ...", "However, the creatures from below are now disturbing our research as well as some particularly pesky dwarves who just would not leave us alone. ...", @@ -215,7 +190,7 @@ local function creatureSayCallback(npc, creature, type, message) }, npc, creature) npcHandler:setTopic(playerId, 33) end - if player:getStorageValue(Storage.DangerousDepths.Gnomes.Charting) < 1 then -- Não possuía a missão, agora possui! + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Charting) < 1 then npcHandler:say({ "While exploring these caves to find places to collect spores and grow mushrooms, we found several strange structures. I am convinced that this system was once home to intelligent beings. ...", "However, the creatures from below are now disturbing our research as well as some particularly pesky dwarves who just would not leave us alone. ...", @@ -223,21 +198,21 @@ local function creatureSayCallback(npc, creature, type, message) "I am especially interested in the scattered dark structures around these parts. Would you do that?", }, npc, creature) npcHandler:setTopic(playerId, 33) - elseif (player:getStorageValue(Storage.DangerousDepths.Gnomes.Charting) == 1) and (player:getStorageValue(Storage.DangerousDepths.Gnomes.ChartingCount) < 3) then -- Está na missão porém não terminou a task! - npcHandler:say({ "Come back when you have finished your job." }, npc, creature) + elseif (player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Charting) == 1) and (player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.ChartingCount) < 3) then + npcHandler:say("Come back when you have finished your job.", npc, creature) npcHandler:setTopic(playerId, 1) end - if player:getStorageValue(Storage.DangerousDepths.Gnomes.Charting) == 1 and player:getStorageValue(Storage.DangerousDepths.Gnomes.ChartingCount) >= 3 then -- Não possuía a missão, agora possui! - npcHandler:say({ "Thank you very much! With those structures mapped out we will be able to complete the puzzle in no time!" }, npc, creature) - if player:getStorageValue(Storage.DangerousDepths.Gnomes.ChartingCount) == 6 then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Charting) == 1 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.ChartingCount) >= 3 then + npcHandler:say("Thank you very much! With those structures mapped out we will be able to complete the puzzle in no time!", npc, creature) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.ChartingCount) == 6 then player:addItem(27654, 2) - player:setStorageValue(Storage.DangerousDepths.Gnomes.Status, player:getStorageValue(Storage.DangerousDepths.Gnomes.Status) + 2) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points) + 2) else player:addItem(27654, 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.Status, player:getStorageValue(Storage.DangerousDepths.Gnomes.Status) + 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points) + 1) end - player:setStorageValue(Storage.DangerousDepths.Gnomes.Charting, 2) - player:setStorageValue(Storage.DangerousDepths.Gnomes.TimeTaskCharting, os.time() + time) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Charting, 2) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.TimeTaskCharting, os.time() + time) npcHandler:setTopic(playerId, 1) end elseif npcHandler:getTopic(playerId) == 33 and MsgContains(message, "yes") then @@ -246,97 +221,123 @@ local function creatureSayCallback(npc, creature, type, message) "Take one from the stack here next to me and map as many structures as possible. However, we need at least three locations to make any sense of this ancient layout at all. ...", "If you manage to map one of each structure around these parts - I assume there must be at least two times as many around here - I will hand you a bonus!", }, npc, creature) - if player:getStorageValue(Storage.DangerousDepths.Questline) < 1 then - player:setStorageValue(Storage.DangerousDepths.Questline, 1) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Questline) < 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Questline, 1) end - player:setStorageValue(Storage.DangerousDepths.Gnomes.Charting, 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.ChartingCount, 0) -- Garantindo que a task não inicie com -1 - player:setStorageValue(Storage.DangerousDepths.Gnomes.GnomeChartPaper, 1) -- Permissão para usar o papel - player:setStorageValue(Storage.DangerousDepths.Gnomes.OldGate, 0) -- Garantindo que a task não inicie com -1 - player:setStorageValue(Storage.DangerousDepths.Gnomes.TheGaze, 0) -- Garantindo que a task não inicie com -1 - player:setStorageValue(Storage.DangerousDepths.Gnomes.LostRuin, 0) -- Garantindo que a task não inicie com -1 - player:setStorageValue(Storage.DangerousDepths.Gnomes.Outpost, 0) -- Garantindo que a task não inicie com -1 - player:setStorageValue(Storage.DangerousDepths.Gnomes.Bastion, 0) -- Garantindo que a task não inicie com -1 - player:setStorageValue(Storage.DangerousDepths.Gnomes.BrokenTower, 0) -- Garantindo que a task não inicie com -1 + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Charting, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.ChartingCount, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.GnomeChartPaper, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.OldGate, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.TheGaze, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LostRuin, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Outpost, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Bastion, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.BrokenTower, 0) npcHandler:setTopic(playerId, 1) end + local plural = "" + if MsgContains(message, "suspicious devices") or MsgContains(message, "suspicious device") then - npcHandler:say({ "If you bring me any suspicious devices on creatures you slay down here, I'll make it worth your while by telling the others of your generosity. How many do you want to offer? " }, npc, creature) + npcHandler:say("If you bring me any suspicious devices on creatures you slay down here, I'll make it worth your while by telling the others of your generosity. How many do you want to offer? ", npc, creature) npcHandler:setTopic(playerId, 55) elseif npcHandler:getTopic(playerId) == 55 then - quantidade[playerId] = tonumber(message) - if quantidade[playerId] then - if quantidade[playerId] > 1 then + amount[playerId] = tonumber(message) + if amount[playerId] then + if amount[playerId] > 1 then plural = plural .. "s" end - npcHandler:say({ "You want to offer " .. quantidade[playerId] .. " suspicious device" .. plural .. ". Which leader shall have it, (Gnomus) of the {gnomes}, (Klom Stonecutter) of the {dwarves} or the {scouts} (Lardoc Bashsmite)?" }, npc, creature) + npcHandler:say("You want to offer " .. amount[playerId] .. " suspicious device" .. plural .. ". Which leader shall have it, (Gnomus) of the {gnomes}, (Klom Stonecutter) of the {dwarves} or the {scouts} (Lardoc Bashsmite)?", npc, creature) npcHandler:setTopic(playerId, 56) else - npcHandler:say({ "Don't waste my time." }, npc, creature) + npcHandler:say("Don't waste my time.", npc, creature) npcHandler:setTopic(playerId, 1) end elseif MsgContains(message, "gnomes") and npcHandler:getTopic(playerId) == 56 then - if player:getItemCount(30888) >= quantidade[playerId] then - npcHandler:say({ "Done." }, npc, creature) - if quantidade[playerId] > 1 then + if player:getItemCount(30888) >= amount[playerId] then + npcHandler:say("Done.", npc, creature) + if amount[playerId] > 1 then plural = plural .. "s" end - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You earned " .. quantidade[playerId] .. " point" .. plural .. " on the gnomes mission.") - player:removeItem(30888, quantidade[playerId]) - player:setStorageValue(Storage.DangerousDepths.Gnomes.Status, player:getStorageValue(Storage.DangerousDepths.Gnomes.Status) + quantidade[playerId]) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You earned " .. amount[playerId] .. " point" .. plural .. " on the gnomes mission.") + player:removeItem(30888, amount[playerId]) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points) + amount[playerId]) else - npcHandler:say({ "You don't have enough suspicious devices." }, npc, creature) + npcHandler:say("You don't have enough suspicious devices.", npc, creature) npcHandler:setTopic(playerId, 1) end elseif MsgContains(message, "dwarves") and npcHandler:getTopic(playerId) == 56 then - if player:getItemCount(30888) >= quantidade[playerId] then - npcHandler:say({ "Done." }, npc, creature) - if quantidade[playerId] > 1 then + if player:getItemCount(30888) >= amount[playerId] then + npcHandler:say("Done.", npc, creature) + if amount[playerId] > 1 then plural = plural .. "s" end - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You earned " .. quantidade[playerId] .. " point" .. plural .. " on the dwarves mission.") - player:removeItem(30888, quantidade[playerId]) - player:setStorageValue(Storage.DangerousDepths.Dwarves.Status, player:getStorageValue(Storage.DangerousDepths.Dwarves.Status) + quantidade[playerId]) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You earned " .. amount[playerId] .. " point" .. plural .. " on the dwarves mission.") + player:removeItem(30888, amount[playerId]) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Points) + amount[playerId]) else - npcHandler:say({ "You don't have enough suspicious devices." }, npc, creature) + npcHandler:say("You don't have enough suspicious devices.", npc, creature) npcHandler:setTopic(playerId, 1) end elseif MsgContains(message, "scouts") and npcHandler:getTopic(playerId) == 56 then - if player:getItemCount(30888) >= quantidade[playerId] then - npcHandler:say({ "Done." }, npc, creature) - if quantidade[playerId] > 1 then + if player:getItemCount(30888) >= amount[playerId] then + npcHandler:say("Done.", npc, creature) + if amount[playerId] > 1 then plural = plural .. "s" end - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You earned " .. quantidade[playerId] .. " point" .. plural .. " on the scouts mission.") - player:removeItem(30888, quantidade[playerId]) - player:setStorageValue(Storage.DangerousDepths.Scouts.Status, player:getStorageValue(Storage.DangerousDepths.Scouts.Status) + quantidade[playerId]) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You earned " .. amount[playerId] .. " point" .. plural .. " on the scouts mission.") + player:removeItem(30888, amount[playerId]) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Points) + amount[playerId]) else - npcHandler:say({ "You don't have enough suspicious devices." }, npc, creature) + npcHandler:say("You don't have enough suspicious devices.", npc, creature) npcHandler:setTopic(playerId, 1) end end - -- Início checagem de pontos de tasks!! if MsgContains(message, "status") then - npcHandler:say({ "So you want to know what we all think about your deeds? What leader's opinion are you interested in, the {gnomes} (Gnomus), the {dwarves} (Klom Stonecutter) or the {scouts} (Lardoc Bashsmite)?" }, npc, creature) + npcHandler:say("So you want to know what we all think about your deeds? What leader's opinion are you interested in, the {gnomes} (Gnomus), the {dwarves} (Klom Stonecutter) or the {scouts} (Lardoc Bashsmite)?", npc, creature) npcHandler:setTopic(playerId, 5) elseif MsgContains(message, "gnomes") and npcHandler:getTopic(playerId) == 5 then - npcHandler:say({ "The gnomes are still in need of your help, member of Bigfoot's Brigade. Prove your worth by answering their calls! (" .. math.max(player:getStorageValue(Storage.DangerousDepths.Gnomes.Status), 0) .. "/10)" }, npc, creature) + npcHandler:say("The gnomes are still in need of your help, member of Bigfoot's Brigade. Prove your worth by answering their calls! (" .. math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points), 0) .. "/10)", npc, creature) elseif MsgContains(message, "dwarves") and npcHandler:getTopic(playerId) == 5 then - npcHandler:say({ "The dwarves are still in need of your help, member of Bigfoot's Brigade. Prove your worth by answering their calls! (" .. math.max(player:getStorageValue(Storage.DangerousDepths.Dwarves.Status), 0) .. "/10)" }, npc, creature) + npcHandler:say("The dwarves are still in need of your help, member of Bigfoot's Brigade. Prove your worth by answering their calls! (" .. math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Points), 0) .. "/10)", npc, creature) elseif MsgContains(message, "scouts") and npcHandler:getTopic(playerId) == 5 then - npcHandler:say({ "The scouts are still in need of your help, member of Bigfoot's Brigade. Prove your worth by answering their calls! (" .. math.max(player:getStorageValue(Storage.DangerousDepths.Scouts.Status), 0) .. "/10)" }, npc, creature) + npcHandler:say("The scouts are still in need of your help, member of Bigfoot's Brigade. Prove your worth by answering their calls! (" .. math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Points), 0) .. "/10)", npc, creature) end + return true end +keywordHandler:addKeyword({ "help" }, StdModule.say, { npcHandler = npcHandler, text = "If you're willing to help us, we could need an escort for arriving {ordnance}, help with {charting} the cave system and someone needs to get some heat {measurements} fast." }) +keywordHandler:addKeyword({ "worthy" }, StdModule.say, { + npcHandler = npcHandler, + text = { + "You're already known amongst the gnomes, member of the Bigfoot Brigade. I will make sure that the alliance learns of your deeds but you'll still need to help the dwarves and gnomes of this outpost to show your worth. ...", + "We also found {suspicious devices} carried by all kinds of creatures down here. Down here, they are of extreme worth to us since they could contain the key to what's happening all around us. ...", + "If you can aquire any, return them to me and I make sure to tell the others of your generosity. Return to me afterwards to check on your current {status}.", + }, +}) +keywordHandler:addKeyword({ "base" }, StdModule.say, { npcHandler = npcHandler, text = { + "Gnomish supplies and ingenuity have helped to establish and fortify this outpost. ...", + "Our knowledge of the enemy and it's tactics would be of more use if the dwarves would listen to us somewhat more. But gnomes have learned to live with the imperfection of the other races.", +} }) +keywordHandler:addKeyword({ "efforts" }, StdModule.say, { + npcHandler = npcHandler, + text = { + "Our surveys of the area showed us some spikes in heat and seismic activity at very specific places. ...", + "We conclude this is no coincidence and the enemy is using devices to pump up the lava to flood the area. We have seen it before and had to retreat each time. ...", + "This time though we might have a counter prepared - given me manage to pierce their defences.", + }, +}) +keywordHandler:addKeyword({ "name" }, StdModule.say, { npcHandler = npcHandler, text = "My name is Gnomus." }) +keywordHandler:addKeyword({ "job" }, StdModule.say, { npcHandler = npcHandler, text = "I'm the main gnomish contact for this base. I coordinate our efforts with those of the dwarves to ensure everything is running smoothly." }) + +npcHandler:setMessage(MESSAGE_GREET, "Greetings, member of the Bigfoot Brigade. We could really use some {help} from you right now. You should prove {worthy} to our alliance.") npcHandler:setMessage(MESSAGE_WALKAWAY, "Well, bye then.") npcHandler:setCallback(CALLBACK_SET_INTERACTION, onAddFocus) npcHandler:setCallback(CALLBACK_REMOVE_INTERACTION, onReleaseFocus) -npcHandler:setCallback(CALLBACK_GREET, greetCallback) npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback) npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true) diff --git a/data-otservbr-global/npc/ivalisse.lua b/data-otservbr-global/npc/ivalisse.lua index 71948ca4bb2..5b3e5a96140 100644 --- a/data-otservbr-global/npc/ivalisse.lua +++ b/data-otservbr-global/npc/ivalisse.lua @@ -50,16 +50,6 @@ npcType.onCloseChannel = function(npc, creature) npcHandler:onCloseChannel(npc, creature) end --- Don't forget npcHandler = npcHandler in the parameters. It is required for all StdModule functions! -keywordHandler:addKeyword({ "silus" }, StdModule.say, { npcHandler = npcHandler, text = "My {father}, can you tell me if he's alright?" }) -keywordHandler:addKeyword({ "thais" }, StdModule.say, { npcHandler = npcHandler, text = "Alright then, you are very welcome to explore the temple!" }) -keywordHandler:addKeyword({ "name" }, StdModule.say, { npcHandler = npcHandler, text = "My name is Ivalisse." }) -keywordHandler:addKeyword({ "time" }, StdModule.say, { npcHandler = npcHandler, text = "There is always time to make a change." }) -keywordHandler:addKeyword({ "job" }, StdModule.say, { npcHandler = npcHandler, text = "Besides my various {duties} in the temple, I also take care of visitors. Well, I would but right now I can't get my mind of how my {father}'s doing. I am sorry." }) -keywordHandler:addKeyword({ "duties" }, StdModule.say, { npcHandler = npcHandler, text = " I help linking the portals of this temple to other ancient sites of the {Astral Shapers}." }) -keywordHandler:addKeyword({ "duties" }, StdModule.say, { npcHandler = npcHandler, text = " I help linking the portals of this temple to other ancient sites of the {Astral Shapers}." }) -keywordHandler:addKeyword({ "mission" }, StdModule.say, { npcHandler = npcHandler, text = "Besides my various {duties} in the temple, I also take care of visitors. Well, I would but right now I can't get my mind of how my {father}'s doing. I am sorry." }) - local function creatureSayCallback(npc, creature, type, message) local player = Player(creature) local playerId = player:getId() @@ -69,13 +59,12 @@ local function creatureSayCallback(npc, creature, type, message) end if MsgContains(message, "temple") then - npcHandler:say({ - "Well, I hope you like it here. We tried to rebuild in the {Shaper}'s will. I am a bit preoccupied at the moment because of the absence of my {father}. I may not be the best of help currently, sorry.", - }, npc, creature) + npcHandler:say("Well, I hope you like it here. We tried to rebuild in the {Shaper}'s will. I am a bit preoccupied at the moment because of the absence of my {father}. I may not be the best of help currently, sorry.", npc, creature) npcHandler:setTopic(playerId, 1) - end - - if MsgContains(message, "imbuing") or MsgContains(message, "imbuements") then + elseif MsgContains(message, "mission") then + npcHandler:say("Besides my various {duties} in the temple, I also take care of visitors. Well, I would but right now I can't get my mind of how my {father}'s doing. I am sorry.", npc, creature) + npcHandler:setTopic(playerId, 1) + elseif MsgContains(message, "imbuing") or MsgContains(message, "imbuements") then npcHandler:say({ "The astral Shapers had many ways to shape and refine weapons and equipment. They built shrines dedicated to this world's energies, focussing it and utilising it like a tool to enhance objects. ...", "They called this process imbuing and perfected it throughout time. Remains of these shrines are scattered all over Tibia. ...", @@ -85,62 +74,67 @@ local function creatureSayCallback(npc, creature, type, message) "We see the gates as an invitation, use them to your advantage and follow the ways of the Shapers. Access to one of these ancient shrines is the only way to learn additional imbuements. ...", "But beware, as far as we know, some of them have been claimed by other ancient beings and there is now way for us to prepare you what lies beyond any of those gates.", }, npc, creature) + npcHandler:setTopic(playerId, 0) + elseif MsgContains(message, "father") and npcHandler:getTopic(playerId) == 1 then + if player:getStorageValue(Storage.Quest.U8_54.TheNewFrontier.Questline) == 29 then + npcHandler:say({ + "Papa- my father has recently started an adventure on his own. His name is Silus; he is a member of the Edron academy. ...", + "Ever since he joined what he called a 'special research division', he went on and on about Zao and how venturing there would help him get ahead. ...", + "You must know he lives for science, especially concerning faraway lands and cultures. He talked about the importance of practical field studies but, frankly, he isn't particularly cut out for that. ...", + "I know he has to focus to get his research done right now, and I simply cannot leave my duties in the temple. You seem like a person who travels a lot; would you be willing to help me?", + }, npc, creature) + npcHandler:setTopic(playerId, 2) + elseif player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.Ivalisse) == 1 or player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.Chalice) == 1 then + npcHandler:say("Well, I hope you like it here. We tried to rebuild in the Shaper's will. I am a bit preoccupied at the moment because of the absence of my father. I may not be the best of help currently, sorry.", npc, creature) + npcHandler:setTopic(playerId, 0) + elseif player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.DragonkingKilled) >= 1 and player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.Ivalisse) < 1 then + npcHandler:say({ + "What? You're telling me you found father? How is he? What did papa say? A chalice? As a disguise? The whole time? ...", + "Well, I am not as much surprised as I am happy to hear that he's alright. You know, after the incident with the duck and the umbrella—it doesn't get to me that easily anymore. ...", + "Thank you very much for doing all this for me; I will be forever grateful. I have nothing to repay you with, but you are already blessed to have been able to lay eyes on the sacred Shaper ruins.", + }, npc, creature) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.Ivalisse, 1) + npcHandler:setTopic(playerId, 0) + end + elseif MsgContains(message, "yes") then + if npcHandler:getTopic(playerId) == 2 then + npcHandler:say({ + "Thank you! He told me the other researchers in his team discovered a bridge leading to a cave with a dragon sculpture somewhere in a muggy, grassy area. ...", + "The cave is said to lead to a temple complex underground which is ued as a gathering place for a race called 'draken'. He left right away and tried to enter Zao on his own. ...", + "I was even more worried when he explained the route he chose. he wanted to head straight through a giant steppe and through a massive mountainous ridge to reach the grassy plains of lower Zao. ...", + "If you're interested: I know that the Shapers where active in all corners of Tibia. If you happen to find Shaper ruins there, you may even be able to gather some of their lost knowledge. ...", + "I may have been a bit stubborn and angry the day he left, I even refused to say farewell. And now I worry if he is safe. ...", + "I can not do much to help you but I can open a portal to get you quite close to his last known location in Zao. What do you say, will you help me find my father?", + }, npc, creature) + npcHandler:setTopic(playerId, 3) + elseif npcHandler:getTopic(playerId) == 3 then + npcHandler:say("You would? That's great! Thank you! If you can find my father, tell him I understand and that I really miss him!", npc, creature) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AccessFire, 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.Chalice, 1) + npcHandler:setTopic(playerId, 0) + end + elseif MsgContains(message, "no") then + npcHandler:say("Oh nevermind, I am sorry I asked you for this.", npc, creature) + npcHandler:setTopic(playerId, 0) end - if MsgContains(message, "father") and npcHandler:getTopic(playerId) == 1 then - npcHandler:say({ - "Papa- my father has recently started an adventure on his own. His name is Silus, he is a member of the Edron academy. ...", - "Ever since he has joined what he called a 'special research division', he went on and on about Zao and how venturing there would help him get ahead. ...", - "You must know he lives for science, especially concerning far-away lands and cultures. He talked about the importance of practical field studies but, frankly, he isn't particularly cut out for that. ...", - "I know he has to focus to get his research done right now and I simply cannot leave my duties in the temple. You seem like a person who travels a lot, would you be willing to help me?", - }, npc, creature) - npcHandler:setTopic(playerId, 2) - elseif MsgContains(message, "father") and npcHandler:getTopic(playerId) == 1 and player:getStorageValue(Storage.ForgottenKnowledge.Ivalisse) == 1 or player:getStorageValue(Storage.ForgottenKnowledge.Chalice) == 1 then - npcHandler:say({ - "Well, I hope you like it here. We tried to rebuild in the Shaper's will. I am a bit preoccupied at the moment because of the absence of my father. I may not be the best of help currently, sorry.", - }, npc, creature) - elseif MsgContains(message, "father") and npcHandler:getTopic(playerId) == 1 and player:getStorageValue(Storage.ForgottenKnowledge.DragonkingKilled) == 1 then - npcHandler:say({ - "What? You're telling me you found father? How is he, what did papa say? A chalice? As a disguise? The whole time? ...", - "Well, I am not as much surprised as I am happy to hear that he's alright. You know, after the incident with the duck and the umbrella - it doesn't get to me that easily anymore. ...", - "Thank you very much for doing all this for me, I will be forever grateful. I have nothing to repay you with but you are already blessed to have been able to lay eyes on the sacred shaper ruins.", - }, npc, creature) - player:setStorageValue(Storage.ForgottenKnowledge.Ivalisse, 1) - end - - if MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 2 then - npcHandler:say({ - "Thank you! He told me the other researchers in his team discovered a bridge leading to a cave with a dragon sculpture somewhere in a muggy, grassy area. ...", - "The cave is said to lead to a temple complex underground which is ued as a gathering place for a race called 'draken'. He left right away and tried to enter Zao on his own. ...", - "I was even more worried when he explained the route he chose. he wanted to head straight through a giant steppe and through a massive mountainous ridge to reach the grassy plains of lower Zao. ...", - "If you're interested: I know that the Shapers where active in all corners of Tibia. If you happen to find Shaper ruins there, you may even be able to gather some of their lost knowledge. ...", - "I may have been a bit stubborn and angry the day he left, I even refused to say farewell. And now I worry if he is safe. ...", - "I can not do much to help you but I can open a portal to get you quite close to his last known location in Zao. What do you say, will you help me find my father?", - }, npc, creature) - npcHandler:setTopic(playerId, 3) - elseif MsgContains(message, "no") and npcHandler:getTopic(playerId) == 2 then - npcHandler:say({ - "Oh nevermind, I am sorry I asked you for this.", - }, npc, creature) - end - - if MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 3 then - npcHandler:say({ - "You would? That's great! Thank you! If you can find my father, tell him I understand and that I really miss him!", - }, npc, creature) - player:setStorageValue(Storage.ForgottenKnowledge.AccessFire, 1) - player:setStorageValue(Storage.ForgottenKnowledge.Chalice, 1) - elseif MsgContains(message, "no") and npcHandler:getTopic(playerId) == 3 then - npcHandler:say({ - "Oh nevermind, I am sorry I asked you for this.", - }, npc, creature) - end return true end +npcHandler:setMessage(MESSAGE_GREET, "Hello, worshiper of the Astral Shapers! Welcome to the temple! While you are here to pay tribute, you can also learn something about the ancient art of imbuing.") npcHandler:setMessage(MESSAGE_WALKAWAY, "Oh... farewell, child.") npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback) + +-- Don't forget npcHandler = npcHandler in the parameters. It is required for all StdModule functions! +keywordHandler:addKeyword({ "silus" }, StdModule.say, { npcHandler = npcHandler, text = "My {father}, can you tell me if he's alright?" }) +keywordHandler:addKeyword({ "thais" }, StdModule.say, { npcHandler = npcHandler, text = "Alright then, you are very welcome to explore the temple!" }) +keywordHandler:addKeyword({ "name" }, StdModule.say, { npcHandler = npcHandler, text = "My name is Ivalisse." }) +keywordHandler:addKeyword({ "time" }, StdModule.say, { npcHandler = npcHandler, text = "There is always time to make a change." }) +keywordHandler:addKeyword({ "job" }, StdModule.say, { npcHandler = npcHandler, text = "Besides my various {duties} in the temple, I also take care of visitors. Well, I would but right now I can't get my mind of how my {father}'s doing. I am sorry." }) +keywordHandler:addKeyword({ "duties" }, StdModule.say, { npcHandler = npcHandler, text = " I help linking the portals of this temple to other ancient sites of the {Astral Shapers}." }) +keywordHandler:addKeyword({ "duties" }, StdModule.say, { npcHandler = npcHandler, text = " I help linking the portals of this temple to other ancient sites of the {Astral Shapers}." }) + npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true) -- npcType registering the npcConfig table diff --git a/data-otservbr-global/npc/iwar.lua b/data-otservbr-global/npc/iwar.lua index d30d368bf5a..b5238538ba9 100644 --- a/data-otservbr-global/npc/iwar.lua +++ b/data-otservbr-global/npc/iwar.lua @@ -49,12 +49,6 @@ npcType.onCloseChannel = function(npc, creature) npcHandler:onCloseChannel(npc, creature) end -local function greetCallback(npc, creature) - local playerId = creature:getId() - - local player = Player(creature) - return true -end local function creatureSayCallback(npc, creature, type, message) local player = Player(creature) local playerId = player:getId() @@ -63,23 +57,22 @@ local function creatureSayCallback(npc, creature, type, message) return false end - local valorPicture = 10000 + local valuePicture = 10000 - -- Começou a quest - if MsgContains(message, "has the cat got your tongue?") and player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) == 4 then - npcHandler:say({ "Nice. You like your picture, haa? Give me 10,000 gold and I will deliver it to the museum. Do you {pay}?" }, npc, creature) + if MsgContains(message, "has the cat got your tongue?") and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) == 4 then + npcHandler:say("Nice. You like your picture, haa? Give me 10,000 gold and I will deliver it to the museum. Do you {pay}?", npc, creature) npcHandler:setTopic(playerId, 2) npcHandler:setTopic(playerId, 2) elseif MsgContains(message, "pay") or MsgContains(message, "yes") then if npcHandler:getTopic(playerId) == 2 then - if (player:getMoney() + player:getBankBalance()) >= valorPicture then - npcHandler:say({ "Well done. The picture will be delivered to the museum as last as possible." }, npc, creature) + if (player:getMoney() + player:getBankBalance()) >= valuePicture then + npcHandler:say("Well done. The picture will be delivered to the museum as last as possible.", npc, creature) npcHandler:setTopic(playerId, 0) npcHandler:setTopic(playerId, 0) - player:removeMoneyBank(valorPicture) - player:setStorageValue(Storage.CultsOfTibia.MotA.Mission, 5) + player:removeMoneyBank(valuePicture) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission, 5) else - npcHandler:say({ "You don't have enough money." }, npc, creature) + npcHandler:say("You don't have enough money.", npc, creature) npcHandler:setTopic(playerId, 1) npcHandler:setTopic(playerId, 1) end @@ -88,12 +81,12 @@ local function creatureSayCallback(npc, creature, type, message) return true end +npcHandler:setMessage(MESSAGE_GREET, "Hiho Storm Killer! Welcome to Kazordoon furniture store.") npcHandler:setMessage(MESSAGE_WALKAWAY, "Well, bye then.") npcHandler:setCallback(CALLBACK_SET_INTERACTION, onAddFocus) npcHandler:setCallback(CALLBACK_REMOVE_INTERACTION, onReleaseFocus) -npcHandler:setCallback(CALLBACK_GREET, greetCallback) npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback) npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true) diff --git a/data-otservbr-global/npc/jamesfrancis.lua b/data-otservbr-global/npc/jamesfrancis.lua index bde27b5345d..bddde257b8b 100644 --- a/data-otservbr-global/npc/jamesfrancis.lua +++ b/data-otservbr-global/npc/jamesfrancis.lua @@ -23,25 +23,6 @@ npcConfig.flags = { floorchange = false, } -local function greetCallback(npc, creature) - local playerId = creature:getId() - - local player = Player(creature) - local playerId = player:getId() - - if player:getStorageValue(Storage.CultsOfTibia.Minotaurs.Access) < 1 then - npcHandler:setMessage(MESSAGE_GREET, "Gerimor is right. As an expert for minotaurs I am researching these creatures for years. I thought I already knew a lot but the monsters in this cave are {different}. It's a big {mystery}.") - npcHandler:setTopic(playerId, 1) - elseif (player:getStorageValue(Storage.CultsOfTibia.Minotaurs.JamesfrancisTask) >= 0 and player:getStorageValue(Storage.CultsOfTibia.Minotaurs.JamesfrancisTask) <= 50) and player:getStorageValue(Storage.CultsOfTibia.Minotaurs.Mission) < 3 then - npcHandler:setMessage(MESSAGE_GREET, "How is your {mission} going?") - npcHandler:setTopic(playerId, 5) - elseif player:getStorageValue(Storage.CultsOfTibia.Minotaurs.Mission) == 4 then - npcHandler:setMessage(MESSAGE_GREET, { "You say the minotaurs were controlled by a very powerful boss they worshipped. This explains why they had so much more power than the normal ones. ...", "I'm very thankful. Please go to the Druid of Crunor and tell him what you've seen. He might be interested in that." }) - player:setStorageValue(Storage.CultsOfTibia.Minotaurs.Mission, 5) - npcHandler:setTopic(playerId, 10) - end - return true -end npcConfig.voices = { interval = 15000, chance = 50, @@ -75,6 +56,28 @@ npcType.onCloseChannel = function(npc, creature) npcHandler:onCloseChannel(npc, creature) end +local function greetCallback(npc, creature) + local player = Player(creature) + local playerId = player:getId() + + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Minotaurs.Mission) < 1 then + npcHandler:setMessage(MESSAGE_GREET, "Gerimor is right. As an expert for minotaurs I am researching these creatures for years. I thought I already knew a lot but the monsters in this cave are {different}. It's a big {mystery}.") + npcHandler:setTopic(playerId, 0) + elseif player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Minotaurs.JamesfrancisTask) <= 50 and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Minotaurs.Mission) < 3 then + npcHandler:setMessage(MESSAGE_GREET, "How is your {mission} going?") + npcHandler:setTopic(playerId, 0) + elseif player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Minotaurs.Mission) == 4 then + npcHandler:setMessage(MESSAGE_GREET, { + "You say the minotaurs were controlled by a very powerful boss they worshipped. This explains why they had so much more power than the normal ones. ...", + "I'm very thankful. Please go to the Druid of Crunor and tell him what you've seen. He might be interested in that.", + }) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Minotaurs.Mission, 5) + npcHandler:setTopic(playerId, 0) + end + + return true +end + local function creatureSayCallback(npc, creature, type, message) local player = Player(creature) local playerId = player:getId() @@ -83,34 +86,30 @@ local function creatureSayCallback(npc, creature, type, message) return false end - -- Start quest - if MsgContains(message, "mystery") and npcHandler:getTopic(playerId) == 1 then - npcHandler:say({ "The minotaurs I faced in the cave are much stronger than the normal ones. What I were able to see before I had to flee: all of them seem to belong to a cult worshipping their god. Could you do me a {favour}?" }, npc, creature) + if MsgContains(message, "mystery") then + npcHandler:say("The minotaurs I faced in the cave are much stronger than the normal ones. What I were able to see before I had to flee: all of them seem to belong to a cult worshipping their god. Could you do me a {favour}?", npc, creature) npcHandler:setTopic(playerId, 2) elseif MsgContains(message, "favour") and npcHandler:getTopic(playerId) == 2 then - npcHandler:say({ "I'd like to work in this cave researching the minotaurs. But right now there are too many of hem and what is more, they are too powerful for me. Could you enter the cave and kill at least 50 of these creatures?" }, npc, creature) + npcHandler:say("I'd like to work in this cave researching the minotaurs. But right now there are too many of hem and what is more, they are too powerful for me. Could you enter the cave and kill at least 50 of these creatures?", npc, creature) npcHandler:setTopic(playerId, 3) - elseif MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 3 then - if player:getStorageValue(Storage.CultsOfTibia.Questline) < 1 then - player:setStorageValue(Storage.CultsOfTibia.Questline, 1) - end - npcHandler:say({ "Very nice. Return to me if you've finished your job." }, npc, creature) - player:setStorageValue(Storage.CultsOfTibia.Minotaurs.Mission, 2) - player:setStorageValue(Storage.CultsOfTibia.Minotaurs.JamesfrancisTask, 0) - player:setStorageValue(Storage.CultsOfTibia.Minotaurs.EntranceAccessDoor, 1) - npcHandler:setTopic(playerId, 0) - -- Delivering the quest - elseif MsgContains(message, "mission") and npcHandler:getTopic(playerId) == 5 then - if player:getStorageValue(Storage.CultsOfTibia.Minotaurs.JamesfrancisTask) >= 50 then - npcHandler:say({ "Great job! You have killed at least 50 of these monsters. I give this key to you to open the door to the inner area. Go there and find out what's going on." }, npc, creature) - player:setStorageValue(Storage.CultsOfTibia.Minotaurs.Mission, 3) - player:setStorageValue(Storage.CultsOfTibia.Minotaurs.AccessDoor, 1) + elseif MsgContains(message, "mission") then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Minotaurs.JamesfrancisTask) >= 50 then + npcHandler:say("Great job! You have killed at least 50 of these monsters. I give this key to you to open the door to the inner area. Go there and find out what's going on.", npc, creature) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Minotaurs.Mission, 3) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Minotaurs.BossAccessDoor, 1) npcHandler:setTopic(playerId, 0) else - npcHandler:say({ "Come back when you have killed enough minotaurs." }, npc, creature) + npcHandler:say("Come back when you have killed enough minotaurs.", npc, creature) npcHandler:setTopic(playerId, 0) end + elseif MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 3 then + npcHandler:say("Very nice. Return to me if you've finished your job.", npc, creature) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Minotaurs.Mission, 2) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Minotaurs.JamesfrancisTask, 0) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Minotaurs.AccessDoor, 1) + npcHandler:setTopic(playerId, 0) end + return true end diff --git a/data-otservbr-global/npc/kizar.lua b/data-otservbr-global/npc/kizar.lua new file mode 100644 index 00000000000..df58dd82d3a --- /dev/null +++ b/data-otservbr-global/npc/kizar.lua @@ -0,0 +1,56 @@ +local internalNpcName = "Kizar" +local npcType = Game.createNpcType(internalNpcName) +local npcConfig = {} + +npcConfig.name = internalNpcName +npcConfig.description = internalNpcName + +npcConfig.health = 100 +npcConfig.maxHealth = npcConfig.health +npcConfig.walkInterval = 2000 +npcConfig.walkRadius = 2 + +npcConfig.outfit = { + lookType = 132, + lookHead = 114, + lookBody = 68, + lookLegs = 19, + lookFeet = 115, + lookAddons = 0, +} + +npcConfig.flags = { + floorchange = false, +} + +local keywordHandler = KeywordHandler:new() +local npcHandler = NpcHandler:new(keywordHandler) + +npcType.onThink = function(npc, interval) + npcHandler:onThink(npc, interval) +end + +npcType.onAppear = function(npc, creature) + npcHandler:onAppear(npc, creature) +end + +npcType.onDisappear = function(npc, creature) + npcHandler:onDisappear(npc, creature) +end + +npcType.onMove = function(npc, creature, fromPosition, toPosition) + npcHandler:onMove(npc, creature, fromPosition, toPosition) +end + +npcType.onSay = function(npc, creature, type, message) + npcHandler:onSay(npc, creature, type, message) +end + +npcType.onCloseChannel = function(npc, creature) + npcHandler:onCloseChannel(npc, creature) +end + +npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true) + +-- npcType registering the npcConfig table +npcType:register(npcConfig) diff --git a/data-otservbr-global/npc/klom_stonecutter.lua b/data-otservbr-global/npc/klom_stonecutter.lua index 8f238b65a29..5c67133cc75 100644 --- a/data-otservbr-global/npc/klom_stonecutter.lua +++ b/data-otservbr-global/npc/klom_stonecutter.lua @@ -23,6 +23,15 @@ npcConfig.flags = { floorchange = false, } +npcConfig.voices = { + interval = 15000, + chance = 50, + { text = "Ah." }, + { text = "We need more volunteers!" }, + { text = 'And they call this "deep"...' }, + { text = "Preparation is paramount." }, +} + local keywordHandler = KeywordHandler:new() local npcHandler = NpcHandler:new(keywordHandler) @@ -50,51 +59,8 @@ npcType.onCloseChannel = function(npc, creature) npcHandler:onCloseChannel(npc, creature) end -npcConfig.voices = { - interval = 15000, - chance = 50, - { text = "Ah." }, - { text = "We need more volunteers!" }, - { text = 'And they call this "deep"...' }, - { text = "Preparation is paramount." }, -} - local count = {} -local function greetCallback(npc, creature) - local player = Player(creature) - local playerId = player:getId() - - if player then - npcHandler:setMessage(MESSAGE_GREET, { - "Greetings. A warning straight ahead: I don't like loiterin'. If you're not here to {help} us, you're here to waste my time. Which I consider loiterin'. Now, try and prove your {worth} to our alliance. ... ", - "I have sealed some of the areas far too dangerous for anyone to enter. If you can prove you're capable, you'll get an opportunity to help destroy the weird machines, pumping lava into the caves leading to the most dangerous enemies.", - }, 2000) - npcHandler:setTopic(playerId, 1) - end - return true -end - -keywordHandler:addKeyword({ "help" }, StdModule.say, { npcHandler = npcHandler, text = "Well, the biggest problem we need to address are the ever charging {subterraneans} around here. And on top of that, there's the threat of the Lost, who quite made themselves at {home} in these parts." }) -keywordHandler:addKeyword({ "job" }, StdModule.say, { npcHandler = npcHandler, text = "Maintainin' this whole operation, the dwarven involvement 'course. Don't know about them gnomes but if I ain't gettin' those dwarves in line, there'll be chaos down here. I also oversee the {defences} and {counterattacks}." }) -keywordHandler:addKeyword({ "defences" }, StdModule.say, { - npcHandler = npcHandler, - text = { - "The attacks of the enemy forces are fierce but we hold our ground. ... ", - "I'd love to face one of their generals in combat but as their masters they cowardly hide far behind enemy lines and I have other duties to fulfil. ... ", - "I envy you for the chance to thrust into the heart of the enemy, locking weapons with their jaws... or whatever... and see the fear in their eyes when they recognise they were bested.", - }, -}) -keywordHandler:addKeyword({ "counterattacks" }, StdModule.say, { - npcHandler = npcHandler, - text = { "I welcome a fine battle as any dwarf worth his beard should do. As long as it's a battle against something I can hit with my trusty axe. ...", "But here the true {enemy} eludes us. We fight wave after wave of their lackeys and if the gnomes are right the true enemy is up to something far more sinister. " }, -}) -keywordHandler:addKeyword({ "enemy" }, StdModule.say, { - npcHandler = npcHandler, - text = { "I have no idea what kind of creeps are behind all this. Even the gnomes don't and they have handled that stuff way more often. ...", "But even if we knew nothing more about them, the fact alone that they employ the help of those mockeries of all things dwarfish, marks them as an enemy of the dwarves and it's our obligation to annihilate them." }, -}) -keywordHandler:addKeyword({ "name" }, StdModule.say, { npcHandler = npcHandler, text = "Klom Stonecutter's the name. " }) - local function creatureSayCallback(npc, creature, type, message) local player = Player(creature) local playerId = player:getId() @@ -103,103 +69,100 @@ local function creatureSayCallback(npc, creature, type, message) return false end - local time = configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN) + local time = 20 * 60 * 60 -- 20 hours - -- missão subterraneans - if MsgContains(message, "subterraneans") and npcHandler:getTopic(playerId) == 1 then - if player:getStorageValue(Storage.DangerousDepths.Dwarves.Subterranean) == 2 and player:getStorageValue(Storage.DangerousDepths.Dwarves.TimeTaskSubterranean) > 0 then - npcHandler:say({ "I don't need your help for now. Come back later." }, npc, creature) + if MsgContains(message, "subterraneans") then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Subterranean) == 2 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.TimeTaskSubterranean) > 0 then + npcHandler:say("I don't need your help for now. Come back later.", npc, creature) npcHandler:setTopic(playerId, 1) end - if player:getStorageValue(Storage.DangerousDepths.Dwarves.Subterranean) == 2 and player:getStorageValue(Storage.DangerousDepths.Dwarves.TimeTaskSubterranean) <= 0 then - npcHandler:say({ "Vermin. Everywhere. We get a lot of strange four-legged crawlers and worms down here lately. It's getting out of hand and... well, I need a real killer for this. ", "Prepared to get rid of some seriously foul creepers for us?" }, npc, creature) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Subterranean) == 2 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.TimeTaskSubterranean) <= 0 then + npcHandler:say({ + "Vermin. Everywhere. We get a lot of strange four-legged crawlers and worms down here lately. It's getting out of hand and... well, I need a real killer for this. ", + "Prepared to get rid of some seriously foul creepers for us?", + }, npc, creature) npcHandler:setTopic(playerId, 2) end - if player:getStorageValue(Storage.DangerousDepths.Dwarves.Subterranean) < 1 then -- Não possuía a missão, agora possui! - npcHandler:say({ "Vermin. Everywhere. We get a lot of strange four-legged crawlers and worms down here lately. It's getting out of hand and... well, I need a real killer for this. ", "Prepared to get rid of some seriously foul creepers for us?" }, npc, creature) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Subterranean) < 1 then + npcHandler:say({ + "Vermin. Everywhere. We get a lot of strange four-legged crawlers and worms down here lately. It's getting out of hand and... well, I need a real killer for this. ", + "Prepared to get rid of some seriously foul creepers for us?", + }, npc, creature) npcHandler:setTopic(playerId, 2) - elseif (player:getStorageValue(Storage.DangerousDepths.Dwarves.Subterranean) == 1) and (player:getStorageValue(Storage.DangerousDepths.Dwarves.Organisms) < 50) then -- Está na missão porém não terminou a task! - npcHandler:say({ "Come back when you have finished your job." }, npc, creature) + elseif (player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Subterranean) == 1) and (player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Organisms) < 50) then + npcHandler:say("Come back when you have finished your job.", npc, creature) npcHandler:setTopic(playerId, 1) - elseif (player:getStorageValue(Storage.DangerousDepths.Dwarves.Subterranean) == 1) and (player:getStorageValue(Storage.DangerousDepths.Dwarves.Organisms) >= 50) then - npcHandler:say({ "I'l say I'm blown away but a Klom Stonecutter is not that easily impressed. Still, your got your hands dirt for us and I appreciate that." }, npc, creature) - -- Entregando surprise jar + 1 ponto de missão! - player:setStorageValue(Storage.DangerousDepths.Dwarves.TimeTaskSubterranean, os.time() + time) + elseif (player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Subterranean) == 1) and (player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Organisms) >= 50) then + npcHandler:say("I'l say I'm blown away but a Klom Stonecutter is not that easily impressed. Still, your got your hands dirt for us and I appreciate that.", npc, creature) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.TimeTaskSubterranean, os.time() + time) player:addItem(27654, 1) - player:setStorageValue(Storage.DangerousDepths.Dwarves.Status, player:getStorageValue(Storage.DangerousDepths.Dwarves.Status) + 1) - player:setStorageValue(Storage.DangerousDepths.Dwarves.Subterranean, 2) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Points) + 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Subterranean, 2) npcHandler:setTopic(playerId, 1) end elseif npcHandler:getTopic(playerId) == 2 and MsgContains(message, "yes") then - npcHandler:say({ "Alright, good. Those things are strolling about and I ain't gonna have that. If it moves more than two legs, destroy it. If it moves legs and tentacles, destroy it again." }, npc, creature) - if player:getStorageValue(Storage.DangerousDepths.Questline) < 1 then - player:setStorageValue(Storage.DangerousDepths.Questline, 1) + npcHandler:say("Alright, good. Those things are strolling about and I ain't gonna have that. If it moves more than two legs, destroy it. If it moves legs and tentacles, destroy it again.", npc, creature) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Questline) < 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Questline, 1) end - player:setStorageValue(Storage.DangerousDepths.Dwarves.Subterranean, 1) - player:setStorageValue(Storage.DangerousDepths.Dwarves.Organisms, 0) -- Garantindo que a task não inicie com -1 - npcHandler:setTopic(playerId, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Subterranean, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Organisms, 0) npcHandler:setTopic(playerId, 1) end - -- missão home - if MsgContains(message, "home") and npcHandler:getTopic(playerId) == 1 then - if player:getStorageValue(Storage.DangerousDepths.Dwarves.Home) == 2 and player:getStorageValue(Storage.DangerousDepths.Dwarves.TimeTaskHome) > 0 then - npcHandler:say({ "I don't need your help for now. Come back later." }, npc, creature) + if MsgContains(message, "home") then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Home) == 2 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.TimeTaskHome) > 0 then + npcHandler:say("I don't need your help for now. Come back later.", npc, creature) npcHandler:setTopic(playerId, 1) end - if player:getStorageValue(Storage.DangerousDepths.Dwarves.Home) == 2 and player:getStorageValue(Storage.DangerousDepths.Dwarves.TimeTaskHome) <= 0 then - npcHandler:say( - { "We need to find a way to drive off the exiles from these caves. Countless makeshift homes are popping up at every corner. Destroy them and get the Lost out of hiding to eliminate them. ... ", "If you can capture a few of them, you'll receive a bonus. Just bring 'em to the border of our outpost and we will take care of the rest. ... ", "Are you ready for that? " }, - npc, - creature - ) - npcHandler:setTopic(playerId, 22) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Home) == 2 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.TimeTaskHome) <= 0 then + npcHandler:say({ + "We need to find a way to drive off the exiles from these caves. Countless makeshift homes are popping up at every corner. Destroy them and get the Lost out of hiding to eliminate them. ... ", + "If you can capture a few of them, you'll receive a bonus. Just bring 'em to the border of our outpost and we will take care of the rest. ... ", + "Are you ready for that? ", + }, npc, creature) npcHandler:setTopic(playerId, 22) end - if player:getStorageValue(Storage.DangerousDepths.Dwarves.Home) < 1 then -- Não possuía a missão, agora possui! - npcHandler:say( - { "We need to find a way to drive off the exiles from these caves. Countless makeshift homes are popping up at every corner. Destroy them and get the Lost out of hiding to eliminate them. ... ", "If you can capture a few of them, you'll receive a bonus. Just bring 'em to the border of our outpost and we will take care of the rest. ... ", "Are you ready for that? " }, - npc, - creature - ) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Home) < 1 then + npcHandler:say({ + "We need to find a way to drive off the exiles from these caves. Countless makeshift homes are popping up at every corner. Destroy them and get the Lost out of hiding to eliminate them. ... ", + "If you can capture a few of them, you'll receive a bonus. Just bring 'em to the border of our outpost and we will take care of the rest. ... ", + "Are you ready for that? ", + }, npc, creature) npcHandler:setTopic(playerId, 22) - npcHandler:setTopic(playerId, 22) - elseif (player:getStorageValue(Storage.DangerousDepths.Dwarves.Home) == 1) and (player:getStorageValue(Storage.DangerousDepths.Dwarves.LostExiles) < 20 and player:getStorageValue(Storage.DangerousDepths.Dwarves.Prisoners) < 3) then -- Está na missão porém não terminou nenhuma das tasks! - npcHandler:say({ "Come back when you have finished your job." }, npc, creature) + elseif (player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Home) == 1) and (player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.LostExiles) < 20 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Prisoners) < 3) then + npcHandler:say("Come back when you have finished your job.", npc, creature) npcHandler:setTopic(playerId, 1) - elseif (player:getStorageValue(Storage.DangerousDepths.Dwarves.Home) == 1) and (player:getStorageValue(Storage.DangerousDepths.Dwarves.LostExiles) >= 20 and player:getStorageValue(Storage.DangerousDepths.Dwarves.Prisoners) < 3) then - npcHandler:say({ "So you did it. Well, that won't be the last of 'em but this sure helps our situation down here. Return to me later if you want to help me again!" }, npc, creature) -- Caso não tenha feito o task bônus - -- Entregando surprise jar + 1 ponto de missão! - player:setStorageValue(Storage.DangerousDepths.Dwarves.TimeTaskHome, os.time() + time) - player:setStorageValue(Storage.DangerousDepths.Dwarves.Home, 2) + elseif (player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Home) == 1) and (player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.LostExiles) >= 20 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Prisoners) < 3) then + npcHandler:say("So you did it. Well, that won't be the last of 'em but this sure helps our situation down here. Return to me later if you want to help me again!", npc, creature) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.TimeTaskHome, os.time() + time) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Home, 2) player:addItem(27654, 1) - player:setStorageValue(Storage.DangerousDepths.Dwarves.Status, player:getStorageValue(Storage.DangerousDepths.Dwarves.Status) + 1) - npcHandler:setTopic(playerId, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Points) + 1) npcHandler:setTopic(playerId, 1) - elseif (player:getStorageValue(Storage.DangerousDepths.Dwarves.Home) == 1) and (player:getStorageValue(Storage.DangerousDepths.Dwarves.LostExiles) >= 20 and player:getStorageValue(Storage.DangerousDepths.Dwarves.Prisoners) >= 3) then - npcHandler:say({ "So you did it. And you even made prisoners, the bonus is yours! Well, that won't be the last of 'em but this sure helps our situation down here. Return to me later if you want to help me again!" }, npc, creature) -- Se tiver feito ambas - -- Entregando 2 surprise jars + 2 pontos de missão! - player:setStorageValue(Storage.DangerousDepths.Dwarves.TimeTaskHome, os.time() + time) - player:setStorageValue(Storage.DangerousDepths.Dwarves.Home, 2) - player:addItem(27654, 2) -- +1 item pela task bônus! - player:setStorageValue(Storage.DangerousDepths.Dwarves.Status, player:getStorageValue(Storage.DangerousDepths.Dwarves.Status) + 2) -- +1 ponto pela task bônus! + elseif (player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Home) == 1) and (player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.LostExiles) >= 20 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Prisoners) >= 3) then + npcHandler:say("So you did it. And you even made prisoners, the bonus is yours! Well, that won't be the last of 'em but this sure helps our situation down here. Return to me later if you want to help me again!", npc, creature) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.TimeTaskHome, os.time() + time) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Home, 2) + player:addItem(27654, 2) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Points) + 2) npcHandler:setTopic(playerId, 1) end elseif npcHandler:getTopic(playerId) == 22 and MsgContains(message, "yes") then - npcHandler:say({ "Very well, now try to find some of their makeshift homes and tear'em down. There's bound to be some stragglers you can 'persuade' to surrender, eliminate any resistance. Get back here when you're done." }, npc, creature) - if player:getStorageValue(Storage.DangerousDepths.Questline) < 1 then - player:setStorageValue(Storage.DangerousDepths.Questline, 1) + npcHandler:say("Very well, now try to find some of their makeshift homes and tear'em down. There's bound to be some stragglers you can 'persuade' to surrender, eliminate any resistance. Get back here when you're done.", npc, creature) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Questline) < 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Questline, 1) end - player:setStorageValue(Storage.DangerousDepths.Dwarves.Home, 1) - player:setStorageValue(Storage.DangerousDepths.Dwarves.LostExiles, 0) -- Garantindo que a task não inicie com -1 - player:setStorageValue(Storage.DangerousDepths.Dwarves.Prisoners, 0) -- Garantindo que a task não inicie com -1 - npcHandler:setTopic(playerId, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Home, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.LostExiles, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Prisoners, 0) npcHandler:setTopic(playerId, 1) end local plural = "" + if MsgContains(message, "suspicious devices") or MsgContains(message, "suspicious device") then - npcHandler:say({ "If you bring me any suspicious devices on creatures you slay down here, I'll make it worth your while by telling the others of your generosity. How many do you want to offer? " }, npc, creature) + npcHandler:say("If you bring me any suspicious devices on creatures you slay down here, I'll make it worth your while by telling the others of your generosity. How many do you want to offer?", npc, creature) npcHandler:setTopic(playerId, 55) elseif npcHandler:getTopic(playerId) == 55 then count[playerId] = tonumber(message) @@ -207,68 +170,97 @@ local function creatureSayCallback(npc, creature, type, message) if count[playerId] > 1 then plural = plural .. "s" end - npcHandler:say({ "You want to offer " .. count[playerId] .. " suspicious device" .. plural .. ". Which leader shall have it, (Gnomus) of the {gnomes}, (Klom Stonecutter) of the {dwarves} or the {scouts} (Lardoc Bashsmite)?" }, npc, creature) + npcHandler:say("You want to offer " .. count[playerId] .. " suspicious device" .. plural .. ". Which leader shall have it, (Gnomus) of the {gnomes}, (Klom Stonecutter) of the {dwarves} or the {scouts} (Lardoc Bashsmite)?", npc, creature) npcHandler:setTopic(playerId, 56) else - npcHandler:say({ "Don't waste my time." }, npc, creature) + npcHandler:say("Don't waste my time.", npc, creature) npcHandler:setTopic(playerId, 1) end elseif MsgContains(message, "gnomes") and npcHandler:getTopic(playerId) == 56 then if player:getItemCount(30888) >= count[playerId] then - npcHandler:say({ "Done." }, npc, creature) + npcHandler:say("Done.", npc, creature) if count[playerId] > 1 then plural = plural .. "s" end player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You earned " .. count[playerId] .. " point" .. plural .. " on the gnomes mission.") player:removeItem(30888, count[playerId]) - player:setStorageValue(Storage.DangerousDepths.Gnomes.Status, player:getStorageValue(Storage.DangerousDepths.Gnomes.Status) + count[playerId]) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points) + count[playerId]) else - npcHandler:say({ "You don't have enough suspicious devices." }, npc, creature) + npcHandler:say("You don't have enough suspicious devices.", npc, creature) npcHandler:setTopic(playerId, 1) end elseif MsgContains(message, "dwarves") and npcHandler:getTopic(playerId) == 56 then if player:getItemCount(30888) >= count[playerId] then - npcHandler:say({ "Done." }, npc, creature) + npcHandler:say("Done.", npc, creature) if count[playerId] > 1 then plural = plural .. "s" end player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You earned " .. count[playerId] .. " point" .. plural .. " on the dwarves mission.") player:removeItem(30888, count[playerId]) - player:setStorageValue(Storage.DangerousDepths.Dwarves.Status, player:getStorageValue(Storage.DangerousDepths.Dwarves.Status) + count[playerId]) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Points) + count[playerId]) else - npcHandler:say({ "You don't have enough suspicious devices." }, npc, creature) + npcHandler:say("You don't have enough suspicious devices.", npc, creature) npcHandler:setTopic(playerId, 1) end elseif MsgContains(message, "scouts") and npcHandler:getTopic(playerId) == 56 then if player:getItemCount(30888) >= count[playerId] then - npcHandler:say({ "Done." }, npc, creature) + npcHandler:say("Done.", npc, creature) if count[playerId] > 1 then plural = plural .. "s" end player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You earned " .. count[playerId] .. " point" .. plural .. " on the scouts mission.") player:removeItem(30888, count[playerId]) - player:setStorageValue(Storage.DangerousDepths.Scouts.Status, player:getStorageValue(Storage.DangerousDepths.Scouts.Status) + count[playerId]) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Points) + count[playerId]) else - npcHandler:say({ "You don't have enough suspicious devices." }, npc, creature) + npcHandler:say("You don't have enough suspicious devices.", npc, creature) npcHandler:setTopic(playerId, 1) end end - -- Início checagem de pontos de tasks!! if MsgContains(message, "status") then - npcHandler:say({ "So you want to know what we all think about your deeds? What leader's opinion are you interested in, the {gnomes} (Gnomus), the {dwarves} (Klom Stonecutter) or the {scouts} (Lardoc Bashsmite)?" }, npc, creature) - npcHandler:setTopic(playerId, 5) + npcHandler:say("So you want to know what we all think about your deeds? What leader's opinion are you interested in, the {gnomes} (Gnomus), the {dwarves} (Klom Stonecutter) or the {scouts} (Lardoc Bashsmite)?", npc, creature) npcHandler:setTopic(playerId, 5) elseif MsgContains(message, "gnomes") and npcHandler:getTopic(playerId) == 5 then - npcHandler:say({ "The gnomes are still in need of your help, member of Bigfoot's Brigade. Prove your worth by answering their calls! (" .. math.max(player:getStorageValue(Storage.DangerousDepths.Gnomes.Status), 0) .. "/10)" }, npc, creature) + npcHandler:say("The gnomes are still in need of your help, member of Bigfoot's Brigade. Prove your worth by answering their calls! (" .. math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points), 0) .. "/10)", npc, creature) elseif MsgContains(message, "dwarves") and npcHandler:getTopic(playerId) == 5 then - npcHandler:say({ "The dwarves are still in need of your help, member of Bigfoot's Brigade. Prove your worth by answering their calls! (" .. math.max(player:getStorageValue(Storage.DangerousDepths.Dwarves.Status), 0) .. "/10)" }, npc, creature) + npcHandler:say("The dwarves are still in need of your help, member of Bigfoot's Brigade. Prove your worth by answering their calls! (" .. math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Points), 0) .. "/10)", npc, creature) elseif MsgContains(message, "scouts") and npcHandler:getTopic(playerId) == 5 then - npcHandler:say({ "The scouts are still in need of your help, member of Bigfoot's Brigade. Prove your worth by answering their calls! (" .. math.max(player:getStorageValue(Storage.DangerousDepths.Scouts.Status), 0) .. "/10)" }, npc, creature) + npcHandler:say("The scouts are still in need of your help, member of Bigfoot's Brigade. Prove your worth by answering their calls! (" .. math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Points), 0) .. "/10)", npc, creature) end + return true end +keywordHandler:addKeyword({ "help" }, StdModule.say, { npcHandler = npcHandler, text = "Well, the biggest problem we need to address are the ever charging {subterraneans} around here. And on top of that, there's the threat of the Lost, who quite made themselves at {home} in these parts." }) +keywordHandler:addKeyword({ "job" }, StdModule.say, { npcHandler = npcHandler, text = "Maintainin' this whole operation, the dwarven involvement 'course. Don't know about them gnomes but if I ain't gettin' those dwarves in line, there'll be chaos down here. I also oversee the {defences} and {counterattacks}." }) +keywordHandler:addKeyword({ "defences" }, StdModule.say, { + npcHandler = npcHandler, + text = { + "The attacks of the enemy forces are fierce but we hold our ground. ... ", + "I'd love to face one of their generals in combat but as their masters they cowardly hide far behind enemy lines and I have other duties to fulfil. ... ", + "I envy you for the chance to thrust into the heart of the enemy, locking weapons with their jaws... or whatever... and see the fear in their eyes when they recognise they were bested.", + }, +}) +keywordHandler:addKeyword({ "counterattacks" }, StdModule.say, { + npcHandler = npcHandler, + text = { + "I welcome a fine battle as any dwarf worth his beard should do. As long as it's a battle against something I can hit with my trusty axe. ...", + "But here the true {enemy} eludes us. We fight wave after wave of their lackeys and if the gnomes are right the true enemy is up to something far more sinister. ", + }, +}) +keywordHandler:addKeyword({ "enemy" }, StdModule.say, { + npcHandler = npcHandler, + text = { + "I have no idea what kind of creeps are behind all this. Even the gnomes don't and they have handled that stuff way more often. ...", + "But even if we knew nothing more about them, the fact alone that they employ the help of those mockeries of all things dwarfish, marks them as an enemy of the dwarves and it's our obligation to annihilate them.", + }, +}) +keywordHandler:addKeyword({ "name" }, StdModule.say, { npcHandler = npcHandler, text = "Klom Stonecutter's the name. " }) + +npcHandler:setMessage(MESSAGE_GREET, { + "Greetings. A warning straight ahead: I don't like loiterin'. If you're not here to {help} us, you're here to waste my time. Which I consider loiterin'. Now, try and prove your {worth} to our alliance. ... ", + "I have sealed some of the areas far too dangerous for anyone to enter. If you can prove you're capable, you'll get an opportunity to help destroy the weird machines, pumping lava into the caves leading to the most dangerous enemies.", +}) npcHandler:setMessage(MESSAGE_WALKAWAY, "Well, bye then.") npcHandler:setCallback(CALLBACK_SET_INTERACTION, onAddFocus) diff --git a/data-otservbr-global/npc/lardoc_bashsmite.lua b/data-otservbr-global/npc/lardoc_bashsmite.lua index 79fbd410d6e..11367640ded 100644 --- a/data-otservbr-global/npc/lardoc_bashsmite.lua +++ b/data-otservbr-global/npc/lardoc_bashsmite.lua @@ -50,37 +50,7 @@ npcType.onCloseChannel = function(npc, creature) npcHandler:onCloseChannel(npc, creature) end -local quantidade = {} - -local function greetCallback(npc, creature) - local player = Player(creature) - local playerId = player:getId() - - if player then - npcHandler:setMessage(MESSAGE_GREET, { "Since you are obviously not one of my relatives who pays me a long overdue visit, we should get {work} right away. We'll see if you can prove your {worth} to our alliance." }) - npcHandler:setTopic(playerId, 1) - end - return true -end - -keywordHandler:addKeyword({ "work" }, StdModule.say, { npcHandler = npcHandler, text = "Here's the situation: a tide of hazardous bugs, the gnomes named them {diremaws}, threatens our base. Then there is some kind of {growth} which seems connected to them somehow. We need to get rid of both." }) -keywordHandler:addKeyword({ "worth" }, StdModule.say, { - npcHandler = npcHandler, - text = { - "You're already known amongst the gnomes, member of the Bigfoot Brigade. I will make sure that the alliance learns of your deeds but you'll still need to help the dwarves and gnomes of this outpost to show your worth. ...", - "We also found suspicious devices carried by all kinds of creatures down here. Down here, they are of extreme worth to us since they could contain the key to what's happening all around us. ... ", - "If you can aquire any, return them to me and I make sure to tell the others of your generosity. Return to me afterwards to check on your current {status}.", - }, -}) -keywordHandler:addKeyword({ "job" }, StdModule.say, { - npcHandler = npcHandler, - text = { - "Leading the charge! It's a war down here. I maintain the outer defences and supplies which are organised back in Kazordoon and also by the gnomes. ...", - "I have sealed some of the areas far too dangerous for anyone to enter. If you can prove you're capable, you'll get an oportunity to help destroying the weird machines, pumping lava into the caves leading to the most dangerous enemies.", - "But even if we knew nothing more about them, the fact alone that they employ the help of those mockeries of all things dwarfish, marks them as an enemy of the dwarves and it's our obligation to annihilate them.", - }, -}) -keywordHandler:addKeyword({ "name" }, StdModule.say, { npcHandler = npcHandler, text = "Bashsmite. That's all you need to know." }) +local amount = {} local function creatureSayCallback(npc, creature, type, message) local player = Player(creature) @@ -90,15 +60,14 @@ local function creatureSayCallback(npc, creature, type, message) return false end - local tempo = configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN) + local time = 20 * 60 * 60 -- 20 hours - -- missão diremaws - if MsgContains(message, "diremaws") and npcHandler:getTopic(playerId) == 1 then - if player:getStorageValue(Storage.DangerousDepths.Scouts.Diremaw) == 2 and player:getStorageValue(Storage.DangerousDepths.Scouts.TimeTaskDiremaws) > 0 then -- Ainda não se passaram as 20h - npcHandler:say({ "I don't need your help for now. Come back later." }, npc, creature) + if MsgContains(message, "diremaws") then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Diremaw) == 2 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.TimeTaskDiremaws) > 0 then + npcHandler:say("I don't need your help for now. Come back later.", npc, creature) npcHandler:setTopic(playerId, 1) end - if player:getStorageValue(Storage.DangerousDepths.Scouts.Diremaw) == 2 and player:getStorageValue(Storage.DangerousDepths.Scouts.TimeTaskDiremaws) <= 0 then -- Vai fazer a missão após 20h + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Diremaw) == 2 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.TimeTaskDiremaws) <= 0 then npcHandler:say({ "The gnomes say that these creatures seem to thrive on mushroom sponges. But not only that, apparently they also lay eggs in their own cadavers to breed even faster. ...", "Yes, disgusting. I guess it provides everything their offspring needs... well, we probably shouldn't go into that much further. Instead we should focus on preventing that from happening. ... ", @@ -108,7 +77,7 @@ local function creatureSayCallback(npc, creature, type, message) }, npc, creature) npcHandler:setTopic(playerId, 2) end - if player:getStorageValue(Storage.DangerousDepths.Scouts.Diremaw) < 1 then -- Não possuía a missão, agora possui! + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Diremaw) < 1 then npcHandler:say({ "The gnomes say that these creatures seem to thrive on mushroom sponges. But not only that, apparently they also lay eggs in their own cadavers to breed even faster. ...", "Yes, disgusting. I guess it provides everything their offspring needs... well, we probably shouldn't go into that much further. Instead we should focus on preventing that from happening. ... ", @@ -117,36 +86,34 @@ local function creatureSayCallback(npc, creature, type, message) "Are you willing to help? ", }, npc, creature) npcHandler:setTopic(playerId, 2) - elseif (player:getStorageValue(Storage.DangerousDepths.Scouts.Diremaw) == 1) and (player:getStorageValue(Storage.DangerousDepths.Scouts.DiremawsCount) < 20) then -- Está na missão porém não terminou a task! - npcHandler:say({ "Come back when you have finished your job." }, npc, creature) + elseif (player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Diremaw) == 1) and (player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.DiremawsCount) < 20) then + npcHandler:say("Come back when you have finished your job.", npc, creature) npcHandler:setTopic(playerId, 1) - elseif player:getStorageValue(Storage.DangerousDepths.Scouts.Diremaw) == 1 and player:getStorageValue(Storage.DangerousDepths.Scouts.DiremawsCount) >= 20 then -- Não possuía a missão, agora possui! - npcHandler:say({ "You got rid of a lot of corpses, very good. Now we have a realistic chance of pushing them back! Return to me later for more work if you want." }, npc, creature) - player:setStorageValue(Storage.DangerousDepths.Dwarves.TimeTaskDiremaws, os.time() + tempo) + elseif player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Diremaw) == 1 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.DiremawsCount) >= 20 then + npcHandler:say("You got rid of a lot of corpses, very good. Now we have a realistic chance of pushing them back! Return to me later for more work if you want.", npc, creature) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.TimeTaskDiremaws, os.time() + time) player:addItem(27654, 1) - player:setStorageValue(Storage.DangerousDepths.Scouts.Status, player:getStorageValue(Storage.DangerousDepths.Scouts.Status) + 1) - player:setStorageValue(Storage.DangerousDepths.Scouts.Diremaw, 2) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Points) + 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Diremaw, 2) npcHandler:setTopic(playerId, 1) end elseif npcHandler:getTopic(playerId) == 2 and MsgContains(message, "yes") then - npcHandler:say({ "Noted. Get one of the gnomish devices from the container next to me and 'use' it on any diremaw corpses to effectively neutralise them. At least the gnomes told me this will work - good luck!" }, npc, creature) - if player:getStorageValue(Storage.DangerousDepths.Questline) < 1 then - player:setStorageValue(Storage.DangerousDepths.Questline, 1) + npcHandler:say("Noted. Get one of the gnomish devices from the container next to me and 'use' it on any diremaw corpses to effectively neutralise them. At least the gnomes told me this will work - good luck!", npc, creature) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Questline) < 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Questline, 1) end - player:setStorageValue(Storage.DangerousDepths.Scouts.Diremaw, 1) - player:setStorageValue(Storage.DangerousDepths.Scouts.GnomishChest, 1) -- Permissão para usar o baú - player:setStorageValue(Storage.DangerousDepths.Scouts.DiremawsCount, 0) -- Garantindo que a task não inicie com -1 - npcHandler:setTopic(playerId, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Diremaw, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.GnomishChest, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.DiremawsCount, 0) npcHandler:setTopic(playerId, 1) end - -- missão growth - if MsgContains(message, "growth") and npcHandler:getTopic(playerId) == 1 then - if player:getStorageValue(Storage.DangerousDepths.Scouts.Growth) == 2 and player:getStorageValue(Storage.DangerousDepths.Scouts.TimeTaskGrowth) > 0 then -- Ainda não se passaram as 20h - npcHandler:say({ "I don't need your help for now. Come back later." }, npc, creature) + if MsgContains(message, "growth") then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Growth) == 2 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.TimeTaskGrowth) > 0 then + npcHandler:say("I don't need your help for now. Come back later.", npc, creature) npcHandler:setTopic(playerId, 1) end - if player:getStorageValue(Storage.DangerousDepths.Scouts.Growth) == 2 and player:getStorageValue(Storage.DangerousDepths.Scouts.TimeTaskGrowth) <= 0 then -- Vai fazer a missão após 20h + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Growth) == 2 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.TimeTaskGrowth) <= 0 then npcHandler:say({ "I can't explain that stuff, even the gnomes don't know what grows in those caves. All we know is that this stuff brought about all the diremaws we are now facing. ...", "This vermin is somehow attracted to this sponge, my guess is they use it as a nourishment, too. We need to get rid of the stuff regularly to reduce their numbers to a manageable level. ...", @@ -156,9 +123,8 @@ local function creatureSayCallback(npc, creature, type, message) "We need someone to take those barrels to the source of the ever growing sponge and burn it down. All of it. There should be at least five locations. Are you up for that? ", }, npc, creature) npcHandler:setTopic(playerId, 22) - npcHandler:setTopic(playerId, 22) end - if player:getStorageValue(Storage.DangerousDepths.Scouts.Growth) < 1 then -- Não possuía a missão, agora possui! + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Growth) < 1 then npcHandler:say({ "I can't explain that stuff, even the gnomes don't know what grows in those caves. All we know is that this stuff brought about all the diremaws we are now facing. ...", "This vermin is somehow attracted to this sponge, my guess is they use it as a nourishment, too. We need to get rid of the stuff regularly to reduce their numbers to a manageable level. ...", @@ -168,109 +134,129 @@ local function creatureSayCallback(npc, creature, type, message) "We need someone to take those barrels to the source of the ever growing sponge and burn it down. All of it. There should be at least five locations. Are you up for that? ", }, npc, creature) npcHandler:setTopic(playerId, 22) - npcHandler:setTopic(playerId, 22) end - if player:getStorageValue(Storage.DangerousDepths.Scouts.Growth) == 1 and player:getStorageValue(Storage.DangerousDepths.Scouts.BarrelCount) >= 3 then -- Não possuía a missão, agora possui! - npcHandler:say({ "You did a great job out there, the stuff will continue to grow, however. Return to me later for more work." }, npc, creature) - player:setStorageValue(Storage.DangerousDepths.Dwarves.TimeTaskGrowth, os.time() + tempo) - if player:getStorageValue(Storage.DangerousDepths.Scouts.BarrelCount) >= 5 then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Growth) == 1 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.BarrelCount) >= 3 then + npcHandler:say("You did a great job out there, the stuff will continue to grow, however. Return to me later for more work.", npc, creature) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.TimeTaskGrowth, os.time() + time) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.BarrelCount) >= 5 then player:addItem(27654, 2) - player:setStorageValue(Storage.DangerousDepths.Scouts.Status, player:getStorageValue(Storage.DangerousDepths.Scouts.Status) + 2) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Points) + 2) else player:addItem(27654, 1) - player:setStorageValue(Storage.DangerousDepths.Scouts.Status, player:getStorageValue(Storage.DangerousDepths.Scouts.Status) + 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Points) + 1) end - player:setStorageValue(Storage.DangerousDepths.Scouts.Growth, 2) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Growth, 2) npcHandler:setTopic(playerId, 1) end elseif npcHandler:getTopic(playerId) == 22 and MsgContains(message, "yes") then - npcHandler:say({ "Noted. Now, get down there and rain some fire on them." }, npc, creature) - if player:getStorageValue(Storage.DangerousDepths.Questline) < 1 then - player:setStorageValue(Storage.DangerousDepths.Questline, 1) + npcHandler:say("Noted. Now, get down there and rain some fire on them.", npc, creature) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Questline) < 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Questline, 1) end - player:setStorageValue(Storage.DangerousDepths.Scouts.Growth, 1) - player:setStorageValue(Storage.DangerousDepths.Scouts.BarrelCount, 0) -- Garantindo que a task não inicie com -1 - player:setStorageValue(Storage.DangerousDepths.Scouts.FirstBarrel, 0) -- Garantindo que a task não inicie com -1 - player:setStorageValue(Storage.DangerousDepths.Scouts.SecondBarrel, 0) -- Garantindo que a task não inicie com -1 - player:setStorageValue(Storage.DangerousDepths.Scouts.ThirdBarrel, 0) -- Garantindo que a task não inicie com -1 - player:setStorageValue(Storage.DangerousDepths.Scouts.FourthBarrel, 0) -- Garantindo que a task não inicie com -1 - player:setStorageValue(Storage.DangerousDepths.Scouts.FifthBarrel, 0) -- Garantindo que a task não inicie com -1 + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Growth, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.BarrelCount, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.FirstBarrel, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.SecondBarrel, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.ThirdBarrel, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.FourthBarrel, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.FifthBarrel, 0) npcHandler:setTopic(playerId, 1) npcHandler:setTopic(playerId, 1) end + local plural = "" + if MsgContains(message, "suspicious devices") or MsgContains(message, "suspicious device") then - npcHandler:say({ "If you bring me any suspicious devices on creatures you slay down here, I'll make it worth your while by telling the others of your generosity. How many do you want to offer? " }, npc, creature) + npcHandler:say("If you bring me any suspicious devices on creatures you slay down here, I'll make it worth your while by telling the others of your generosity. How many do you want to offer? ", npc, creature) npcHandler:setTopic(playerId, 55) elseif npcHandler:getTopic(playerId) == 55 then - quantidade[playerId] = tonumber(message) - if quantidade[playerId] then - if quantidade[playerId] > 1 then + amount[playerId] = tonumber(message) + if amount[playerId] then + if amount[playerId] > 1 then plural = plural .. "s" end - npcHandler:say({ "You want to offer " .. quantidade[playerId] .. " suspicious device" .. plural .. ". Which leader shall have it, (Gnomus) of the {gnomes}, (Klom Stonecutter) of the {dwarves} or the {scouts} (Lardoc Bashsmite)?" }, npc, creature) + npcHandler:say("You want to offer " .. amount[playerId] .. " suspicious device" .. plural .. ". Which leader shall have it, (Gnomus) of the {gnomes}, (Klom Stonecutter) of the {dwarves} or the {scouts} (Lardoc Bashsmite)?", npc, creature) npcHandler:setTopic(playerId, 56) else - npcHandler:say({ "Don't waste my time." }, npc, creature) + npcHandler:say("Don't waste my time.", npc, creature) npcHandler:setTopic(playerId, 1) end elseif MsgContains(message, "gnomes") and npcHandler:getTopic(playerId) == 56 then - if player:getItemCount(30888) >= quantidade[playerId] then - npcHandler:say({ "Done." }, npc, creature) - if quantidade[playerId] > 1 then + if player:getItemCount(30888) >= amount[playerId] then + npcHandler:say("Done.", npc, creature) + if amount[playerId] > 1 then plural = plural .. "s" end - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You earned " .. quantidade[playerId] .. " point" .. plural .. " on the gnomes mission.") - player:removeItem(30888, quantidade[playerId]) - player:setStorageValue(Storage.DangerousDepths.Gnomes.Status, player:getStorageValue(Storage.DangerousDepths.Gnomes.Status) + quantidade[playerId]) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You earned " .. amount[playerId] .. " point" .. plural .. " on the gnomes mission.") + player:removeItem(30888, amount[playerId]) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points) + amount[playerId]) else - npcHandler:say({ "You don't have enough suspicious devices." }, npc, creature) + npcHandler:say("You don't have enough suspicious devices.", npc, creature) npcHandler:setTopic(playerId, 1) end elseif MsgContains(message, "dwarves") and npcHandler:getTopic(playerId) == 56 then - if player:getItemCount(30888) >= quantidade[playerId] then - npcHandler:say({ "Done." }, npc, creature) - if quantidade[playerId] > 1 then + if player:getItemCount(30888) >= amount[playerId] then + npcHandler:say("Done.", npc, creature) + if amount[playerId] > 1 then plural = plural .. "s" end - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You earned " .. quantidade[playerId] .. " point" .. plural .. " on the dwarves mission.") - player:removeItem(30888, quantidade[playerId]) - player:setStorageValue(Storage.DangerousDepths.Dwarves.Status, player:getStorageValue(Storage.DangerousDepths.Dwarves.Status) + quantidade[playerId]) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You earned " .. amount[playerId] .. " point" .. plural .. " on the dwarves mission.") + player:removeItem(30888, amount[playerId]) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Points) + amount[playerId]) else - npcHandler:say({ "You don't have enough suspicious devices." }, npc, creature) + npcHandler:say("You don't have enough suspicious devices.", npc, creature) npcHandler:setTopic(playerId, 1) end elseif MsgContains(message, "scouts") and npcHandler:getTopic(playerId) == 56 then - if player:getItemCount(30888) >= quantidade[playerId] then - npcHandler:say({ "Done." }, npc, creature) - if quantidade[playerId] > 1 then + if player:getItemCount(30888) >= amount[playerId] then + npcHandler:say("Done.", npc, creature) + if amount[playerId] > 1 then plural = plural .. "s" end - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You earned " .. quantidade[playerId] .. " point" .. plural .. " on the scouts mission.") - player:removeItem(30888, quantidade[playerId]) - player:setStorageValue(Storage.DangerousDepths.Scouts.Status, player:getStorageValue(Storage.DangerousDepths.Scouts.Status) + quantidade[playerId]) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You earned " .. amount[playerId] .. " point" .. plural .. " on the scouts mission.") + player:removeItem(30888, amount[playerId]) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Points, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Points) + amount[playerId]) else - npcHandler:say({ "You don't have enough suspicious devices." }, npc, creature) + npcHandler:say("You don't have enough suspicious devices.", npc, creature) npcHandler:setTopic(playerId, 1) end end - -- Início checagem de pontos de tasks!! if MsgContains(message, "status") then - npcHandler:say({ "So you want to know what we all think about your deeds? What leader's opinion are you interested in, the {gnomes} (Gnomus), the {dwarves} (Klom Stonecutter) or the {scouts} (Lardoc Bashsmite)?" }, npc, creature) - npcHandler:setTopic(playerId, 5) + npcHandler:say("So you want to know what we all think about your deeds? What leader's opinion are you interested in, the {gnomes} (Gnomus), the {dwarves} (Klom Stonecutter) or the {scouts} (Lardoc Bashsmite)?", npc, creature) npcHandler:setTopic(playerId, 5) elseif MsgContains(message, "gnomes") and npcHandler:getTopic(playerId) == 5 then - npcHandler:say({ "The gnomes are still in need of your help, member of Bigfoot's Brigade. Prove your worth by answering their calls! (" .. math.max(player:getStorageValue(Storage.DangerousDepths.Gnomes.Status), 0) .. "/10)" }, npc, creature) + npcHandler:say("The gnomes are still in need of your help, member of Bigfoot's Brigade. Prove your worth by answering their calls! (" .. math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points), 0) .. "/10)", npc, creature) elseif MsgContains(message, "dwarves") and npcHandler:getTopic(playerId) == 5 then - npcHandler:say({ "The dwarves are still in need of your help, member of Bigfoot's Brigade. Prove your worth by answering their calls! (" .. math.max(player:getStorageValue(Storage.DangerousDepths.Dwarves.Status), 0) .. "/10)" }, npc, creature) + npcHandler:say("The dwarves are still in need of your help, member of Bigfoot's Brigade. Prove your worth by answering their calls! (" .. math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Points), 0) .. "/10)", npc, creature) elseif MsgContains(message, "scouts") and npcHandler:getTopic(playerId) == 5 then - npcHandler:say({ "The scouts are still in need of your help, member of Bigfoot's Brigade. Prove your worth by answering their calls! (" .. math.max(player:getStorageValue(Storage.DangerousDepths.Scouts.Status), 0) .. "/10)" }, npc, creature) + npcHandler:say("The scouts are still in need of your help, member of Bigfoot's Brigade. Prove your worth by answering their calls! (" .. math.max(player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Points), 0) .. "/10)", npc, creature) end + return true end +keywordHandler:addKeyword({ "work" }, StdModule.say, { npcHandler = npcHandler, text = "Here's the situation: a tide of hazardous bugs, the gnomes named them {diremaws}, threatens our base. Then there is some kind of {growth} which seems connected to them somehow. We need to get rid of both." }) +keywordHandler:addKeyword({ "worth" }, StdModule.say, { + npcHandler = npcHandler, + text = { + "You're already known amongst the gnomes, member of the Bigfoot Brigade. I will make sure that the alliance learns of your deeds but you'll still need to help the dwarves and gnomes of this outpost to show your worth. ...", + "We also found suspicious devices carried by all kinds of creatures down here. Down here, they are of extreme worth to us since they could contain the key to what's happening all around us. ... ", + "If you can aquire any, return them to me and I make sure to tell the others of your generosity. Return to me afterwards to check on your current {status}.", + }, +}) +keywordHandler:addKeyword({ "job" }, StdModule.say, { + npcHandler = npcHandler, + text = { + "Leading the charge! It's a war down here. I maintain the outer defences and supplies which are organised back in Kazordoon and also by the gnomes. ...", + "I have sealed some of the areas far too dangerous for anyone to enter. If you can prove you're capable, you'll get an oportunity to help destroying the weird machines, pumping lava into the caves leading to the most dangerous enemies.", + "But even if we knew nothing more about them, the fact alone that they employ the help of those mockeries of all things dwarfish, marks them as an enemy of the dwarves and it's our obligation to annihilate them.", + }, +}) +keywordHandler:addKeyword({ "name" }, StdModule.say, { npcHandler = npcHandler, text = "Bashsmite. That's all you need to know." }) + +npcHandler:setMessage(MESSAGE_GREET, "Since you are obviously not one of my relatives who pays me a long overdue visit, we should get {work} right away. We'll see if you can prove your {worth} to our alliance.") npcHandler:setMessage(MESSAGE_WALKAWAY, "Well, bye then.") npcHandler:setCallback(CALLBACK_SET_INTERACTION, onAddFocus) diff --git a/data-otservbr-global/npc/maelyrra.lua b/data-otservbr-global/npc/maelyrra.lua index f582f2418c1..2a4bce29fb6 100644 --- a/data-otservbr-global/npc/maelyrra.lua +++ b/data-otservbr-global/npc/maelyrra.lua @@ -61,39 +61,25 @@ local function creatureSayCallback(npc, creature, type, message) if MsgContains(message, "mission") then if player:getStorageValue(ThreatenedDreams.Mission02[1]) < 1 then - npcHandler:say({ - "Some annoying nightmarish creatures rove about in the tunnels underneath this island. They are threatening the members of my court and devastate the flora and fauna. They also threaten the natural balance. Would you go and fight them for me?", - }, npc, creature) + npcHandler:say("Some annoying nightmarish creatures rove about in the tunnels underneath this island. They are threatening the members of my court and devastate the flora and fauna. They also threaten the natural balance. Would you go and fight them for me?", npc, creature) npcHandler:setTopic(playerId, 1) elseif player:getStorageValue(ThreatenedDreams.Mission02[1]) >= 1 and player:getStorageValue(ThreatenedDreams.Mission02[1]) <= 2 then - npcHandler:say({ - "Have you defeated the nightmare monsters?", - }, npc, creature) + npcHandler:say("Have you defeated the nightmare monsters?", npc, creature) npcHandler:setTopic(playerId, 2) elseif player:getStorageValue(ThreatenedDreams.Mission02[1]) >= 3 and player:getStorageValue(ThreatenedDreams.Mission02[1]) <= 4 then - npcHandler:say({ - "Have you found the moon mirror and freed the captured fairies?", - }, npc, creature) + npcHandler:say("Have you found the moon mirror and freed the captured fairies?", npc, creature) npcHandler:setTopic(playerId, 4) elseif player:getStorageValue(ThreatenedDreams.Mission02[1]) == 5 and player:getStorageValue(ThreatenedDreams.Mission03[1]) == 4 then - npcHandler:say({ - "Have you already found the starlight vial and the sun catcher?", - }, npc, creature) + npcHandler:say("Have you already found the starlight vial and the sun catcher?", npc, creature) npcHandler:setTopic(playerId, 6) elseif player:getStorageValue(ThreatenedDreams.Mission02[1]) == 6 then - npcHandler:say({ - "Could you already gather the three lights?", - }, npc, creature) + npcHandler:say("Could you already gather the three lights?", npc, creature) npcHandler:setTopic(playerId, 7) elseif player:getStorageValue(ThreatenedDreams.Mission02[1]) == 7 then - npcHandler:say({ - "Have you repaired the magical barrier? Is Feyrist safe?", - }, npc, creature) + npcHandler:say("Have you repaired the magical barrier? Is Feyrist safe?", npc, creature) npcHandler:setTopic(playerId, 8) else - npcHandler:say({ - "Thank you again, mortal being! The fae will be forever grateful.", - }, npc, creature) + npcHandler:say("Thank you again, mortal being! The fae will be forever grateful.", npc, creature) npcHandler:setTopic(playerId, 0) end elseif MsgContains(message, "yes") then @@ -110,20 +96,14 @@ local function creatureSayCallback(npc, creature, type, message) local frazzlemawsKills = player:getStorageValue(ThreatenedDreams.Mission02.FrazzlemawsCount) local kroazurKill = player:getStorageValue(ThreatenedDreams.Mission02.KroazurKill) if player:getStorageValue(ThreatenedDreams.Mission02[1]) == 1 and kroazurKill >= 1 and (enfeebledKills + frazzlemawsKills) >= 200 then - npcHandler:say({ - "Thank you! You rendered a great favour to the fae courts and Feyrist alike. Would you help us with another problem?", - }, npc, creature) + npcHandler:say("Thank you! You rendered a great favour to the fae courts and Feyrist alike. Would you help us with another problem?", npc, creature) npcHandler:setTopic(playerId, 3) player:setStorageValue(ThreatenedDreams.Mission02[1], 2) elseif player:getStorageValue(ThreatenedDreams.Mission02[1]) == 2 then - npcHandler:say({ - "You rendered a great favour to the fae courts and Feyrist alike. Would you help us with another problem?", - }, npc, creature) + npcHandler:say("You rendered a great favour to the fae courts and Feyrist alike. Would you help us with another problem?", npc, creature) npcHandler:setTopic(playerId, 3) else - npcHandler:say({ - "You have to kill two hundred of nightmare creatures and the terrible demon Kroazur who's leading them. That should bring some relief for the fae.", - }, npc, creature) + npcHandler:say("You have to kill two hundred of nightmare creatures and the terrible demon Kroazur who's leading them. That should bring some relief for the fae.", npc, creature) npcHandler:setTopic(playerId, 0) end elseif npcHandler:getTopic(playerId) == 3 then @@ -137,20 +117,14 @@ local function creatureSayCallback(npc, creature, type, message) npcHandler:setTopic(playerId, 0) elseif npcHandler:getTopic(playerId) == 4 then if player:getStorageValue(ThreatenedDreams.Mission02.FairiesCounter) == 5 and player:getStorageValue(ThreatenedDreams.Mission02.DarkMoonMirror) == 1 then - npcHandler:say({ - "Thank you, mortal being! Please keep the mirror. I think you may need it soon, because there is another problem. Would you help us again?", - }, npc, creature) + npcHandler:say("Thank you, mortal being! Please keep the mirror. I think you may need it soon, because there is another problem. Would you help us again?", npc, creature) npcHandler:setTopic(playerId, 5) player:setStorageValue(ThreatenedDreams.Mission02[1], 4) elseif player:getStorageValue(ThreatenedDreams.Mission02[1]) == 4 then - npcHandler:say({ - "Please keep the mirror. I think you may need it soon, because there is another problem. Would you help us again?", - }, npc, creature) + npcHandler:say("Please keep the mirror. I think you may need it soon, because there is another problem. Would you help us again?", npc, creature) npcHandler:setTopic(playerId, 5) else - npcHandler:say({ - "Please seek out the tainted fae, retrieve the artefact and free the captured fairies.", - }, npc, creature) + npcHandler:say("Please seek out the tainted fae, retrieve the artefact and free the captured fairies.", npc, creature) npcHandler:setTopic(playerId, 0) end elseif npcHandler:getTopic(playerId) == 5 then @@ -178,23 +152,17 @@ local function creatureSayCallback(npc, creature, type, message) player:setStorageValue(ThreatenedDreams.Mission02[1], 7) npcHandler:setTopic(playerId, 0) else - npcHandler:say({ - "You have to catch the respective lights and store it in the corresponding vessel.", - }, npc, creature) + npcHandler:say("You have to catch the respective lights and store it in the corresponding vessel.", npc, creature) npcHandler:setTopic(playerId, 0) end elseif npcHandler:getTopic(playerId) == 8 then if player:getStorageValue(ThreatenedDreams.Mission02.ChargedMoonMirror) == 0 and player:getStorageValue(ThreatenedDreams.Mission02.ChargedStarlightVial) == 0 and player:getStorageValue(ThreatenedDreams.Mission02.ChargedSunCatcher) == 0 then - npcHandler:say({ - "Thank you, mortal being! The fae will be forever grateful. Take this blossom bag as a little thank-you gift. Such blossoms grow on our trees just once in a decade, so they are quite rare.", - }, npc, creature) + npcHandler:say("Thank you, mortal being! The fae will be forever grateful. Take this blossom bag as a little thank-you gift. Such blossoms grow on our trees just once in a decade, so they are quite rare.", npc, creature) player:addItem(25780, 1) player:setStorageValue(ThreatenedDreams.Mission02[1], 8) npcHandler:setTopic(playerId, 0) else - npcHandler:say({ - "If you charge all fifteen arcane sources with the respective light, Feyrist's protection will be ensured again.", - }, npc, creature) + npcHandler:say("If you charge all fifteen arcane sources with the respective light, Feyrist's protection will be ensured again.", npc, creature) npcHandler:setTopic(playerId, 0) end end diff --git a/data-otservbr-global/npc/muriel.lua b/data-otservbr-global/npc/muriel.lua index 8017528a681..8aece06cd9a 100644 --- a/data-otservbr-global/npc/muriel.lua +++ b/data-otservbr-global/npc/muriel.lua @@ -76,19 +76,58 @@ local function creatureSayCallback(npc, creature, type, message) "The rotworms dug deep into the soil north of Thais. Rumours say that you can access a place of endless moaning from there. ...", "No one knows how old that common grave is but the people who died there are cursed and never come to rest. A bone from that pit would be perfect for my studies.", }, npc, creature) + npcHandler:setTopic(playerId, 0) elseif player:getStorageValue(Storage.Quest.U8_1.TibiaTales.IntoTheBonePit) == 2 then player:setStorageValue(Storage.Quest.U8_1.TibiaTales.IntoTheBonePit, 3) if player:removeItem(131, 1) then player:addItem(6299, 1) npcHandler:say("Excellent! Now I can try to put my theoretical thoughts into practice and find a cure for the symptoms of undead. Here, take this for your efforts.", npc, creature) + npcHandler:setTopic(playerId, 0) else npcHandler:say({ "I am so glad you are still alive. Benjamin found the container with the bone sample inside. Fortunately, I inscribe everything with my name, so he knew it was mine. ...", "I thought you have been haunted and killed by the undead. I'm glad that this is not the case. Thank you for your help.", }, npc, creature) + npcHandler:setTopic(playerId, 0) end else npcHandler:say("I am very glad you helped me, but I am very busy at the moment.", npc, creature) + npcHandler:setTopic(playerId, 0) + end + elseif MsgContains(message, "addons") then + local hasMasks = player:getItemCount(25088) >= 3 + local hasFeathers = player:getItemCount(25089) >= 50 + if player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.Feathers) == 2 and player:getStorageValue(Storage.Quest.U11_02.FestiveOutfits.Addon1) == 1 and hasMasks then + npcHandler:say("I see you have the porcelain masks. Are you ready to exchange them for the first addon?", npc, creature) + npcHandler:setTopic(playerId, 2) + elseif player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.Feathers) == 2 and player:getStorageValue(Storage.Quest.U11_02.FestiveOutfits.Addon2) == 1 and hasFeathers then + npcHandler:say("I see you have the colourful feathers. Are you ready to exchange them for the second addon?", npc, creature) + npcHandler:setTopic(playerId, 4) + else + npcHandler:say("You need the outfit and 3 porcelain masks or 50 colored feathers to get the festive costume accessories.", npc, creature) + npcHandler:setTopic(playerId, 0) + end + elseif MsgContains(message, "mask") and player:getStorageValue(Storage.Quest.U11_02.FestiveOutfits.Addon1) == 1 then + if player:removeItem(25088, 3) then + player:addOutfit(929, 1) + player:addOutfit(931, 1) + npcHandler:say("Very good! You gained the first addon to the festive outfit.", npc, creature) + player:setStorageValue(Storage.Quest.U11_02.FestiveOutfits.Addon1, 2) + npcHandler:setTopic(playerId, 0) + else + npcHandler:say("Oh, sorry but you don't have enough porcelain masks!", npc, creature) + npcHandler:setTopic(playerId, 0) + end + elseif MsgContains(message, "feather") and player:getStorageValue(Storage.Quest.U11_02.FestiveOutfits.Addon2) == 1 then + if player:removeItem(25089, 50) then + player:addOutfit(929, 2) + player:addOutfit(931, 2) + npcHandler:say("Very good! You gained the second addon to the festive outfit.", npc, creature) + player:setStorageValue(Storage.Quest.U11_02.FestiveOutfits.Addon2, 2) + npcHandler:setTopic(playerId, 0) + else + npcHandler:say("Oh, sorry but you don't have enough colourful feathers!", npc, creature) + npcHandler:setTopic(playerId, 0) end elseif MsgContains(message, "yes") then if npcHandler:getTopic(playerId) == 1 then @@ -98,12 +137,22 @@ local function creatureSayCallback(npc, creature, type, message) }, npc, creature) player:addItem(4852, 1) player:setStorageValue(Storage.Quest.U8_1.TibiaTales.IntoTheBonePit, 1) + npcHandler:setTopic(playerId, 0) + elseif npcHandler:getTopic(playerId) == 2 then + npcHandler:say("I provide two addons. For the first one I need you to bring me three porcelain masks. For the second addon you need fifty colourful ostrich feathers. Do you want one of these addons?", npc, creature) + npcHandler:setTopic(playerId, 3) + elseif npcHandler:getTopic(playerId) == 3 then + npcHandler:say("What do you have for me: the porcelain masks or the colourful feathers?", npc, creature) + player:setStorageValue(Storage.Quest.U11_02.FestiveOutfits.Addon1, 1) + elseif npcHandler:getTopic(playerId) == 4 then + npcHandler:say("I provide two addons. For the first one I need you to bring me three porcelain masks. For the second addon you need fifty colourful ostrich feathers. Do you want one of these addons?", npc, creature) + player:setStorageValue(Storage.Quest.U11_02.FestiveOutfits.Addon2, 1) end elseif MsgContains(message, "no") then - if npcHandler:getTopic(playerId) == 1 then - npcHandler:say("Ohh, then I need to find another adventurer who wants to earn a great reward. Bye!", npc, creature) - end + npcHandler:say("Ohh, then I need to find another adventurer who wants to earn a great reward. Bye!", npc, creature) + npcHandler:setTopic(playerId, 0) end + return true end diff --git a/data-otservbr-global/npc/noozer.lua b/data-otservbr-global/npc/noozer.lua index 855e3e0174b..5a0478fa2f3 100644 --- a/data-otservbr-global/npc/noozer.lua +++ b/data-otservbr-global/npc/noozer.lua @@ -50,15 +50,6 @@ npcType.onCloseChannel = function(npc, creature) npcHandler:onCloseChannel(npc, creature) end -local function greetCallback(npc, creature) - local playerId = creature:getId() - local player = Player(creature) - - npcHandler:setMessage(MESSAGE_GREET, "Hm? Oh! Oh, yes a... visitor! Intruder? Benefactor...? Wha- what are you? If you want to {pass} through this {cave}, I may have to disappoint you. Or maybe not. It... depends. So, just passing through?.") - npcHandler:setTopic(playerId, 1) - - return true -end local function creatureSayCallback(npc, creature, type, message) local player = Player(creature) local playerId = player:getId() @@ -67,8 +58,7 @@ local function creatureSayCallback(npc, creature, type, message) return false end - -- Começou a quest - if MsgContains(message, "pass") and npcHandler:getTopic(playerId) == 1 then + if MsgContains(message, "pass") then npcHandler:say({ "Yes, yes. Or wait - why do you want to.. ah what does it matter. So you want to get through these {caves}, fine. But be warned! ...", "...wait a second, I lost it. What was I going to say again? Ah yes - DANGEROUS! These. Caves. Are. Dangerous. No way you get out alive. Ever. Again. ...", @@ -76,29 +66,31 @@ local function creatureSayCallback(npc, creature, type, message) "Wait - I am the guardian here, yes! The keeper of... something... or another, yes, I... guard this place. With my life. Don't I? Of course! ...", "Is, er... this the moment where I should try to... stop you? Yes? No? Ah, you know what - you go down there, those guys are angry as dung anyway. Try your luck, return to me when you're done. If you still can. Or not.", }, npc, creature) - if player:getStorageValue(Storage.CultsOfTibia.Questline) < 1 then - player:setStorageValue(Storage.CultsOfTibia.Questline, 1) - end - if player:getStorageValue(Storage.CultsOfTibia.Misguided.Mission) < 2 then - player:setStorageValue(Storage.CultsOfTibia.Misguided.Mission, 2) - player:setStorageValue(Storage.CultsOfTibia.Misguided.AccessDoor, 1) + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.Mission) < 2 then + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.Mission, 2) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.AccessDoor, 1) end - elseif MsgContains(message, "cave") and npcHandler:getTopic(playerId) == 1 then - npcHandler:say({ "I was stationed in this cave to... guard something. Right now I am not even sure what that was." }, npc, creature) - elseif MsgContains(message, "job") and npcHandler:getTopic(playerId) == 1 then - npcHandler:say({ "Then don't waste my time. I'm doing some important... business... here. Actually... where am I? If I find out, I will be even more angry than I am now. Out of my sight." }, npc, creature) - elseif MsgContains(message, "mission") and npcHandler:getTopic(playerId) == 1 then - npcHandler:say({ "I was on a mission, too - I guess. It was all quite blurry back then. Maybe I'll leave this place after I recovered completely. I have to find out what happened to me." }, npc, creature) + npcHandler:setTopic(playerId, 0) + elseif MsgContains(message, "cave") then + npcHandler:say("I was stationed in this cave to... guard something. Right now I am not even sure what that was.", npc, creature) + npcHandler:setTopic(playerId, 0) + elseif MsgContains(message, "job") then + npcHandler:say("Then don't waste my time. I'm doing some important... business... here. Actually... where am I? If I find out, I will be even more angry than I am now. Out of my sight.", npc, creature) + npcHandler:setTopic(playerId, 0) + elseif MsgContains(message, "mission") then + npcHandler:say("I was on a mission, too - I guess. It was all quite blurry back then. Maybe I'll leave this place after I recovered completely. I have to find out what happened to me.", npc, creature) + npcHandler:setTopic(playerId, 0) end + return true end +npcHandler:setMessage(MESSAGE_GREET, "Hm? Oh! Oh, yes a... visitor! Intruder? Benefactor...? Wha- what are you? If you want to pass through this cave, I may have to disappoint you. Or maybe not. It... depends. So, just passing through?") npcHandler:setMessage(MESSAGE_WALKAWAY, "Well, bye then.") npcHandler:setCallback(CALLBACK_SET_INTERACTION, onAddFocus) npcHandler:setCallback(CALLBACK_REMOVE_INTERACTION, onReleaseFocus) -npcHandler:setCallback(CALLBACK_GREET, greetCallback) npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback) npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true) diff --git a/data-otservbr-global/npc/spectulus.lua b/data-otservbr-global/npc/spectulus.lua index 9856bce82c0..6bed2acf958 100644 --- a/data-otservbr-global/npc/spectulus.lua +++ b/data-otservbr-global/npc/spectulus.lua @@ -424,13 +424,39 @@ local function creatureSayCallback(npc, creature, type, message) "Are you willing to help me?", }, npc, creature) npcHandler:setTopic(playerId, 25) - elseif MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 25 then + elseif MsgContains(message, "yes") then if npcHandler:getTopic(playerId) == 25 then npcHandler:say("Excellent, excellent. The rumours pointed to the north of Tiquanda, a sunken temple probably half drowned in water. Return to me if you find anything interesting!", npc, creature) - player:setStorageValue(Storage.TheSecretLibrary.LiquidDeath, 1) + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Questline) < 1 then + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Questline, 1) + end npcHandler:setTopic(playerId, 0) end end + + if MsgContains(message, "njey") then + npcHandler:say({ + "Mh? Ah, yes yes. 'Njey' is the native-language term for a very old race of undersea creatures which ...", + "...hm, wait - only a select few of my colleagues even bothered studying their culture. They are a mere fantasy to the common man - is there anything of importance you want to tell me?", + }, npc, creature) + npcHandler:setTopic(playerId, 26) + elseif MsgContains(message, "yes") then + if npcHandler:getTopic(playerId) == 26 then + npcHandler:say({ + "Well, if you really want to delve into this - I could use some help. So you have found my machine on that island? And you found the notes with the coordinates? Then you can find the entrance! ...", + "Just look for a large staircase with sprawling steps. There is an unpassable stream there that will prevent you from venturing further on. But fear not, you can indeed travel down there - with these small enhancements I created. ...", + "I will put this under your footgear. Here you go. And this in your nose. There. And there will be no further problems for you down there. Except- ah, well you'll find out yourself soon enough, won't you?", + }, npc, creature) + npcHandler:setTopic(playerId, 34) + elseif npcHandler:getTopic(playerId) == 34 then + npcHandler:say("Then off you go! I'm sorry that I cannot offer you any further help but I'm sure you will find support along your way. And - be careful. The sea can appear pitch black down there.", npc, creature) + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Questline) < 4 then + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Questline, 4) + end + npcHandler:setTopic(playerId, 0) + end + end + return true end diff --git a/data-otservbr-global/npc/tamoril.lua b/data-otservbr-global/npc/tamoril.lua index 654355d9be5..e6ed393420f 100644 --- a/data-otservbr-global/npc/tamoril.lua +++ b/data-otservbr-global/npc/tamoril.lua @@ -42,16 +42,26 @@ npcType.onThink = function(npc, interval) npcHandler:onThink(npc, interval) end -local function greetCallback(npc, creature) - local playerId = creature:getId() - npcHandler:setMessage(MESSAGE_GREET, "Another pesky mortal who believes his gold outweighs his nutrition value.") - return true +local function isDateWithinEvent() + local currentDate = os.date("*t") + local startDate = { day = 14, month = 1 } + local endDate = { day = 12, month = 2 } + + if (currentDate.month == startDate.month and currentDate.day >= startDate.day) or (currentDate.month == endDate.month and currentDate.day <= endDate.day) or (currentDate.month > startDate.month and currentDate.month < endDate.month) then + return true + end + return false end local function creatureSayCallback(npc, creature, type, message) local player = Player(creature) local playerId = player:getId() + if not isDateWithinEvent() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only discuss the First Dragon between January 14 and February 12.") + return true + end + if MsgContains(message, "first dragon") then npcHandler:say("The First Dragon? The first of all of us? The Son of Garsharak? I'm surprised you heard about him. It is such a long time that he wandered Tibia. Yet, there are some {rumours}.", npc, creature) npcHandler:setTopic(playerId, 1) @@ -86,15 +96,22 @@ local function creatureSayCallback(npc, creature, type, message) "And finally an amethyst teleporter in undead-infested caverns underneath Edron allows you to enter the lair of Zorvorax.", }, npc, creature) npcHandler:setTopic(playerId, 0) - player:setStorageValue(Storage.FirstDragon.Questline, 1) - player:setStorageValue(Storage.FirstDragon.DragonCounter, 0) - player:setStorageValue(Storage.FirstDragon.ChestCounter, 0) - player:setStorageValue(Storage.FirstDragon.GelidrazahAccess, 0) - player:setStorageValue(Storage.FirstDragon.SecretsCounter, 0) + if player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.Questline) < 1 then + player:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.Questline, 1) + end + if player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.ChestCounter) < 0 then + player:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.ChestCounter, 0) + end + player:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.DragonCounter, 0) + player:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.GelidrazahAccess, 0) + player:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.SecretsCounter, 0) end + return true end -npcHandler:setCallback(CALLBACK_GREET, greetCallback) + +npcHandler:setMessage(MESSAGE_GREET, "Another pesky mortal who believes his gold outweighs his nutrition value.") + npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback) npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true) @@ -107,6 +124,7 @@ npcConfig.shop = { { itemName = "white gem", clientId = 32769, sell = 12000 }, { itemName = "yellow gem", clientId = 3037, sell = 1000 }, } + -- On buy npc shop message npcType.onBuyItem = function(npc, player, itemId, subType, amount, ignore, inBackpacks, totalCost) npc:sellItem(player, itemId, amount, subType, 0, ignore, inBackpacks) diff --git a/data-otservbr-global/npc/the_first_dragon.lua b/data-otservbr-global/npc/the_first_dragon.lua index 64b10bc4cf5..ec3ba95d004 100644 --- a/data-otservbr-global/npc/the_first_dragon.lua +++ b/data-otservbr-global/npc/the_first_dragon.lua @@ -54,28 +54,30 @@ local function creatureSayCallback(npc, creature, type, message) local player = Player(creature) local playerId = player:getId() - if MsgContains(message, "reward") and npcHandler:getTopic(playerId) == 0 and player:getStorageValue(34013) < 1 then + if MsgContains(message, "reward") and player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.Feathers) == 1 then npcHandler:say({ "Have a look at the chests over there. Feel free to take the things you find within. You know, gold, gems and the like. There's also a garment I'm storing here for a while. As it doesn't fit me I have no need for it. ...", "But it might be a nice outfit for a little human. You also can take the porcelain mask and the feathers. There's a wizard named Muriel in Thais who reportedly embellishes outfits with stuff like this.", }, npc, creature) - player:addOutfit(929, 1) - player:addOutfit(931, 1) + player:addOutfit(929, 0) + player:addOutfit(931, 0) player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE) - player:setStorageValue(34013, 1) + if player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.Feathers) < 2 then + player:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.Feathers, 2) + end npcHandler:setTopic(playerId, 0) - elseif MsgContains(message, "fight") and npcHandler:getTopic(playerId) == 0 then + elseif MsgContains(message, "fight") then npcHandler:say({ "Even in {retirement} I sometimes succumb to the temptation of sweet battle. Fighting was so different back in the old {times}. You can read about it in my {memoirs}. There should be a copy lying here somewhere.", }, npc, creature) npcHandler:setTopic(playerId, 0) - elseif MsgContains(message, "retirement") and npcHandler:getTopic(playerId) == 0 then + elseif MsgContains(message, "retirement") then npcHandler:say({ "When you are young, you are much more tolerant towards the burden of life. At a certain age even a {dragon} will start to think about if things will go on forever. ...", "Eventually there comes a time when you decide all the {hassle} isn't worth it anymore and you decide to concentrate on the {finer} things life has to offer.", }, npc, creature) npcHandler:setTopic(playerId, 0) - elseif MsgContains(message, "memoirs") and npcHandler:getTopic(playerId) == 0 then + elseif MsgContains(message, "memoirs") then npcHandler:say({ "I dictated my memoirs to a human servant. I like the idea to share my thoughts and memories with humanity with the help of a {book}. ...", "You are my greatest fans after all. ...", @@ -83,37 +85,37 @@ local function creatureSayCallback(npc, creature, type, message) "By the way, it's a funny story how I learnt the bonelord language. However, I saved it for a possible part two of my memoirs.", }, npc, creature) npcHandler:setTopic(playerId, 0) - elseif MsgContains(message, "hassle") and npcHandler:getTopic(playerId) == 0 then + elseif MsgContains(message, "hassle") then npcHandler:say({ "I really enjoy a good fight now and then. ...", "A real pain, however, is the constant annoyance caused by tedious fights against adventurers with more healing pots than brain.", }, npc, creature) npcHandler:setTopic(playerId, 0) - elseif MsgContains(message, "worthy") and npcHandler:getTopic(playerId) == 0 then + elseif MsgContains(message, "worthy") then npcHandler:say({ "To be honest, the first dragon hunters weren't {worthy}. ...", "They used {weaknesses} to their advantage. It took some time before a dragon could be killed in a fair fight.", }, npc, creature) npcHandler:setTopic(playerId, 0) - elseif MsgContains(message, "weaknesses") and npcHandler:getTopic(playerId) == 0 then + elseif MsgContains(message, "weaknesses") then npcHandler:say({ "The first awoken dragons were quite disoriented when leaving the {mists} and re-entering reality. ...", "This led to certain vulnerabilities. However, the dragons overcame such shortcomings quickly and soon took their rightful place in the food chain, so to say.", }, npc, creature) npcHandler:setTopic(playerId, 0) - elseif MsgContains(message, "mists") and npcHandler:getTopic(playerId) == 0 then + elseif MsgContains(message, "mists") then npcHandler:say({ "There was a time before the gods cast the mists of healing over the world. At that time the dragons ruled and burnt the {world}. ...", "In hindsight I'm not too proud of this. This was some kind of juvenile bullying.", }, npc, creature) npcHandler:setTopic(playerId, 0) - elseif MsgContains(message, "world") and npcHandler:getTopic(playerId) == 0 then + elseif MsgContains(message, "world") then npcHandler:say({ "The world has changed so much; I'm beginning to feel really old. People have changed, the face of the world has changed, even the laws of nature are no longer the same. ...", "I think it is the greatest advantage of you {humans} to be more flexible and adaptable.", }, npc, creature) npcHandler:setTopic(playerId, 0) - elseif MsgContains(message, "books") and npcHandler:getTopic(playerId) == 0 then + elseif MsgContains(message, "books") then npcHandler:say({ "Your books are an amazing thing. Draconic glyphs are so different. ...", "They are not only more complicated but also more specific. A single word which always keeps the same meaning is astonishingly effective in its primitive way. ...", @@ -121,19 +123,19 @@ local function creatureSayCallback(npc, creature, type, message) "Books are a wonderful {invention}. If you are ever tired of your heroic exploits, take the time to read one.", }, npc, creature) npcHandler:setTopic(playerId, 0) - elseif MsgContains(message, "invention") and npcHandler:getTopic(playerId) == 0 then + elseif MsgContains(message, "invention") then npcHandler:say({ "Being powerful creatures themselves, dragons had no need for inventions. ...", "However, now that times have changed and the pressure increases, even my kin might slowly start to change in order to {adapt}.", }, npc, creature) npcHandler:setTopic(playerId, 0) - elseif MsgContains(message, "adapt") and npcHandler:getTopic(playerId) == 0 then + elseif MsgContains(message, "adapt") then npcHandler:say({ "Well, surely some of my kin might whine and complain as they always do. ...", "You have no idea how backwards thinking some dragons can be. A few are even still idealising the times before the {mists}.", }, npc, creature) npcHandler:setTopic(playerId, 0) - elseif MsgContains(message, "finer") and npcHandler:getTopic(playerId) == 0 then + elseif MsgContains(message, "finer") then npcHandler:say({ "The definition of finer things is different for a dragon than for a human. ...", "A young dragon appreciates diving into a lava pool, while an old dragon just can't stand to get rid of the {lava} afterwards and prefers bathing in boiling water.", @@ -144,6 +146,8 @@ local function creatureSayCallback(npc, creature, type, message) return true end +npcHandler:setMessage(MESSAGE_GREET, "Hello, my sparring buddy. We should have another fight sometimes. I think you may have earned a little reward.") + keywordHandler:addKeyword({ "times" }, StdModule.say, { npcHandler = npcHandler, text = "Times have changed . In the past dragons were feared and respected. Only the {demons} rivalled our notoriety." }) keywordHandler:addKeyword({ "demons" }, StdModule.say, { npcHandler = npcHandler, text = "Those upstarts! I wonder why would anyone care about them. They lack our style. For them it is all about brute force and showing-off." }) keywordHandler:addKeyword({ "style" }, StdModule.say, { npcHandler = npcHandler, text = "Breathing fire is an art! Instead of setting everything on fire, you exhale a cone of fire to give a worthy opponent a chance to avoid it." }) diff --git a/data-otservbr-global/npc/tigo.lua b/data-otservbr-global/npc/tigo.lua index 70646d20e40..c663f6ff812 100644 --- a/data-otservbr-global/npc/tigo.lua +++ b/data-otservbr-global/npc/tigo.lua @@ -55,12 +55,14 @@ local function greetCallback(npc, creature) local player = Player(creature) - if player:getStorageValue(Storage.CultsOfTibia.Barkless.Mission) < 2 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Mission) < 2 then npcHandler:setMessage(MESSAGE_GREET, "There, there initiate. You will now become one of us, as so many before you. One of the {Barkless}. Walk with us and you will walk tall my friend.") npcHandler:setTopic(playerId, 1) end + return true end + local function creatureSayCallback(npc, creature, type, message) local player = Player(creature) local playerId = player:getId() @@ -69,23 +71,13 @@ local function creatureSayCallback(npc, creature, type, message) return false end - -- Começou a quest - if MsgContains(message, "barkless") and npcHandler:getTopic(playerId) == 1 then + if MsgContains(message, "barkless") and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Mission) == 1 then npcHandler:say({ "You are now one of us. Learn to endure this world's suffering in every facet and take delight in the soothing eternity that waits for the {purest} of us on the other side." }, npc, creature) - npcHandler:setTopic(playerId, 2) - npcHandler:setTopic(playerId, 2) - if player:getStorageValue(Storage.CultsOfTibia.Questline) < 1 then - player:setStorageValue(Storage.CultsOfTibia.Questline, 1) - end - if player:getStorageValue(Storage.CultsOfTibia.Barkless.Mission) < 1 then - player:setStorageValue(Storage.CultsOfTibia.Barkless.Mission, 1) - player:setStorageValue(Storage.CultsOfTibia.Barkless.TrialAccessDoor, 1) - end - elseif MsgContains(message, "purest") and npcHandler:getTopic(playerId) == 2 then + npcHandler:setTopic(playerId, 1) + elseif MsgContains(message, "purest") and npcHandler:getTopic(playerId) == 1 then npcHandler:say({ "Purification is but one of the difficult steps on your way to the other side. The {trial} of tar, sulphur and ice." }, npc, creature) npcHandler:setTopic(playerId, 2) - npcHandler:setTopic(playerId, 2) - elseif MsgContains(message, "trial") and npcHandler:getTopic(playerId) == 3 then + elseif MsgContains(message, "trial") and npcHandler:getTopic(playerId) == 2 then npcHandler:say({ "The trial consists of three steps. The trial of tar, where you will suffer unbearable heat and embrace the stigma of misfortune. ...", "The trial of sulphur, where you will bathe in burning sulphur and embrace the stigma of vanity. Then, there is the trial of purification. The truest of us will be purified to face judgement from the {Penitent}.", @@ -94,9 +86,10 @@ local function creatureSayCallback(npc, creature, type, message) "If he does, follow him into his own chambers. Barkless are neither allowed to go near the throne room, aside from being judged, nor can we actually enter it.", "He should be easy to defeat with his back to the wall, find him - and delvier us from whatever became of the Penitent.", }, npc, creature) - npcHandler:setTopic(playerId, 0) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.TrialAccessDoor, 1) npcHandler:setTopic(playerId, 0) end + return true end diff --git a/data-otservbr-global/scripts/actions/bosses_levers/dragonking_zyrtarch.lua b/data-otservbr-global/scripts/actions/bosses_levers/dragonking_zyrtarch.lua deleted file mode 100644 index 99585188b91..00000000000 --- a/data-otservbr-global/scripts/actions/bosses_levers/dragonking_zyrtarch.lua +++ /dev/null @@ -1,30 +0,0 @@ -local config = { - boss = { - name = "soul of dragonking zyrtarch", - position = Position(33359, 31182, 12), - }, - requiredLevel = 250, - playerPositions = { - { pos = Position(33391, 31178, 10), teleport = Position(33359, 31186, 10) }, - { pos = Position(33391, 31179, 10), teleport = Position(33359, 31186, 10) }, - { pos = Position(33391, 31180, 10), teleport = Position(33359, 31186, 10) }, - { pos = Position(33391, 31181, 10), teleport = Position(33359, 31186, 10) }, - { pos = Position(33391, 31182, 10), teleport = Position(33359, 31186, 10) }, - }, - monsters = { - { name = "soulcatcher", pos = Position(33352, 31187, 10) }, - { name = "soulcatcher", pos = Position(33363, 31187, 10) }, - { name = "soulcatcher", pos = Position(33353, 31176, 10) }, - { name = "soulcatcher", pos = Position(33363, 31176, 10) }, - { name = "dragonking zyrtarch", pos = Position(33357, 31182, 10) }, - }, - specPos = { - from = Position(33348, 31172, 10), - to = Position(33368, 31190, 12), - }, - exit = Position(33407, 31172, 10), -} - -local lever = BossLever(config) -lever:position(Position(33391, 31177, 10)) -lever:register() diff --git a/data-otservbr-global/scripts/actions/bosses_levers/lady_tenebris.lua b/data-otservbr-global/scripts/actions/bosses_levers/lady_tenebris.lua deleted file mode 100644 index 795b3cf2234..00000000000 --- a/data-otservbr-global/scripts/actions/bosses_levers/lady_tenebris.lua +++ /dev/null @@ -1,28 +0,0 @@ -local config = { - boss = { - name = "Lady Tenebris", - position = Position(32912, 31599, 14), - }, - requiredLevel = 250, - playerPositions = { - { pos = Position(32902, 31623, 14), teleport = Position(32911, 31603, 14) }, - { pos = Position(32902, 31624, 14), teleport = Position(32911, 31603, 14) }, - { pos = Position(32902, 31625, 14), teleport = Position(32911, 31603, 14) }, - { pos = Position(32902, 31626, 14), teleport = Position(32911, 31603, 14) }, - { pos = Position(32902, 31627, 14), teleport = Position(32911, 31603, 14) }, - }, - monsters = { - { name = "shadow tentacle", pos = Position(32910, 31599, 14) }, - { name = "shadow tentacle", pos = Position(32912, 31597, 14) }, - { name = "shadow tentacle", pos = Position(32914, 31599, 14) }, - }, - specPos = { - from = Position(32899, 31587, 14), - to = Position(32923, 31612, 14), - }, - exit = Position(32902, 31629, 14), -} - -local lever = BossLever(config) -lever:position(Position(32902, 31622, 14)) -lever:register() diff --git a/data-otservbr-global/scripts/actions/bosses_levers/lloyd.lua b/data-otservbr-global/scripts/actions/bosses_levers/lloyd.lua deleted file mode 100644 index 7a2fa1d0a86..00000000000 --- a/data-otservbr-global/scripts/actions/bosses_levers/lloyd.lua +++ /dev/null @@ -1,29 +0,0 @@ -local config = { - boss = { - name = "Lloyd", - position = Position(32799, 32827, 14), - }, - requiredLevel = 250, - playerPositions = { - { pos = Position(32759, 32868, 14), teleport = Position(32800, 32831, 14) }, - { pos = Position(32759, 32869, 14), teleport = Position(32800, 32831, 14) }, - { pos = Position(32759, 32870, 14), teleport = Position(32800, 32831, 14) }, - { pos = Position(32759, 32871, 14), teleport = Position(32800, 32831, 14) }, - { pos = Position(32759, 32872, 14), teleport = Position(32800, 32831, 14) }, - }, - monsters = { - { name = "cosmic energy prism a invu", pos = Position(32801, 32827, 14) }, - { name = "cosmic energy prism b invu", pos = Position(32798, 32827, 14) }, - { name = "cosmic energy prism c invu", pos = Position(32803, 32826, 14) }, - { name = "cosmic energy prism d invu", pos = Position(32796, 32826, 14) }, - }, - specPos = { - from = Position(32785, 32813, 14), - to = Position(32812, 32838, 14), - }, - exit = Position(32815, 32873, 13), -} - -local lever = BossLever(config) -lever:position(Position(32759, 32867, 14)) -lever:register() diff --git a/data-otservbr-global/scripts/actions/bosses_levers/melting_frozen_horror.lua b/data-otservbr-global/scripts/actions/bosses_levers/melting_frozen_horror.lua deleted file mode 100644 index fbb60e8fec2..00000000000 --- a/data-otservbr-global/scripts/actions/bosses_levers/melting_frozen_horror.lua +++ /dev/null @@ -1,34 +0,0 @@ -local eggPos = Position(32269, 31084, 14) -local config = { - boss = { - name = "Melting Frozen Horror", - createFunction = function() - Tile(eggPos):getTopCreature():setHealth(1) - return Game.createMonster("solid frozen horror", Position(32269, 31091, 14), true, true) - end, - }, - timeToDefeat = 15 * 60, -- In seconds - requiredLevel = 250, - playerPositions = { - { pos = Position(32302, 31088, 14), teleport = Position(32271, 31097, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(32302, 31089, 14), teleport = Position(32271, 31097, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(32302, 31090, 14), teleport = Position(32271, 31097, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(32302, 31091, 14), teleport = Position(32271, 31097, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(32302, 31092, 14), teleport = Position(32271, 31097, 14), effect = CONST_ME_TELEPORT }, - }, - monsters = { - { name = "icicle", pos = Position(32266, 31084, 14) }, - { name = "icicle", pos = Position(32272, 31084, 14) }, - { name = "dragon egg", pos = eggPos }, - { name = "melting frozen horror", pos = Position(32267, 31071, 14) }, - }, - specPos = { - from = Position(32257, 31080, 14), - to = Position(32280, 31102, 14), - }, - exit = Position(32271, 31097, 14), -} - -local lever = BossLever(config) -lever:position(Position(32302, 31087, 14)) -lever:register() diff --git a/data-otservbr-global/scripts/actions/bosses_levers/the_last_lore_keeper.lua b/data-otservbr-global/scripts/actions/bosses_levers/the_last_lore_keeper.lua deleted file mode 100644 index d14d9b88d73..00000000000 --- a/data-otservbr-global/scripts/actions/bosses_levers/the_last_lore_keeper.lua +++ /dev/null @@ -1,44 +0,0 @@ -local config = { - boss = { - name = "The Last Lore Keeper", - position = Position(31987, 32839, 14), - }, - timeToFightAgain = ParseDuration("14d") / 1000, - timeToDefeat = ParseDuration("17m") / 1000, - requiredLevel = 250, - playerPositions = { - { pos = Position(32018, 32844, 14), teleport = Position(31984, 32851, 14) }, - { pos = Position(32019, 32844, 14), teleport = Position(31984, 32851, 14) }, - { pos = Position(32020, 32844, 14), teleport = Position(31984, 32851, 14) }, - { pos = Position(32018, 32845, 14), teleport = Position(31984, 32851, 14) }, - { pos = Position(32019, 32845, 14), teleport = Position(31984, 32851, 14) }, - { pos = Position(32020, 32845, 14), teleport = Position(31984, 32851, 14) }, - { pos = Position(32018, 32846, 14), teleport = Position(31984, 32851, 14) }, - { pos = Position(32019, 32846, 14), teleport = Position(31984, 32851, 14) }, - { pos = Position(32020, 32846, 14), teleport = Position(31984, 32851, 14) }, - { pos = Position(32018, 32847, 14), teleport = Position(31984, 32851, 14) }, - { pos = Position(32019, 32847, 14), teleport = Position(31984, 32851, 14) }, - { pos = Position(32020, 32847, 14), teleport = Position(31984, 32851, 14) }, - { pos = Position(32018, 32848, 14), teleport = Position(31984, 32851, 14) }, - { pos = Position(32019, 32848, 14), teleport = Position(31984, 32851, 14) }, - { pos = Position(32020, 32848, 14), teleport = Position(31984, 32851, 14) }, - }, - monsters = { - { name = "bound astral power", pos = Position(31973, 32840, 15) }, - { name = "bound astral power", pos = Position(31973, 32856, 15) }, - { name = "bound astral power", pos = Position(31989, 32856, 15) }, - { name = "bound astral power", pos = Position(31989, 32840, 15) }, - { name = "a shielded astral glyph", pos = Position(31986, 32840, 14) }, - { name = "the distorted astral source", pos = Position(31986, 32823, 15) }, - { name = "an astral glyph", pos = Position(31989, 32823, 15) }, - }, - specPos = { - from = Position(31968, 32821, 14), - to = Position(32004, 32865, 15), - }, - exit = Position(32035, 32859, 14), -} - -local lever = BossLever(config) -lever:position(Position(32019, 32843, 14)) -lever:register() diff --git a/data-otservbr-global/scripts/actions/bosses_levers/the_thorn_knight.lua b/data-otservbr-global/scripts/actions/bosses_levers/the_thorn_knight.lua deleted file mode 100644 index b48a07e44e1..00000000000 --- a/data-otservbr-global/scripts/actions/bosses_levers/the_thorn_knight.lua +++ /dev/null @@ -1,30 +0,0 @@ -local config = { - boss = { - name = "The Enraged Thorn Knight", - createFunction = function() - return Game.createMonster("Mounted Thorn Knight", Position(32624, 32880, 14), true, true) - end, - }, - requiredLevel = 250, - playerPositions = { - { pos = Position(32657, 32877, 14), teleport = Position(32624, 32886, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(32657, 32878, 14), teleport = Position(32624, 32886, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(32657, 32879, 14), teleport = Position(32624, 32886, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(32657, 32880, 14), teleport = Position(32624, 32886, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(32657, 32881, 14), teleport = Position(32624, 32886, 14), effect = CONST_ME_TELEPORT }, - }, - onUseExtra = function(player) - for d = 1, 6 do - Game.createMonster("possessed tree", Position(math.random(32619, 32629), math.random(32877, 32884), 14), true, true) - end - end, - specPos = { - from = Position(32613, 32869, 14), - to = Position(32636, 32892, 14), - }, - exit = Position(32678, 32888, 14), -} - -local lever = BossLever(config) -lever:position(Position(32657, 32876, 14)) -lever:register() diff --git a/data-otservbr-global/scripts/actions/bosses_levers/the_time_guardian.lua b/data-otservbr-global/scripts/actions/bosses_levers/the_time_guardian.lua deleted file mode 100644 index 386bce7d94d..00000000000 --- a/data-otservbr-global/scripts/actions/bosses_levers/the_time_guardian.lua +++ /dev/null @@ -1,27 +0,0 @@ -local config = { - boss = { - name = "The Time Guardian", - position = Position(32977, 31662, 14), - }, - requiredLevel = 250, - playerPositions = { - { pos = Position(33010, 31660, 14), teleport = Position(32977, 31667, 14) }, - { pos = Position(33010, 31661, 14), teleport = Position(32977, 31667, 14) }, - { pos = Position(33010, 31662, 14), teleport = Position(32977, 31667, 14) }, - { pos = Position(33010, 31663, 14), teleport = Position(32977, 31667, 14) }, - { pos = Position(33010, 31664, 14), teleport = Position(32977, 31667, 14) }, - }, - monsters = { - { name = "The Freezing Time Guardian", pos = Position(32975, 31664, 13) }, - { name = "The Blazing Time Guardian", pos = Position(32980, 31664, 13) }, - }, - specPos = { - from = Position(32967, 31654, 14), - to = Position(32989, 31677, 14), - }, - exit = Position(32870, 32724, 14), -} - -local lever = BossLever(config) -lever:position(Position(33010, 31659, 14)) -lever:register() diff --git a/data-otservbr-global/scripts/actions/farmine/boat.lua b/data-otservbr-global/scripts/actions/farmine/boat.lua deleted file mode 100644 index 6a1e381c1e5..00000000000 --- a/data-otservbr-global/scripts/actions/farmine/boat.lua +++ /dev/null @@ -1,36 +0,0 @@ -local boats = { - { pos = { x = 33344, y = 31349, z = 7 }, destination = Position(33326, 31351, 7), unlockShortcut = Storage.TheSecretLibrary.ShortcutToBastion }, - { pos = { x = 33373, y = 31309, z = 7 }, destination = Position(33382, 31292, 7) }, - { pos = { x = 33382, y = 31294, z = 7 }, destination = Position(33374, 31310, 7) }, - { pos = { x = 33328, y = 31352, z = 7 }, destination = Position(33346, 31348, 7), access = Storage.TheSecretLibrary.ShortcutToBastion }, -} - -local boat = Action() - -function boat.onUse(player, item, fromPosition, itemEx, toPosition) - for b = 1, #boats do - if item:getPosition() == Position(boats[b].pos) then - if boats[b].unlockShortcut then - if player:getStorageValue(boats[b].unlockShortcut) < 1 then - player:setStorageValue(boats[b].unlockShortcut, 1) - end - end - if boats[b].access then - if player:getStorageValue(boats[b].access) == 1 then - player:teleportTo(boats[b].destination) - player:getPosition():sendMagicEffect(CONST_ME_WATERSPLASH) - return true - end - else - player:teleportTo(boats[b].destination) - player:getPosition():sendMagicEffect(CONST_ME_WATERSPLASH) - return true - end - end - end -end - -for a = 1, #boats do - boat:position(boats[a].pos) -end -boat:register() diff --git a/data-otservbr-global/scripts/actions/object/imbuement_shrine.lua b/data-otservbr-global/scripts/actions/object/imbuement_shrine.lua index 0fb998a9756..f5d878df284 100644 --- a/data-otservbr-global/scripts/actions/object/imbuement_shrine.lua +++ b/data-otservbr-global/scripts/actions/object/imbuement_shrine.lua @@ -1,7 +1,7 @@ local imbuement = Action() function imbuement.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if configManager.getBoolean(configKeys.TOGGLE_IMBUEMENT_SHRINE_STORAGE) and player:getStorageValue(Storage.ForgottenKnowledge.Tomes) ~= 1 then + if configManager.getBoolean(configKeys.TOGGLE_IMBUEMENT_SHRINE_STORAGE) and player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.Tomes) ~= 1 then return player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You did not collect enough knowledge from the ancient Shapers. Visit the Shaper temple in Thais for help.") end diff --git a/data-otservbr-global/scripts/actions/other/others/quest_system2.lua b/data-otservbr-global/scripts/actions/other/others/quest_system2.lua index a187810b3bc..89b61977dfc 100644 --- a/data-otservbr-global/scripts/actions/other/others/quest_system2.lua +++ b/data-otservbr-global/scripts/actions/other/others/quest_system2.lua @@ -309,7 +309,7 @@ This page seems to be part of a book about ancient rituals, mystic incantations name = "Falcon Bastion Access", }, }, - storage = Storage.TheSecretLibrary.FalconBastionAccess, + storage = Storage.Quest.U11_80.TheSecretLibrary.FalconBastionAccess, }, [20002] = { items = { diff --git a/data-otservbr-global/scripts/actions/valuables/random_items.lua b/data-otservbr-global/scripts/actions/valuables/random_items.lua index 9e62086a972..2772ac859ce 100644 --- a/data-otservbr-global/scripts/actions/valuables/random_items.lua +++ b/data-otservbr-global/scripts/actions/valuables/random_items.lua @@ -172,6 +172,13 @@ local config = { }, effect = CONST_ME_CRAPS, }, + [26186] = { -- mystery box + chances = { + { from = 0, to = 5001, itemId = 25361 }, -- blood of the mountain + { from = 5002, to = 10001, itemId = 25360 }, -- heart of the mountain + }, + effect = CONST_ME_CRAPS, + }, [27654] = { -- surprise jar chances = { { from = 0, to = 2500, itemId = 3041 }, diff --git a/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua b/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua index bf0e1430635..1cbca17c563 100644 --- a/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua +++ b/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua @@ -15,13 +15,13 @@ local questTable = { { storage = Storage.Quest.U9_60.BigfootsBurden.Warzone1Access, storageValue = 2 }, { storage = Storage.Quest.U9_60.BigfootsBurden.Warzone2Access, storageValue = 2 }, { storage = Storage.Quest.U9_60.BigfootsBurden.Warzone3Access, storageValue = 2 }, - { storage = Storage.DangerousDepths.Questline, storageValue = 10 }, - { storage = Storage.DangerousDepths.Access.LavaPumpWarzoneVI, storageValue = 10 }, - { storage = Storage.DangerousDepths.Access.LavaPumpWarzoneV, storageValue = 10 }, - { storage = Storage.DangerousDepths.Access.LavaPumpWarzoneIV, storageValue = 30 }, - { storage = Storage.DangerousDepths.Dwarves.Status, storageValue = 10 }, - { storage = Storage.DangerousDepths.Scouts.Status, storageValue = 10 }, - { storage = Storage.DangerousDepths.Gnomes.Status, storageValue = 10 }, + { storage = Storage.Quest.U11_50.DangerousDepths.Questline, storageValue = 10 }, + { storage = Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneVI, storageValue = 10 }, + { storage = Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneV, storageValue = 10 }, + { storage = Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneIV, storageValue = 30 }, + { storage = Storage.Quest.U11_50.DangerousDepths.Dwarves.Points, storageValue = 10 }, + { storage = Storage.Quest.U11_50.DangerousDepths.Scouts.Points, storageValue = 10 }, + { storage = Storage.Quest.U11_50.DangerousDepths.Gnomes.Points, storageValue = 10 }, { storage = Storage.Quest.U8_4.InServiceOfYalahar.Questline, storageValue = 51 }, { storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission01, storageValue = 6 }, { storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission02, storageValue = 8 }, @@ -59,38 +59,38 @@ local questTable = { { storage = Storage.Quest.U8_4.InServiceOfYalahar.DoorToLastFight, storageValue = 1 }, { storage = Storage.Quest.U8_4.InServiceOfYalahar.DoorToMatrix, storageValue = 1 }, { storage = Storage.Quest.U8_4.InServiceOfYalahar.DoorToQuara, storageValue = 1 }, - { storage = Storage.CultsOfTibia.Questline, storageValue = 7 }, - { storage = Storage.CultsOfTibia.Minotaurs.JamesfrancisTask, storageValue = 1 }, - { storage = Storage.CultsOfTibia.Minotaurs.Mission, storageValue = 1 }, - { storage = Storage.CultsOfTibia.Minotaurs.BossTimer, storageValue = 1 }, - { storage = Storage.CultsOfTibia.MotA.Mission, 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 }, - { storage = Storage.CultsOfTibia.Barkless.Ice, storageValue = 3 }, - { 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.Life.Mission, storageValue = 7 }, - { 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.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.Minotaurs.EntranceAccessDoor, storageValue = 1 }, - { storage = Storage.CultsOfTibia.Minotaurs.AccessDoor, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Questline, storageValue = 7 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.JamesfrancisTask, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.Mission, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.BossTimer, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.MotA.Mission, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.MotA.Stone1, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.MotA.Stone2, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.MotA.Stone3, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.MotA.Answer, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.MotA.QuestionId, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.Mission, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.sulphur, storageValue = 4 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.Tar, storageValue = 3 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.Ice, storageValue = 3 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.Objects, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.Temp, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Orcs.Mission, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Orcs.LookType, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Orcs.BossTimer, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Life.Mission, storageValue = 7 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Life.BossTimer, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Humans.Mission, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Humans.Vaporized, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Humans.Decaying, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Humans.BossTimer, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.Mission, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.Monsters, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.Exorcisms, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.Time, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.BossTimer, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.EntranceAccessDoor, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.AccessDoor, storageValue = 1 }, { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 1 }, { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 4 }, { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 7 }, @@ -110,16 +110,16 @@ local questTable = { { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 58 }, { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 61 }, { storage = Storage.Quest.U7_6.ExplorerSociety.CalassaQuest, storageValue = 2 }, - { storage = Storage.ForgottenKnowledge.Tomes, storageValue = 1 }, - { storage = Storage.ForgottenKnowledge.LastLoreKilled, storageValue = 1 }, - { storage = Storage.ForgottenKnowledge.TimeGuardianKilled, storageValue = 1 }, - { storage = Storage.ForgottenKnowledge.HorrorKilled, storageValue = 1 }, - { storage = Storage.ForgottenKnowledge.DragonkingKilled, storageValue = 1 }, - { storage = Storage.ForgottenKnowledge.ThornKnightKilled, storageValue = 1 }, - { 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.Quest.U11_02.ForgottenKnowledge.Tomes, storageValue = 1 }, + { storage = Storage.Quest.U11_02.ForgottenKnowledge.LastLoreKilled, storageValue = 1 }, + { storage = Storage.Quest.U11_02.ForgottenKnowledge.TimeGuardianKilled, storageValue = 1 }, + { storage = Storage.Quest.U11_02.ForgottenKnowledge.HorrorKilled, storageValue = 1 }, + { storage = Storage.Quest.U11_02.ForgottenKnowledge.DragonkingKilled, storageValue = 1 }, + { storage = Storage.Quest.U11_02.ForgottenKnowledge.ThornKnightKilled, storageValue = 1 }, + { storage = Storage.Quest.U11_02.ForgottenKnowledge.LloydKilled, storageValue = 1 }, + { storage = Storage.Quest.U11_02.ForgottenKnowledge.LadyTenebrisKilled, storageValue = 1 }, + { storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessMachine, storageValue = 1 }, + { storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessLavaTeleport, storageValue = 1 }, { storage = Storage.Quest.U8_0.BarbarianTest.Questline, storageValue = 8 }, { storage = Storage.Quest.U8_0.BarbarianTest.Mission01, storageValue = 3 }, { storage = Storage.Quest.U8_0.BarbarianTest.Mission02, storageValue = 3 }, @@ -265,14 +265,14 @@ local questTable = { { storage = Storage.BanutaSecretTunnel.DeeperBanutaShortcut, storageValue = 1 }, { storage = Storage.Quest.U10_50.OramondQuest.QuestLine, storageValue = 1 }, { storage = Storage.Quest.U10_50.OramondQuest.ToTakeRoots.Mission, storageValue = 3000 }, - { storage = Storage.DangerousDepths.Questline, storageValue = 1 }, - { storage = Storage.DangerousDepths.Dwarves.Home, storageValue = 2 }, - { storage = Storage.DangerousDepths.Dwarves.Subterranean, storageValue = 2 }, - { storage = Storage.DangerousDepths.Gnomes.Measurements, storageValue = 2 }, - { storage = Storage.DangerousDepths.Gnomes.Ordnance, storageValue = 3 }, - { storage = Storage.DangerousDepths.Gnomes.Charting, storageValue = 2 }, - { storage = Storage.DangerousDepths.Scouts.Growth, storageValue = 2 }, - { storage = Storage.DangerousDepths.Scouts.Diremaw, storageValue = 2 }, + { storage = Storage.Quest.U11_50.DangerousDepths.Questline, storageValue = 1 }, + { storage = Storage.Quest.U11_50.DangerousDepths.Dwarves.Home, storageValue = 2 }, + { storage = Storage.Quest.U11_50.DangerousDepths.Dwarves.Subterranean, storageValue = 2 }, + { storage = Storage.Quest.U11_50.DangerousDepths.Gnomes.Measurements, storageValue = 2 }, + { storage = Storage.Quest.U11_50.DangerousDepths.Gnomes.Ordnance, storageValue = 3 }, + { storage = Storage.Quest.U11_50.DangerousDepths.Gnomes.Charting, storageValue = 2 }, + { storage = Storage.Quest.U11_50.DangerousDepths.Scouts.Growth, storageValue = 2 }, + { storage = Storage.Quest.U11_50.DangerousDepths.Scouts.Diremaw, storageValue = 2 }, { 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 }, @@ -282,12 +282,12 @@ local questTable = { { storage = Storage.Quest.U10_80.TheLostBrotherQuest, 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 }, - { storage = Storage.ForgottenKnowledge.AccessViolet, storageValue = 1 }, - { storage = Storage.ForgottenKnowledge.AccessEarth, storageValue = 1 }, - { storage = Storage.ForgottenKnowledge.AccessFire, storageValue = 1 }, - { storage = Storage.ForgottenKnowledge.AccessIce, storageValue = 1 }, - { storage = Storage.ForgottenKnowledge.AccessGolden, storageValue = 1 }, + { storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessDeath, storageValue = 1 }, + { storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessViolet, storageValue = 1 }, + { storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessEarth, storageValue = 1 }, + { storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessFire, storageValue = 1 }, + { storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessIce, storageValue = 1 }, + { storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessGolden, storageValue = 1 }, { storage = Storage.Quest.U10_80.GrimvaleQuest.AncientFeudDoors, storageValue = 1 }, { storage = Storage.Quest.U10_80.GrimvaleQuest.AncientFeudShortcut, storageValue = 1 }, { storage = Storage.Kilmaresh.AccessDoor, storageValue = 1 }, @@ -302,13 +302,13 @@ local questTable = { { storage = Storage.Quest.U12_60.APiratesTail.RascacoonShortcut, storageValue = 1 }, { storage = Storage.Quest.U12_70.AdventuresOfGalthen.AccessDoor, storageValue = 1 }, - { storage = Storage.CultsOfTibia.Barkless.AccessDoor, storageValue = 1 }, - { storage = Storage.CultsOfTibia.Barkless.TrialAccessDoor, storageValue = 1 }, - { storage = Storage.CultsOfTibia.Barkless.TarAccessDoor, storageValue = 1 }, - { storage = Storage.CultsOfTibia.Barkless.BossAccessDoor, storageValue = 1 }, - { storage = Storage.CultsOfTibia.Life.AccessDoor, storageValue = 1 }, - { storage = Storage.CultsOfTibia.Misguided.AccessDoor, storageValue = 1 }, - { storage = Storage.CultsOfTibia.FinalBoss.AccessDoor, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.AccessDoor, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.TrialAccessDoor, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.TarAccessDoor, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.BossAccessDoor, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Life.AccessDoor, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.AccessDoor, storageValue = 1 }, + { storage = Storage.Quest.U11_40.CultsOfTibia.FinalBoss.AccessDoor, storageValue = 1 }, { storage = Storage.Quest.U10_90.FerumbrasAscension.FirstDoor, storageValue = 1 }, { storage = Storage.Quest.U10_90.FerumbrasAscension.MonsterDoor, storageValue = 1 }, @@ -328,20 +328,20 @@ local questTable = { { storage = Storage.Quest.U8_6.WrathOfTheEmperor.TeleportAccess.Zizzle, storageValue = 1 }, { storage = Storage.Quest.U8_6.WrathOfTheEmperor.TeleportAccess.Zlak, storageValue = 1 }, - { storage = Storage.TheSecretLibrary.FalconBastionAccess, storageValue = 1 }, - { storage = Storage.TheSecretLibrary.LowerBastionAccess, storageValue = 1 }, - { storage = Storage.TheSecretLibrary.UndergroundBastionAccess, storageValue = 1 }, - { storage = Storage.TheSecretLibrary.ShortcutToBastion, storageValue = 1 }, - { storage = Storage.TheSecretLibrary.OberonAccess, storageValue = 1 }, - { storage = Storage.TheSecretLibrary.MotaDoor, storageValue = 1 }, - { storage = Storage.TheSecretLibrary.BasinDoor, storageValue = 1 }, - { storage = Storage.TheSecretLibrary.SkullDoor, storageValue = 1 }, - { storage = Storage.TheSecretLibrary.Mota, storageValue = 1 }, - { storage = Storage.TheSecretLibrary.MiniBosses.PreceptorLazare, storageValue = 1 }, - { storage = Storage.TheSecretLibrary.MiniBosses.GrandCanonDominus, storageValue = 1 }, - { storage = Storage.TheSecretLibrary.MiniBosses.GrandChaplainGaunder, storageValue = 1 }, - { storage = Storage.TheSecretLibrary.MiniBosses.GrandCommanderSoeren, storageValue = 1 }, - { storage = Storage.TheSecretLibrary.MiniBosses.DazedLeafGolem, storageValue = 1 }, + --[[ { storage = Storage.Quest.U11_80.TheSecretLibrary.FalconBastionAccess, storageValue = 1 }, + { storage = Storage.Quest.U11_80.TheSecretLibrary.LowerBastionAccess, storageValue = 1 }, + { storage = Storage.Quest.U11_80.TheSecretLibrary.UndergroundBastionAccess, storageValue = 1 }, + { storage = Storage.Quest.U11_80.TheSecretLibrary.ShortcutToBastion, storageValue = 1 }, + { storage = Storage.Quest.U11_80.TheSecretLibrary.OberonAccess, storageValue = 1 }, + { storage = Storage.Quest.U11_80.TheSecretLibrary.MotaDoor, storageValue = 1 }, + { storage = Storage.Quest.U11_80.TheSecretLibrary.BasinDoor, storageValue = 1 }, + { storage = Storage.Quest.U11_80.TheSecretLibrary.SkullDoor, storageValue = 1 }, + { storage = Storage.Quest.U11_80.TheSecretLibrary.Mota, storageValue = 1 }, + { storage = Storage.Quest.U11_80.TheSecretLibrary.MiniBosses.PreceptorLazare, storageValue = 1 }, + { storage = Storage.Quest.U11_80.TheSecretLibrary.MiniBosses.GrandCanonDominus, storageValue = 1 }, + { storage = Storage.Quest.U11_80.TheSecretLibrary.MiniBosses.GrandChaplainGaunder, storageValue = 1 }, + { storage = Storage.Quest.U11_80.TheSecretLibrary.MiniBosses.GrandCommanderSoeren, storageValue = 1 }, + { storage = Storage.Quest.U11_80.TheSecretLibrary.MiniBosses.DazedLeafGolem, storageValue = 1 }, ]] { storage = Storage.Quest.U7_4.DjinnWar.EfreetFaction.DoorToLamp, storageValue = 1 }, { storage = Storage.Quest.U7_4.DjinnWar.EfreetFaction.DoorToMaridTerritory, storageValue = 1 }, diff --git a/data-otservbr-global/scripts/lib/register_actions.lua b/data-otservbr-global/scripts/lib/register_actions.lua index ebb77654882..a65de89e017 100644 --- a/data-otservbr-global/scripts/lib/register_actions.lua +++ b/data-otservbr-global/scripts/lib/register_actions.lua @@ -58,6 +58,22 @@ local lava = { Position(32813, 32333, 11), } +local secret_library = { + crystals = { + [1] = { storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.Crystal1, position = Position(33216, 32108, 9) }, + [2] = { storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.Crystal2, position = Position(33242, 32100, 9) }, + [3] = { storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.Crystal3, position = Position(33226, 32103, 9) }, + [4] = { storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.Crystal4, position = Position(33236, 32084, 9) }, + [5] = { storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.Crystal5, position = Position(33260, 32103, 9) }, + [6] = { storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.Crystal6, position = Position(33260, 32103, 9) }, + [7] = { storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.Crystal7, position = Position(33260, 32103, 9) }, + [8] = { storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.Crystal8, position = Position(33260, 32103, 9) }, + }, + timer = "tsl_crystaltimer", + exhaustMessage = "Digging crystal is exhausting. You're still weary from your last prospect.", + items = { 27867, 27868, 27869 }, +} + local function revertItem(position, itemId, transformId) local item = Tile(position):getItemById(itemId) if item then @@ -504,6 +520,23 @@ function onUsePick(player, item, fromPosition, target, toPosition, isHotkey) end end + -- The Secret Library Quest + local tPos = toPosition + for _, j in pairs(secret_library.crystals) do + if tPos == j.position then + if player:getStorageValue(j.storage) < os.time() then + local r = math.random(1, 3) + local item_id = secret_library.items[r] + player:addItem(item_id, 1) + player:say("You have found a " .. ItemType(item_id):getName() .. ".", TALKTYPE_MONSTER_SAY) + player:setStorageValue(j.storage, os.time() + 2 * 60) + else + player:say(secret_library.exhaustMessage, TALKTYPE_MONSTER_SAY) + end + return true + end + end + -- The Rookie Guard Quest - Mission 09: Rock 'n Troll -- Path: data\scripts\actions\quests\the_rookie_guard\mission09_rock_troll.lua -- Damage tunnel pillars @@ -957,12 +990,18 @@ function onUseScythe(player, item, fromPosition, target, toPosition, isHotkey) target:decay() Game.createItem(30975, 1, toPosition) return true - -- The secret library + -- The Secret Library Quest elseif toPosition == Position(32177, 31925, 7) then - player:teleportTo({ x = 32515, y = 32535, z = 12 }) + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.LibraryPermission) == 7 then + player:teleportTo({ x = 32515, y = 32535, z = 12 }) + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + else + Position(32177, 31925, 7):sendMagicEffect(CONST_ME_POFF) + end else return false end + return onDestroyItem(player, item, fromPosition, target, toPosition, isHotkey) end @@ -971,6 +1010,23 @@ function onUseKitchenKnife(player, item, fromPosition, target, toPosition, isHot return false end + -- The Secret Library Quest + local tPos = toPosition + for _, j in pairs(secret_library.crystals) do + if tPos == j.position then + if player:getStorageValue(j.storage) < os.time() then + local r = math.random(1, 3) + local item_id = secret_library.items[r] + player:addItem(item_id, 1) + player:say("You have found a " .. ItemType(item_id):getName() .. ".", TALKTYPE_MONSTER_SAY) + player:setStorageValue(j.storage, os.time() + 2 * 60) + else + player:say(secret_library.exhaustMessage, TALKTYPE_MONSTER_SAY) + end + return true + end + end + local targetId = target:getId() --The Ice Islands Quest - Cure the Dogs diff --git a/data-otservbr-global/scripts/movements/teleport/falcon_castle.lua b/data-otservbr-global/scripts/movements/teleport/falcon_castle.lua deleted file mode 100644 index a7473748602..00000000000 --- a/data-otservbr-global/scripts/movements/teleport/falcon_castle.lua +++ /dev/null @@ -1,71 +0,0 @@ -local teleports = { - { position = { x = 33306, y = 31349, z = 8 }, destination = Position(33349, 31346, 8) }, - { position = { x = 33310, y = 31325, z = 8 }, access = Storage.TheSecretLibrary.OberonAccess, destination = Position(33329, 31332, 9) }, - { position = { x = 33329, y = 31333, z = 9 }, destination = Position(33308, 31325, 8) }, - { - positions = { - { x = 33296, y = 31287, z = 9 }, - { x = 33297, y = 31287, z = 9 }, - { x = 33298, y = 31287, z = 9 }, - { x = 33296, y = 31288, z = 9 }, - { x = 33298, y = 31288, z = 9 }, - { x = 33298, y = 31289, z = 9 }, - { x = 33297, y = 31289, z = 9 }, - }, - destination = Position(33363, 31342, 9), - }, -} - -local falconCastle = MoveEvent() - -function falconCastle.onStepIn(creature, item, position, fromPosition) - local player = creature:getPlayer() - if not player then - return false - end - for c = 1, #teleports do - if teleports[c].position then - if player:getPosition() == Position(teleports[c].position) then - if teleports[c].access then - if player:getStorageValue(teleports[c].access) == 1 then - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - player:teleportTo(teleports[c].destination) - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - else - player:teleportTo(fromPosition) - end - else - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - player:teleportTo(teleports[c].destination) - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - end - end - elseif teleports[c].positions then - for d = 1, #teleports[c].positions do - if player:getPosition() == Position(teleports[c].positions[d]) then - if player:getStorageValue(Storage.TheSecretLibrary.TheOrderOfTheFalcon.OberonTimer) > os.time() then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have faced this boss in the last 20 hours.") - player:teleportTo(fromPosition) - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - return false - else - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - player:teleportTo(teleports[c].destination) - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - end - end - end - end - end -end - -for a = 1, #teleports do - if teleports[a].position then - falconCastle:position(teleports[a].position) - elseif teleports[a].positions then - for b = 1, #teleports[a].positions do - falconCastle:position(teleports[a].positions[b]) - end - end -end -falconCastle:register() diff --git a/data-otservbr-global/scripts/quests/cradle_of_monsters/the_monster_fight.lua b/data-otservbr-global/scripts/quests/cradle_of_monsters/the_monster_fight.lua index ac10eead616..d85bd3491e5 100644 --- a/data-otservbr-global/scripts/quests/cradle_of_monsters/the_monster_fight.lua +++ b/data-otservbr-global/scripts/quests/cradle_of_monsters/the_monster_fight.lua @@ -196,6 +196,7 @@ local function setShields(creature, count) end local monsterHealth = CreatureEvent("fight.the-monster.TheMonsterHealth") + function monsterHealth.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) if not creature then return primaryDamage, primaryType, secondaryDamage, secondaryType diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_analyser.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_analyser.lua index 17b7ffbc976..ce01d36c244 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_analyser.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_analyser.lua @@ -14,9 +14,9 @@ function cultsOfTibiaAnalyser.onUse(player, item, fromPosition, target, toPositi return false end - if player:getStorageValue(Storage.CultsOfTibia.Life.Mission) == 4 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission) == 4 then if target:getPosition() == sqm or target:getPosition() == Position(sqm.x, sqm.y + 1, sqm.z) or target:getPosition() == Position(sqm.x, sqm.y + 2, sqm.z) then - player:setStorageValue(Storage.CultsOfTibia.Life.Mission, 5) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission, 5) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Well done! The water is analyzed.") end end diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_bosses_levers.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_bosses_levers.lua index 61fb460fd72..cf30097533c 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_bosses_levers.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_bosses_levers.lua @@ -34,7 +34,7 @@ end local function spawnDarkSoul(area, t_time) if t_time == 0 then - local esperandoPlayer = false + local waitingForPlayer = false local p1 = Position(33023, 31904, 14) local p2 = Position(33037, 31915, 14) local sA1 = Position(33028, 31908, 14) @@ -51,45 +51,43 @@ local function spawnDarkSoul(area, t_time) if not isPlayerInArea(a1, a2) then return true end - esperandoPlayer = true + waitingForPlayer = true else - local monster = {} + local monsters = {} for _x = sA1.x, sA2.x, 1 do for _y = sA1.y, sA2.y, 1 do local tileMonster = Tile(Position(_x, _y, sA1.z)):getTopCreature() if tileMonster and tileMonster:isMonster() and tileMonster:getName() == "Dark Soul" then - monster[#monster + 1] = tileMonster + monsters[#monsters + 1] = tileMonster end end end - if #monster >= 4 then - for _, pid in pairs(monster) do + if #monsters >= 4 then + for _, pid in pairs(monsters) do pid:remove() end end - -- spawn local newPos = Position(math.random(sA1.x, sA2.x), math.random(sA1.y, sA2.y), math.random(sA1.z, sA2.z)) Game.createMonster("Dark Soul", newPos) end - addEvent(spawnDarkSoul, 1000, area, (esperandoPlayer and 1 or 30)) + addEvent(spawnDarkSoul, 1000, area, (waitingForPlayer and 1 or 30)) else addEvent(spawnDarkSoul, 1000, area, t_time - 1) end end local function transformMonster(itid, action, monster, frompos, topos, _temp) - -- minotaur idol if action == 1 then - local tempo = _temp + local delay = _temp for _x = frompos.x, topos.x, 1 do local tile = Tile(Position(_x, frompos.y, frompos.z)) if _x % 2 == 0 then - tempo = tempo + 1 + delay = delay + 1 if tile then if tile:getItemCountById(itid) < 1 then Game.createItem(itid, 1, Position(_x, frompos.y, frompos.z)) end - addEvent(transformMonster, tempo * 15000, itid, 2, monster, Position(_x, frompos.y, frompos.z), {}, _temp + 1) + addEvent(transformMonster, delay * 15000, itid, 2, monster, Position(_x, frompos.y, frompos.z), {}, _temp + 1) end end end @@ -110,31 +108,31 @@ local function transformMonster(itid, action, monster, frompos, topos, _temp) end end -local function ativarGeyser(player) - local frompos = Position(33119, 31913, 15) -- Checagem - local topos = Position(33142, 31936, 15) -- Checagem +local function activateGeyser(player) + local frompos = Position(33119, 31913, 15) + local topos = Position(33142, 31936, 15) if isPlayerInArea(frompos, topos) then addEvent(function() local rand = math.random(1, 12) local geyserPos = Position(geyser[rand]) - local checar1 = Tile(Position(geyserPos)):getItemById(25509) - if checar1 then + local check1 = Tile(Position(geyserPos)):getItemById(25509) + if check1 then addEvent(function() local player1 = Game.getPlayers()[1] Game.createItem(25510, 1, geyserPos) player1:say("SPLASH!", TALKTYPE_MONSTER_SAY, false, false, geyserPos) addEvent(function() - local checar2 = Tile(Position(geyserPos)):getItemById(25510) - if checar2 then - checar2:remove() + local check2 = Tile(Position(geyserPos)):getItemById(25510) + if check2 then + check2:remove() end end, 9 * 1000) end, 5 * 1000) - elseif checar2 then + elseif check2 then return false end addEvent(function() - ativarGeyser() + activateGeyser() end, 1 * 1000) end, 8 * 1000) end @@ -142,6 +140,7 @@ local function ativarGeyser(player) end local cultsOfTibiaLevers = Action() + function cultsOfTibiaLevers.onUse(player, item, fromPosition, itemEx, toPosition) local players = {} local ittable = {} @@ -149,19 +148,26 @@ function cultsOfTibiaLevers.onUse(player, item, fromPosition, itemEx, toPosition local convertTable = {} item:transform(transformid[item:getId()]) - if item:getActionId() == 5501 and item:getId() == 8912 then -- Leiden + if item:getActionId() == 5501 and item:getId() == 8912 then if player:getPosition() == Position(33138, 31953, 15) then local teleport = 0 - for i = 31953, 31957, 1 do + local playersInArea = {} + local frompos = Position(33151, 31942, 15) + local topos = Position(33176, 31966, 15) + + for i = 31953, 31957 do local newpos = Position(33138, i, 15) local nplayer = Tile(newpos):getTopCreature() if nplayer and nplayer:isPlayer() then teleport = teleport + 1 + table.insert(playersInArea, nplayer) end end - local frompos = Position(33151, 31942, 15) -- Checagem - local topos = Position(33176, 31966, 15) -- Checagem + if teleport ~= 5 then + player:sendCancelMessage("You need exactly 5 players to start this challenge.") + return true + end if isPlayerInArea(frompos, topos) then player:sendCancelMessage("The room is full.") @@ -179,36 +185,41 @@ function cultsOfTibiaLevers.onUse(player, item, fromPosition, itemEx, toPosition end end - for i = 31953, 31957, 1 do - local newpos = Position(33138, i, 15) - local nplayer = Tile(newpos):getTopCreature() - if nplayer and nplayer:isPlayer() then - nplayer:setBossCooldown("Ravenous Hunger", os.time() + configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN)) - nplayer:teleportTo(Position(33161, 31959, 15), true) - nplayer:sendBosstiaryCooldownTimer() - convertTable[#convertTable + 1] = nplayer:getId() - nplayer:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - end + for _, nplayer in ipairs(playersInArea) do + nplayer:setBossCooldown("Ravenous Hunger", os.time() + configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN)) + nplayer:teleportTo(Position(33161, 31959, 15), true) + nplayer:sendBosstiaryCooldownTimer() + convertTable[#convertTable + 1] = nplayer:getId() + nplayer:getPosition():sendMagicEffect(CONST_ME_TELEPORT) end + Game.createMonster("Wine Cask", Position(33162, 31945, 15)) local leiden = Game.createMonster("Leiden", Position(33162, 31950, 15)) leiden:setHealth(leiden:getHealth() / 2) kickerPlayerRoomAfterMin(convertTable, frompos, topos, Position(33121, 31951, 15), "You were kicked for exceeding the time limit within the boss room.", "", 60, true, ittable, blockmonsters) end end - if item:getActionId() == 5502 and item:getId() == 8912 then -- The False God + + if item:getActionId() == 5502 and item:getId() == 8912 then if player:getPosition() == Position(33162, 31893, 15) then local teleport = 0 - for i = 31893, 31897, 1 do + local playersInArea = {} + local frompos = Position(33152, 31908, 15) + local topos = Position(33175, 31923, 15) + + for i = 31893, 31897 do local newpos = Position(33162, i, 15) local nplayer = Tile(newpos):getTopCreature() if nplayer and nplayer:isPlayer() then teleport = teleport + 1 + table.insert(playersInArea, nplayer) end end - local frompos = Position(33152, 31908, 15) -- Checagem - local topos = Position(33175, 31923, 15) -- Checagem + if teleport ~= 5 then + player:sendCancelMessage("You need exactly 5 players to start this challenge.") + return true + end if isPlayerInArea(frompos, topos) then player:sendCancelMessage("The room is full.") @@ -226,38 +237,41 @@ function cultsOfTibiaLevers.onUse(player, item, fromPosition, itemEx, toPosition end end - for i = 31893, 31897, 1 do - local newpos = Position(33162, i, 15) - local nplayer = Tile(newpos):getTopCreature() - if nplayer and nplayer:isPlayer() then - nplayer:setBossCooldown("The False God", os.time() + configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN)) - nplayer:teleportTo(Position(33169, 31915, 15), true) - convertTable[#convertTable + 1] = nplayer:getId() - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - end + for _, nplayer in ipairs(playersInArea) do + nplayer:setBossCooldown("The False God", os.time() + configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN)) + nplayer:teleportTo(Position(33169, 31915, 15), true) + convertTable[#convertTable + 1] = nplayer:getId() + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) end + transformMonster(25300, 1, "minotaur idol", Position(33157, 31910, 15), Position(33168, 31910, 15), 0) transformMonster(25300, 1, "minotaur idol", Position(33158, 31921, 15), Position(33168, 31921, 15), 6) addEvent(transformMonster, 13 * 15000, 3, 3, "Sphere Of Wrath", {}, {}, 0) Game.createMonster("The False God", Position(33159, 31914, 15)) - -- funçao kickerPlayerRoomAfterMin(convertTable, frompos, topos, Position(33181, 31894, 15), "You were kicked for exceeding the time limit within the boss room.", "", 60, true, ittable, blockmonsters) end end - if item:getActionId() == 5500 then -- Essence of Malice + if item:getActionId() == 5500 then if player:getPosition() == Position(33095, 31943, 15) and item:getId() == 8912 then local teleport = 0 - for i = 31943, 31947, 1 do + local playersInArea = {} + local frompos = Position(33084, 31907, 15) + local topos = Position(33114, 31933, 15) + + for i = 31943, 31947 do local newpos = Position(33095, i, 15) local nplayer = Tile(newpos):getTopCreature() if nplayer and nplayer:isPlayer() then teleport = teleport + 1 + table.insert(playersInArea, nplayer) end end - local frompos = Position(33084, 31907, 15) -- Checagem - local topos = Position(33114, 31933, 15) -- Checagem + if teleport ~= 1 then + player:sendCancelMessage("You need exactly 5 players to start this challenge.") + return true + end if isPlayerInArea(frompos, topos) then player:sendCancelMessage("It looks like there is someone inside.") @@ -275,16 +289,13 @@ function cultsOfTibiaLevers.onUse(player, item, fromPosition, itemEx, toPosition end end - for i = 31943, 31947, 1 do - local newpos = Position(33095, i, 15) - local nplayer = Tile(newpos):getTopCreature() - if nplayer and nplayer:isPlayer() then - nplayer:setBossCooldown("Essence of Malice", os.time() + configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN)) - nplayer:teleportTo(Position(33098, 31921, 15), true) - convertTable[#convertTable + 1] = nplayer:getId() - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - end + for _, nplayer in ipairs(playersInArea) do + nplayer:setBossCooldown("Essence of Malice", os.time() + configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN)) + nplayer:teleportTo(Position(33098, 31921, 15), true) + convertTable[#convertTable + 1] = nplayer:getId() + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) end + kickerPlayerRoomAfterMin(convertTable, frompos, topos, Position(33091, 31963, 15), "You were kicked for exceeding the time limit within the boss room.", "", 60, true, ittable, blockmonsters) Game.createMonster("Pillar of Summoning", Position(33093, 31919, 15)) Game.createMonster("Pillar of Death", Position(33098, 31915, 15)) @@ -299,19 +310,26 @@ function cultsOfTibiaLevers.onUse(player, item, fromPosition, itemEx, toPosition end end - if item:getActionId() == 5503 then -- The Souldespoiler + if item:getActionId() == 5503 then if player:getPosition() == Position(33127, 31892, 15) and item:getId() == 8912 then local teleport = 0 - for i = 31892, 31896, 1 do + local playersInArea = {} + local frompos = Position(33119, 31913, 15) + local topos = Position(33142, 31936, 15) + + for i = 31892, 31896 do local newpos = Position(33127, i, 15) local nplayer = Tile(newpos):getTopCreature() if nplayer and nplayer:isPlayer() then teleport = teleport + 1 + table.insert(playersInArea, nplayer) end end - local frompos = Position(33119, 31913, 15) -- Checagem - local topos = Position(33142, 31936, 15) -- Checagem + if teleport ~= 5 then + player:sendCancelMessage("You need exactly 5 players to start this challenge.") + return true + end if isPlayerInArea(frompos, topos) then player:sendCancelMessage("It looks like there is someone inside.") @@ -329,35 +347,39 @@ function cultsOfTibiaLevers.onUse(player, item, fromPosition, itemEx, toPosition end end - for i = 31892, 31896, 1 do - local newpos = Position(33127, i, 15) - local nplayer = Tile(newpos):getTopCreature() - if nplayer and nplayer:isPlayer() then - nplayer:setBossCooldown("The Souldespoiler", os.time() + configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN)) - nplayer:teleportTo(Position(33130, 31919, 15), true) - convertTable[#convertTable + 1] = nplayer:getId() - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - end + for _, nplayer in ipairs(playersInArea) do + nplayer:setBossCooldown("The Souldespoiler", os.time() + configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN)) + nplayer:teleportTo(Position(33130, 31919, 15), true) + convertTable[#convertTable + 1] = nplayer:getId() + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) end + kickerPlayerRoomAfterMin(convertTable, frompos, topos, Position(33109, 31887, 15), "You were kicked for exceeding the time limit within the boss room.", "", 60, true, ittable, blockmonsters) Game.createMonster("The Sinister Hermit Dirty", Position(33131, 31925, 15)) - ativarGeyser() + activateGeyser() end end - if item:getActionId() == 5504 then -- Boss do orc + if item:getActionId() == 5504 then if player:getPosition() == Position(33164, 31859, 15) and item:getId() == 8912 then local teleport = 0 - for y = 31859, 31863, 1 do + local playersInArea = {} + local frompos = Position(33123, 31846, 15) + local topos = Position(33149, 31871, 15) + + for y = 31859, 31863 do local newpos = Position(33164, y, 15) local nplayer = Tile(newpos):getTopCreature() if nplayer and nplayer:isPlayer() then teleport = teleport + 1 + table.insert(playersInArea, nplayer) end end - local frompos = Position(33123, 31846, 15) -- Checagem - local topos = Position(33149, 31871, 15) -- Checagem + if teleport ~= 5 then + player:sendCancelMessage("You need exactly 5 players to start this challenge.") + return true + end if isPlayerInArea(frompos, topos) then player:sendCancelMessage("It looks like there is someone inside.") @@ -375,17 +397,14 @@ function cultsOfTibiaLevers.onUse(player, item, fromPosition, itemEx, toPosition end end - for y = 31859, 31863, 1 do - local newpos = Position(33164, y, 15) - local nplayer = Tile(newpos):getTopCreature() - if nplayer and nplayer:isPlayer() then - nplayer:setBossCooldown("The Armored Voidborn", os.time() + configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN)) - nplayer:teleportTo(Position(33137, 31867, 15), true) - convertTable[#convertTable + 1] = nplayer:getId() - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - end + for _, nplayer in ipairs(playersInArea) do + nplayer:setBossCooldown("The Armored Voidborn", os.time() + configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN)) + nplayer:teleportTo(Position(33137, 31867, 15), true) + convertTable[#convertTable + 1] = nplayer:getId() + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) end - local function criarRaio1(fromPos, toPos, id, dir) + + local function createBeam1(fromPos, toPos, id, dir) if dir == 1 then for _x = fromPos.x, toPos.x, 1 do local tile = Tile(Position(_x, fromPos.y, fromPos.z)) @@ -402,26 +421,26 @@ function cultsOfTibiaLevers.onUse(player, item, fromPosition, itemEx, toPosition end end end - local itensToMonster = { --8633 + + local itemsToMonster = { Position(33133, 31856, 15), Position(33140, 31856, 15), Position(33140, 31863, 15), Position(33133, 31863, 15), } - -- criando os itens - for _, position in pairs(itensToMonster) do + + for _, position in pairs(itemsToMonster) do local tile = Tile(position) if tile and tile:getItemCountById(7805) < 1 then Game.createItem(7805, 1, position) end end - -- criando os raios - criarRaio1(Position(33134, 31856, 15), Position(33139, 31856, 15), 6116, 1) - criarRaio1(Position(33134, 31863, 15), Position(33139, 31863, 15), 6116, 1) - criarRaio1(Position(33140, 31857, 15), Position(33140, 31862, 15), 6117, 2) - criarRaio1(Position(33133, 31857, 15), Position(33133, 31862, 15), 6117, 2) - -- criando os securys + createBeam1(Position(33134, 31856, 15), Position(33139, 31856, 15), 6116, 1) + createBeam1(Position(33134, 31863, 15), Position(33139, 31863, 15), 6116, 1) + createBeam1(Position(33140, 31857, 15), Position(33140, 31862, 15), 6117, 2) + createBeam1(Position(33133, 31857, 15), Position(33133, 31862, 15), 6117, 2) + Game.createMonster("Security Golem", Position(33131, 31855, 15)) Game.createMonster("Security Golem", Position(33142, 31855, 15)) Game.createMonster("Security Golem", Position(33141, 31863, 15)) @@ -432,19 +451,28 @@ function cultsOfTibiaLevers.onUse(player, item, fromPosition, itemEx, toPosition kickerPlayerRoomAfterMin(convertTable, frompos, topos, Position(33179, 31840, 15), "You were kicked for exceeding the time limit within the boss room.", "", 60, true, ittable, blockmonsters) end end - if item:getActionId() == 5505 then -- Boss da areia + + if item:getActionId() == 5505 then if player:getPosition() == Position(33507, 32228, 10) and item:getId() == 8912 then local teleport = 0 - for _y = 32228, 32232, 1 do + local teleport = 0 + local playersInArea = {} + local frompos = Position(33087, 31848, 15) + local topos = Position(33109, 31871, 15) + + for _y = 32228, 32232 do local newpos = Position(33507, _y, 10) local nplayer = Tile(newpos):getTopCreature() if nplayer and nplayer:isPlayer() then teleport = teleport + 1 + table.insert(playersInArea, nplayer) end end - local frompos = Position(33087, 31848, 15) -- Checagem - local topos = Position(33109, 31871, 15) -- Checagem + if teleport ~= 5 then + player:sendCancelMessage("You need exactly 5 players to start this challenge.") + return true + end if isPlayerInArea(frompos, topos) then player:sendCancelMessage("It looks like there is someone inside.") @@ -472,15 +500,11 @@ function cultsOfTibiaLevers.onUse(player, item, fromPosition, itemEx, toPosition end end - for _y = 32228, 32232, 1 do - local newpos = Position(33507, _y, 10) - local nplayer = Tile(newpos):getTopCreature() - if nplayer and nplayer:isPlayer() then - nplayer:setBossCooldown("The Sandking", os.time() + configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN)) - nplayer:teleportTo(Position(33099, 31864, 15), true) - convertTable[#convertTable + 1] = nplayer:getId() - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - end + for _, nplayer in ipairs(playersInArea) do + nplayer:setBossCooldown("The Sandking", os.time() + configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN)) + nplayer:teleportTo(Position(33099, 31864, 15), true) + convertTable[#convertTable + 1] = nplayer:getId() + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) end Game.createMonster("the sandking fake", Position(33099, 31858, 15)):registerEvent("SandkingThink") @@ -489,39 +513,67 @@ function cultsOfTibiaLevers.onUse(player, item, fromPosition, itemEx, toPosition end end - -- final boss if item:getActionId() == 5506 then if player:getPosition() == Position(33074, 31884, 15) and item:getId() == 8912 then - local convertTable = {} - convertTable[#convertTable + 1] = player:getId() + local teleport = 0 + local playersInArea = {} + local frompos = Position(33023, 31904, 14) + local topos = Position(33052, 31932, 15) + + if player:getPosition() == Position(33074, 31884, 15) then + teleport = teleport + 1 + table.insert(playersInArea, player) + else + return true + end + + local pt1 = Position(33073, 31885, 15) + local pt2 = Position(33075, 31887, 15) + for _x = pt1.x, pt2.x do + for _y = pt1.y, pt2.y do + local nplayer = Tile(Position(_x, _y, 15)):getTopCreature() + if nplayer and nplayer:isPlayer() then + teleport = teleport + 1 + table.insert(playersInArea, nplayer) + end + end + end - local frompos = Position(33023, 31904, 14) -- Checagem - local topos = Position(33052, 31932, 15) -- Checagem + if teleport ~= 10 then + player:sendCancelMessage("You need exactly 10 players to start this challenge.") + return true + end if isPlayerInArea(frompos, topos) then player:sendCancelMessage("It looks like there is someone inside.") return true end - local pt1 = Position(33073, 31885, 15) - local pt2 = Position(33075, 31887, 15) - for _x = pt1.x, pt2.x, 1 do - for _y = pt1.y, pt2.y, 1 do - for _z = pt1.z, pt2.z, 1 do - local nplayer = Tile(Position(_x, _y, _z)):getTopCreature() - if nplayer and nplayer:isPlayer() then - convertTable[#convertTable + 1] = nplayer:getId() + for _x = frompos.x, topos.x do + for _y = frompos.y, topos.y do + for _z = frompos.z, topos.z do + local tile = Tile(Position(_x, _y, _z)) + if tile and tile:getTopCreature() and tile:getTopCreature():isMonster() then + tile:getTopCreature():remove() + end + if tile then + local tileItems = tile:getItems() + if type(tileItems) == "table" and #tileItems > 0 then + for _, it in pairs(tileItems) do + if ItemType(it:getId()):isCorpse() then + it:remove() + end + end + end end end end end - for _, pid in pairs(convertTable) do - local nplayer = Player(pid) - if nplayer then - nplayer:setBossCooldown("The Source Of Corruption", os.time() + configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN)) - nplayer:teleportTo(Position(33039, 31925, 15), true) - nplayer:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - end + + for _, nplayer in ipairs(playersInArea) do + nplayer:setBossCooldown("The Source Of Corruption", os.time() + configManager.getNumber(configKeys.BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN)) + nplayer:teleportTo(Position(33039, 31925, 15), true) + nplayer:getPosition():sendMagicEffect(CONST_ME_TELEPORT) end Game.createMonster("The Remorseless Corruptor", Position(33039, 31922, 15)) @@ -531,9 +583,11 @@ function cultsOfTibiaLevers.onUse(player, item, fromPosition, itemEx, toPosition spawnDarkSoul(1, 30) spawnDarkSoul(2, 30) spawnStolenSoul(30) - kickerPlayerRoomAfterMin(convertTable, frompos, topos, Position(33072, 31867, 15), "You were kicked for exceeding the time limit within the boss room.", "", 60, true, ittable, blockmonsters) + + kickerPlayerRoomAfterMin(playersInArea, frompos, topos, Position(33072, 31867, 15), "You were kicked for exceeding the time limit within the boss room.", "", 60, true, ittable, blockmonsters) end end + return true end diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_counter_agent.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_counter_agent.lua index b59b1508430..24b414399ee 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_counter_agent.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_counter_agent.lua @@ -27,10 +27,10 @@ function cultsOfTibiaCounter.onUse(player, item, fromPosition, target, toPositio return false end - if player:getStorageValue(Storage.CultsOfTibia.Life.Mission) == 6 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission) == 6 then if target:getPosition() == sqm or target:getPosition() == Position(sqm.x, sqm.y + 1, sqm.z) or target:getPosition() == Position(sqm.x, sqm.y + 2, sqm.z) then player:teleportTo(destino) - player:setStorageValue(Storage.CultsOfTibia.Life.Mission, 7) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission, 7) for i, position in pairs(config) do position:sendMagicEffect(CONST_ME_POFF) end diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_crate.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_crate.lua index 0cc39a78e51..33ef2a91c0f 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_crate.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_crate.lua @@ -3,11 +3,11 @@ function cultsOfTibiaCrate.onUse(player, item, fromPosition, target, toPosition, local posCrate = Position(33300, 32277, 12) -- Document if item:getPosition() == posCrate then - if player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) == 7 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) == 7 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Perfect! You took a wonderful loupe from this crate.") player:addItem(25306, 1) - player:setStorageValue(Storage.CultsOfTibia.MotA.Mission, 8) - elseif player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) > 7 then + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission, 8) + elseif player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) > 7 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You already took the magnifier.") else player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "This crate belongs to Angelo. You should talk to him before taking something out.") diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_cult_symbol.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_cult_symbol.lua index 80a23f7eb63..6663a616b27 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_cult_symbol.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_cult_symbol.lua @@ -16,12 +16,11 @@ local config = { local cultsOfTibiaCult = Action() function cultsOfTibiaCult.onUse(player, item, fromPosition, target, toPosition, isHotkey) - local stg = math.max(player:getStorageValue(Storage.CultsOfTibia.Barkless.Objects), 0) + local stg = math.max(player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Objects), 0) if stg >= 10 then - if player:getStorageValue(Storage.CultsOfTibia.Barkless.Mission) >= 4 then - -- continue + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Mission) >= 4 then else - player:setStorageValue(Storage.CultsOfTibia.Barkless.Mission, 4) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Mission, 4) end return false end @@ -32,19 +31,19 @@ function cultsOfTibiaCult.onUse(player, item, fromPosition, target, toPosition, break end end - local stgTemp = math.max(player:getStorageValue(Storage.CultsOfTibia.Barkless.Temp), 0) + local stgTemp = math.max(player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Temp), 0) local manchaMeta = NewBit(stgTemp) local base = bit.lshift(1, mancha.base) if manchaMeta:hasFlag(base) then return false end manchaMeta:updateFlag(base) - player:setStorageValue(Storage.CultsOfTibia.Barkless.Temp, manchaMeta:getNumber()) - player:setStorageValue(Storage.CultsOfTibia.Barkless.Objects, stg + 1) - if player:getStorageValue(Storage.CultsOfTibia.Barkless.Objects) >= 10 then - player:setStorageValue(Storage.CultsOfTibia.Barkless.Mission, 4) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Temp, manchaMeta:getNumber()) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Objects, stg + 1) + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Objects) >= 10 then + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Mission, 4) end - local msg = (player:getStorageValue(Storage.CultsOfTibia.Barkless.Objects) < 10 and "Your body reacts to this strange green substance as you reach out to touch it. You feel an urge for more of this energy." or "You gathered an impressive amount of power from simply touching the strange green symbols of the Barkless. But how...?") + local msg = (player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Objects) < 10 and "Your body reacts to this strange green substance as you reach out to touch it. You feel an urge for more of this energy." or "You gathered an impressive amount of power from simply touching the strange green symbols of the Barkless. But how...?") player:sendTextMessage(MESSAGE_EVENT_ADVANCE, msg) return true end diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_document.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_document.lua index 50c81e864b6..93b96264d53 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_document.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_document.lua @@ -1,17 +1,18 @@ local cultsOfTibiaDocument = Action() + function cultsOfTibiaDocument.onUse(player, item, fromPosition, target, toPosition, isHotkey) local posDocument = Position(33279, 32169, 8) - -- Document if item:getPosition() == posDocument then - if player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) == 2 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) == 2 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Dear curator, this recently opened museum is a really nice place to be.") player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "But wait! What about the empty space in front of you? What a pity!") player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "It seems that somebody has removed one of the beautiful pictures. But come on! You have the money and we need it.") player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "So for a small expense allowance you'll get it back. Just talk to Iwar in Kazordoon for further information. ") player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Ask him: Has the cat got your tongue?") - player:setStorageValue(Storage.CultsOfTibia.MotA.Mission, 3) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission, 3) end end + return true end diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_last_object.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_last_object.lua index 565da77e68c..be82c316271 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_last_object.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_last_object.lua @@ -14,16 +14,17 @@ local config = { } local cultsOfTibiaLastObject = Action() + function cultsOfTibiaLastObject.onUse(player, item) - if player:getStorageValue(Storage.CultsOfTibia.Barkless.Mission) == 4 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Mission) == 4 then for i, position in pairs(config) do position:sendMagicEffect(CONST_ME_YELLOWENERGY) end player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The strange symbol in the wall in the back glows violently.") player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The cult object just lies there quietly. The dangerous area around it has been all but dissolved.") player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "As you reach out to touch the cult object, a surge of energy burst from the skull. You banished its power.") - player:setStorageValue(Storage.CultsOfTibia.Barkless.Mission, 5) - player:setStorageValue(Storage.CultsOfTibia.Barkless.BossAccessDoor, 1) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Mission, 5) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.BossAccessDoor, 1) end return true end diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_lever_mota.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_lever_mota.lua index 03aaf3b2004..51035795573 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_lever_mota.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_lever_mota.lua @@ -4,55 +4,62 @@ local transformid = { } local cultsOfTibiaLeverMota = Action() + function cultsOfTibiaLeverMota.onUse(player, item, fromPosition, itemEx, toPosition) - local posPedra = Position() - local posPedra2 = Position() - local posPedra3 = Position() - local pedraid = 15487 - if player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) ~= 12 then + local stonePos1 = Position() + local stonePos2 = Position() + local stonePos3 = Position() + local stoneId = 15487 + + local missionStatus = player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) + if missionStatus ~= 12 and missionStatus ~= 13 then player:getPosition():sendMagicEffect(CONST_ME_POFF) return false end - for i = 33300, 33305, 1 do + + for i = 33300, 33305 do local newpos = Position(i, 32144, 10) - if Tile(newpos):getItemCountById(pedraid) == 1 then - posPedra = newpos + if Tile(newpos):getItemCountById(stoneId) == 1 then + stonePos1 = newpos end newpos = Position(i, 32146, 10) - if Tile(newpos):getItemCountById(pedraid) == 1 then - posPedra2 = newpos + if Tile(newpos):getItemCountById(stoneId) == 1 then + stonePos2 = newpos end newpos = Position(i, 32148, 10) - if Tile(newpos):getItemCountById(pedraid) == 1 then - posPedra3 = newpos + if Tile(newpos):getItemCountById(stoneId) == 1 then + stonePos3 = newpos end end - local pedra1 = math.random(0, 5) - local pedra2 = math.random(0, 5) - local pedra3 = math.random(0, 5) - -- pedra 1 - local itpedra1 = Tile(posPedra):getItemById(pedraid) - local newpos = { x = 33300 + pedra1, y = itpedra1:getPosition().y, z = itpedra1:getPosition().z } - itpedra1:moveTo(newpos) + + local stone1 = math.random(0, 5) + local stone2 = math.random(0, 5) + local stone3 = math.random(0, 5) + + local itstone1 = Tile(stonePos1):getItemById(stoneId) + local newpos = { x = 33300 + stone1, y = itstone1:getPosition().y, z = itstone1:getPosition().z } + itstone1:moveTo(newpos) Position(newpos):sendMagicEffect(CONST_ME_POFF) - -- pedra 2 - local itpedra2 = Tile(posPedra2):getItemById(pedraid) - local newpos = { x = 33300 + pedra2, y = itpedra2:getPosition().y, z = itpedra2:getPosition().z } - itpedra2:moveTo(newpos) + local itstone2 = Tile(stonePos2):getItemById(stoneId) + local newpos = { x = 33300 + stone2, y = itstone2:getPosition().y, z = itstone2:getPosition().z } + itstone2:moveTo(newpos) Position(newpos):sendMagicEffect(CONST_ME_POFF) - -- pedra 3 - local itpedra3 = Tile(posPedra3):getItemById(pedraid) - local newpos = { x = 33300 + pedra3, y = itpedra3:getPosition().y, z = itpedra3:getPosition().z } - itpedra3:moveTo(newpos) + local itstone3 = Tile(stonePos3):getItemById(stoneId) + local newpos = { x = 33300 + stone3, y = itstone3:getPosition().y, z = itstone3:getPosition().z } + itstone3:moveTo(newpos) Position(newpos):sendMagicEffect(CONST_ME_POFF) - player:setStorageValue(Storage.CultsOfTibia.MotA.Stone1, Stone1) - player:setStorageValue(Storage.CultsOfTibia.MotA.Stone2, Stone2) - player:setStorageValue(Storage.CultsOfTibia.MotA.Stone3, Stone3) - player:setStorageValue(Storage.CultsOfTibia.MotA.AccessDoorGareth, -1) - player:setStorageValue(Storage.CultsOfTibia.MotA.Mission, player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) + 1) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Stone1, stone1) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Stone2, stone2) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Stone3, stone3) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.AccessDoorGareth, -1) + + if missionStatus == 12 then + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission, 13) + end + item:transform(transformid[item:getId()]) return true end diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_magnifier.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_magnifier.lua index 449e79dc115..e50f94226b0 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_magnifier.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_magnifier.lua @@ -18,9 +18,9 @@ function cultsOfTibiaMagnifier.onUse(player, item, fromPosition, target, toPosit if table.contains({ 2622, 2601, 2596, 2612, 2618 }, target:getId()) then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Nothing special. This picture looks genuine.") target:getPosition():sendMagicEffect(CONST_ME_POFF) - elseif target:getPosition() == sqm and target:getId() == 2613 and player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) == 8 then + elseif target:getPosition() == sqm and target:getId() == 2613 and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) == 8 then target:getPosition():sendMagicEffect(CONST_ME_POFF) - player:setStorageValue(Storage.CultsOfTibia.MotA.Mission, 9) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission, 9) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "This is it. It looks like it was painted by a child!") end diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_misguided.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_misguided.lua index 3c555338d21..42b81b970db 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_misguided.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_misguided.lua @@ -39,22 +39,26 @@ local function changeMap(mapName) end local cultsOfTibiaMisguided = Action() + function cultsOfTibiaMisguided.onUse(player, item, position, target, targetPosition) - local monster = Monster(target) - if not monster then + local creature = Creature(target) + if not creature then return false end + local map = (type(Game.getStorageValue("cultsMap")) == "string" and Game.getStorageValue("cultsMap") or "illusion") - if monster:getName():lower() == "misguided bully" or monster:getName():lower() == "misguided thief" then - player:setStorageValue(Storage.CultsOfTibia.Misguided.Monsters, 0) + if creature:getName():lower() == "misguided bully" or creature:getName():lower() == "misguided thief" then + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.Monsters, 0) item:remove(1) - local pos = monster:getPosition() + local pos = creature:getPosition() Game.createItem(25298, 1, pos) - monster:remove() - local newMonster = Game.createMonster("Misguided Shadow", pos) - if newMonster then - newMonster:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + creature:remove() + + local newCreature = Game.createMonster("Misguided Shadow", pos) + if newCreature then + newCreature:getPosition():sendMagicEffect(CONST_ME_TELEPORT) end + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You paralyse the bully and the amulet reveals the true face of the creature behind the possession of this misguided creature.") local it = player:addItem(25296, 1) if map == "illusion" then @@ -62,6 +66,7 @@ function cultsOfTibiaMisguided.onUse(player, item, position, target, targetPosit end it:decay() end + return true end diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_torch.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_torch.lua index c60e5d024b8..7b1d8c4826e 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/actions_torch.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/actions_torch.lua @@ -3,9 +3,11 @@ local config = { } local cultsOfTibiaTouch = Action() + function cultsOfTibiaTouch.onUse(player, item, fromPosition, target, toPosition, isHotkey) local wallItem - if Game.getStorageValue(12345) >= os.time() then + + if Game.getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Humans.WallDecaying) >= os.time() then player:getPosition():sendMagicEffect(CONST_ME_POFF) else for i = 1, #config do @@ -14,11 +16,12 @@ function cultsOfTibiaTouch.onUse(player, item, fromPosition, target, toPosition, Position(32396, 31806, 8):sendMagicEffect(CONST_ME_POFF) wallItem:remove() player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You hear a loud grinding sound not very far from you. something very heavy seems to have moved.") - Game.setStorageValue(12345, os.time() + 306) + Game.setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Humans.WallDecaying, os.time() + 306) addEvent(Game.createItem, 300000, 1295, 1, Position(32396, 31806, 8)) end end end + return true end diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/creaturescripts_bosses_mission_cults.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/creaturescripts_bosses_mission_cults.lua index bd4da1601f1..5ce125af212 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/creaturescripts_bosses_mission_cults.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/creaturescripts_bosses_mission_cults.lua @@ -1,12 +1,12 @@ local bosses = { - ["ravenous hunger"] = { storage = Storage.CultsOfTibia.Barkless.Mission, value = 6 }, - ["the souldespoiler"] = { storage = Storage.CultsOfTibia.Misguided.Mission, value = 4 }, - ["essence of malice"] = { storage = Storage.CultsOfTibia.Humans.Mission, value = 2 }, - ["the unarmored voidborn"] = { storage = Storage.CultsOfTibia.Orcs.Mission, value = 2 }, - ["the false god"] = { storage = Storage.CultsOfTibia.Minotaurs.Mission, value = 4 }, - ["the sandking"] = { storage = Storage.CultsOfTibia.Life.Mission, value = 8, global = "sandking", g_value = 5 }, + ["ravenous hunger"] = { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.Mission, value = 6 }, + ["the souldespoiler"] = { storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.Mission, value = 4 }, + ["essence of malice"] = { storage = Storage.Quest.U11_40.CultsOfTibia.Humans.Mission, value = 2 }, + ["the unarmored voidborn"] = { storage = Storage.Quest.U11_40.CultsOfTibia.Orcs.Mission, value = 2 }, + ["the false god"] = { storage = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.Mission, value = 4 }, + ["the sandking"] = { storage = Storage.Quest.U11_40.CultsOfTibia.Life.Mission, value = 8, global = "sandking", g_value = 5 }, ["the corruptor of souls"] = { createNew = "The Source Of Corruption", pos = Position(33039, 31922, 15), removeMonster = "zarcorix of yalahar", area1 = Position(33073, 31885, 15), area2 = Position(33075, 31887, 15) }, - ["the source of corruption"] = { storage = Storage.CultsOfTibia.FinalBoss.Mission, value = 2 }, + ["the source of corruption"] = { storage = Storage.Quest.U11_40.CultsOfTibia.FinalBoss.Mission, value = 2 }, } local bossesCults = CreatureEvent("CultsOfTibiaBossDeath") diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/creaturescripts_glowing_rubbish_amulet.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/creaturescripts_glowing_rubbish_amulet.lua index 55a16295bb3..a5fbfcc60f4 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/creaturescripts_glowing_rubbish_amulet.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/creaturescripts_glowing_rubbish_amulet.lua @@ -1,27 +1,31 @@ local glowingRubbishAmulet = CreatureEvent("GlowingRubbishAmuletDeath") + function glowingRubbishAmulet.onDeath(creature, _corpse, _lastHitKiller, mostDamageKiller) onDeathForParty(creature, mostDamageKiller, function(creature, player) local amulet = player:getSlotItem(CONST_SLOT_NECKLACE) - if not amulet or amulet:getId() ~= 25296 then - return true - end - if player:getStorageValue(Storage.CultsOfTibia.Misguided.Mission) ~= 3 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.Mission) ~= 3 then return true end - local mStg = math.max(player:getStorageValue(Storage.CultsOfTibia.Misguided.Monsters), 0) - local eStg = math.max(player:getStorageValue(Storage.CultsOfTibia.Misguided.Exorcisms), 0) - if monster:getName():lower() == "misguided shadow" then + local mStg = math.max(player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.Monsters), 0) + local eStg = math.max(player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.Exorcisms), 0) + + if creature:getName():lower():trim() == "misguided shadow" then if eStg < 5 then - player:setStorageValue(Storage.CultsOfTibia.Misguided.Exorcisms, eStg + 1) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.Exorcisms, eStg + 1) end return true end - if monster:getName():lower() == "misguided bully" or monster:getName():lower() == "misguided thief" then - player:setStorageValue(Storage.CultsOfTibia.Misguided.Monsters, mStg + 1) - if player:getStorageValue(Storage.CultsOfTibia.Misguided.Monsters) >= 10 then + if not amulet or amulet:getId() ~= 25296 then + return true + end + + if creature:getName():lower():trim() == "misguided bully" or creature:getName():lower():trim() == "misguided thief" then + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.Monsters, mStg + 1) + + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.Monsters) >= 10 then amulet:remove() local it = player:addItem(25297, 1) if it then diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/creaturescripts_minotaur_task_count.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/creaturescripts_minotaur_task_count.lua index 68f3a0f93a1..267bc2e5d06 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/creaturescripts_minotaur_task_count.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/creaturescripts_minotaur_task_count.lua @@ -1,9 +1,9 @@ local minotaurTask = CreatureEvent("MinotaurCultTaskDeath") function minotaurTask.onDeath(creature, _corpse, _lastHitKiller, mostDamageKiller) onDeathForParty(creature, mostDamageKiller, function(creature, player) - local storage = player:getStorageValue(Storage.CultsOfTibia.Minotaurs.JamesfrancisTask) + local storage = player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Minotaurs.JamesfrancisTask) if storage >= 0 and storage < 50 then - player:setStorageValue(Storage.CultsOfTibia.Minotaurs.JamesfrancisTask, storage + 1) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Minotaurs.JamesfrancisTask, storage + 1) end end) return true diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/creaturescripts_splash.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/creaturescripts_splash.lua index 17af493a144..bf8b0ac2d59 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/creaturescripts_splash.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/creaturescripts_splash.lua @@ -1,4 +1,5 @@ local splash = CreatureEvent("Splash") + function splash.onThink(creature) local hp = (creature:getHealth() / creature:getMaxHealth()) * 100 if hp < 85.0 then diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_begin_task.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_begin_task.lua index f1bf05f4b08..fb8a88cdb0b 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_begin_task.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_begin_task.lua @@ -1,4 +1,4 @@ -function fallFloor(pid, id) +local function fallFloor(pid, id) local player = Player(pid) if not player then return true @@ -10,10 +10,11 @@ function fallFloor(pid, id) end local chance = math.random(0, 100) - if chance <= 1 then + if chance <= 2 then amulet:moveTo(player:getPosition()) end addEvent(fallFloor, 10000, player:getId(), id) + return true end @@ -24,19 +25,15 @@ function beginTask.onEquip(player, item, slot, isCheck) return true end - if player:getStorageValue(Storage.CultsOfTibia.Misguided.Mission) >= 2 and player:getStorageValue(Storage.CultsOfTibia.Misguided.Mission) <= 3 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.Mission) >= 2 and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.Mission) <= 3 then local equippedBefore = item:getCustomAttribute("task") or 0 - if equippedBefore ~= player:getGuid() and player:getStorageValue(Storage.CultsOfTibia.Misguided.Monsters) < 10 then - player:setStorageValue(Storage.CultsOfTibia.Misguided.Monsters, 0) + if equippedBefore ~= player:getGuid() and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.Monsters) < 10 then + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.Monsters, 0) item:setCustomAttribute("task", player:getGuid()) end - if player:getStorageValue(Storage.CultsOfTibia.Misguided.Mission) == 2 then - player:setStorageValue(Storage.CultsOfTibia.Misguided.Mission, 3) - player:sendTextMessage( - MESSAGE_EVENT_ADVANCE, - "The amulet burns your skin. \z - It hungers for energy right now, gather a large amount of energy as fast as possible to charge it. " - ) + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.Mission) == 2 then + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.Mission, 3) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The amulet burns your skin. It hungers for energy right now, gather a large amount of energy as fast as possible to charge it.") end end addEvent(fallFloor, 10000, player:getId(), item:getId()) diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_check_oasis.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_check_oasis.lua index 5a52cb16748..b4e769d0b29 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_check_oasis.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_check_oasis.lua @@ -6,9 +6,9 @@ function checkOasis.onStepIn(creature, item, position, fromPosition) return true end - if player:getStorageValue(Storage.CultsOfTibia.Life.Mission) == 2 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission) == 2 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found a lovely oasis with green fauna and juicy fruit. You didn't expect that.") - player:setStorageValue(Storage.CultsOfTibia.Life.Mission, 3) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission, 3) end return true end diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_energy_fence.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_energy_fence.lua index 7f5314001b4..b8b035c8955 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_energy_fence.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_energy_fence.lua @@ -27,38 +27,26 @@ function energyFence.onStepIn(creature, item, position, fromPosition) end if position == firstCheck or position == Position(firstCheck.x + 1, firstCheck.y, firstCheck.z) or position == Position(firstCheck.x + 2, firstCheck.y, firstCheck.z) then - if player:getStorageValue(Storage.CultsOfTibia.Humans.Vaporized) == 10 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Humans.Vaporized) == 10 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "With you being the vessel binding the power of whitered souls, you step through the magic barrier.") player:getPosition():sendMagicEffect(CONST_ME_ENERGYHIT) else player:teleportTo(fromPosition, true) - player:sendTextMessage( - MESSAGE_EVENT_ADVANCE, - "The combined powers of decaying souls \z - roaming these halls may help breach this barrier, it needs but a vessel to bind them." - ) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The combined powers of decaying souls roaming these halls may help breach this barrier, it needs but a vessel to bind them.") end elseif position == secondCheck or position == Position(secondCheck.x + 1, secondCheck.y, secondCheck.z) then - if player:getStorageValue(Storage.CultsOfTibia.Humans.Decaying) == 10 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Humans.Decaying) == 10 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "With you being the vessel binding the power of whitered souls, you step through the magic barrier.") player:getPosition():sendMagicEffect(CONST_ME_ENERGYHIT) else player:teleportTo(fromPosition, true) - player:sendTextMessage( - MESSAGE_EVENT_ADVANCE, - "The combined powers of decaying souls \z - roaming these halls may help breach this barrier, it needs but a vessel to bind them." - ) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The combined powers of decaying souls roaming these halls may help breach this barrier, it needs but a vessel to bind them.") end elseif misguided then for i, position in pairs(misguided) do - if player:getStorageValue(Storage.CultsOfTibia.Misguided.Exorcisms) >= 5 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Misguided.Exorcisms) >= 5 then player:getPosition():sendMagicEffect(CONST_ME_ENERGYHIT) - player:sendTextMessage( - MESSAGE_EVENT_ADVANCE, - "As you cross the threshold in to the inner \z - sanctuary of the cult of the Misguided, you feel an eerie presence all around you." - ) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "As you cross the threshold in to the inner sanctuary of the cult of the Misguided, you feel an eerie presence all around you.") break else player:teleportTo(fromPosition, true) diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_ice.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_ice.lua index fe1340bb44f..13649dc1d6f 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_ice.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_ice.lua @@ -1,5 +1,5 @@ -function sendConditionCults(playerid, info, fromPos, toPos, fromPos2, toPos2, time) - local player = Player(playerid) +local function sendConditionCults(playerId, info, fromPos, toPos, fromPos2, toPos2, time) + local player = Player(playerId) if not player then return false end @@ -20,16 +20,20 @@ function sendConditionCults(playerid, info, fromPos, toPos, fromPos2, toPos2, ti elseif time >= 120 then local storage = player:getStorageValue(info.storageBarkless) < 0 and 0 or player:getStorageValue(info.storageBarkless) if storage < 3 and storage ~= 1 and storage ~= 2 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, info.msgs[3]) - player:setStorageValue(info.storageBarkless, 1) + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Sulphur) == 3 and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Tar) == 3 then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, info.msgs[3]) + player:setStorageValue(info.storageBarkless, 1) + else + return true + end end end player:getPosition():sendMagicEffect(info.effect) - addEvent(sendConditionCults, 2000, playerid, info, fromPos, toPos, fromPos2, toPos2, time) + addEvent(sendConditionCults, 2000, playerId, info, fromPos, toPos, fromPos2, toPos2, time) end -local function floorPassage(playerid, info, time) - local player = Player(playerid) +local function floorPassage(playerId, info, time) + local player = Player(playerId) if not player then return true end @@ -42,7 +46,7 @@ local function floorPassage(playerid, info, time) if storage == 3 then return true end - addEvent(floorPassage, 1000, playerid, info, time - 1) + addEvent(floorPassage, 1000, playerId, info, time - 1) end local ice = MoveEvent() @@ -58,16 +62,15 @@ function ice.onStepIn(creature, item, position, fromPosition) fromPos2 = Position(32696, 31429, 8), toPos2 = Position(32728, 31435, 8), effect = CONST_ME_GIANTICE, - firstSqm = Position(32698, 31405, 8), - storageBarkless = Storage.CultsOfTibia.Barkless.Ice, + firstTile = Position(32698, 31405, 8), + storageBarkless = Storage.Quest.U11_40.CultsOfTibia.Barkless.Ice, msgs = { - "As you enter the icy cavern, you feel an unnatural frostiness. \z - The ice cold air stings in your face. Survive and prove worthy.", -- on enter - "Your body temperature sinks. You can see your breath freezing in the cold.", -- 30/60 seconds + "As you enter the icy cavern, you feel an unnatural frostiness. The ice cold air stings in your face. Survive and prove worthy.", -- on enter + "Your body temperature sinks. You can see your breath freezing in the cold.", -- 30/60/90 seconds "The icy cold is grasping to you. You can barely move anymore.", -- 120 seconds - "You are now washed and ready to purify yourself in the chambers of purification.", -- step in the first tile - "You are now ready to prove your worth. Take heart and cross the threshold of ice.", -- step in the second tile - "You took so long. You are no longer purified.", -- there's no time to step + "You are now washed and ready to purify yourself in the chambers of purification.", -- step on the first tile + "You are now ready to prove your worth. Take heart and cross the threshold of ice.", -- step on the second tile + "You took so long. You are no longer purified.", -- didn't step in time }, } if fromPosition.y == 31441 then @@ -76,7 +79,7 @@ function ice.onStepIn(creature, item, position, fromPosition) return true end - if item:getPosition():compare(setting.firstSqm) then + if item:getPosition():compare(setting.firstTile) then if player:getStorageValue(setting.storageBarkless) ~= 1 then return true end diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_ice_death.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_ice_death.lua index aff8d2462af..cd43068e0c3 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_ice_death.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_ice_death.lua @@ -1,11 +1,11 @@ function Player.sendFakeDeathWindow(self) - -- consider migrating to ProtocolGame::sendDeath local msg = NetworkMessage() + -- I found this in the function ProtocolGame::sendReLoginWindow msg:addByte(0x28) - msg:addByte(0x01) - msg:addByte(2) - msg:addByte(0x00) -- Use death redemption - msg:sendToPlayer(self, false) + msg:addByte(0x00) + msg:addByte(0x00) + msg:addByte(0x00) + msg:sendToPlayer(self) return true end @@ -44,9 +44,9 @@ function iceDeath.onStepIn(creature, item, position, fromPosition) if not player then return true end - if player:getStorageValue(Storage.CultsOfTibia.Barkless.Ice) == 2 then - player:setStorageValue(Storage.CultsOfTibia.Barkless.Ice, 3) - player:setStorageValue(Storage.CultsOfTibia.Barkless.Death, 1) + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Ice) == 2 then + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Ice, 3) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Death, 1) for _, conditionType in pairs(conditions) do if player:getCondition(conditionType) then player:removeCondition(conditionType) @@ -58,18 +58,13 @@ function iceDeath.onStepIn(creature, item, position, fromPosition) if it then it:decay() end - player:addHealth((-player:getHealth() + 1)) + player:addHealth(-player:getHealth() + 1) player:sendTextMessage(MESSAGE_BEYOND_LAST, "You were killed by something evil and others.") - -- TODO parse active blessings and show that you didn't lose any blessings - player:sendTextMessage( - MESSAGE_BEYOND_LAST, - "You are still blessed with Wisdom of Solitude, Spark of the Phoenix,Fire of the Suns, \z - Spiritual Shielding, Embrace of Tibia, Heart of the Mountani, Blood of the Montain and Twist of Fate." - ) + player:sendTextMessage(MESSAGE_BEYOND_LAST, "You are still blessed with Wisdom of Solitude, Spark of the Phoenix, Fire of the Suns, Spiritual Shielding, Embrace of Tibia, Heart of the Mountain, Blood of the Mountain, and Twist of Fate.") player:sendTextMessage(MESSAGE_BEYOND_LAST, "You lost 0 experience and 0.00% of all of your skills.") player:sendTextMessage(MESSAGE_BEYOND_LAST, "You did not lose any items.") - player:setStorageValue(Storage.CultsOfTibia.Barkless.Mission, 3) - player:setStorageValue(Storage.CultsOfTibia.Barkless.AccessDoor, 1) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Mission, 3) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.AccessDoor, 1) player:sendTextMessage(MESSAGE_BEYOND_LAST, "The cold has all but disappeared from your body and you're getting warmer. You need to renew all preparations for purification.") player:sendFakeDeathWindow() else diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_looktype.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_looktype.lua index 021ccfc5aae..36a2eb09172 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_looktype.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_looktype.lua @@ -10,10 +10,10 @@ function looktype.onStepIn(creature, item, position, fromPosition) local secondCheck = Position(33128, 31885, 11) local thirdCheck = Position(33175, 31923, 12) if position == firstCheck or position == Position(firstCheck.x + 1, firstCheck.y, firstCheck.z) then - if player:getStorageValue(Storage.CultsOfTibia.Orcs.LookType) < 1 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Orcs.LookType) < 1 then if creature:getOutfit().lookType == 5 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Due to the strength of an orc you are able to pass this rift.") - player:setStorageValue(Storage.CultsOfTibia.Orcs.LookType, 1) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Orcs.LookType, 1) else player:teleportTo(fromPosition, true) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need the strength of an orc to pass this rift.") @@ -21,10 +21,10 @@ function looktype.onStepIn(creature, item, position, fromPosition) end end if position == secondCheck or position == Position(secondCheck.x + 1, secondCheck.y, secondCheck.z) then - if player:getStorageValue(Storage.CultsOfTibia.Orcs.LookType) < 2 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Orcs.LookType) < 2 then if creature:getOutfit().lookType == 2 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "With the help off the might of an orc warlod you are able to pass this rift.") - player:setStorageValue(Storage.CultsOfTibia.Orcs.LookType, 2) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Orcs.LookType, 2) else player:teleportTo(fromPosition, true) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need the might of an orc warlod to pass this rift.") @@ -32,10 +32,10 @@ function looktype.onStepIn(creature, item, position, fromPosition) end end if position == thirdCheck or position == Position(thirdCheck.x + 1, thirdCheck.y, thirdCheck.z) or position == Position(thirdCheck.x + 2, thirdCheck.y, thirdCheck.z) then - if player:getStorageValue(Storage.CultsOfTibia.Orcs.LookType) < 3 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Orcs.LookType) < 3 then if creature:getOutfit().lookType == 6 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "With the help of the wisdom of an orc shaman you are able to pass this rift.") - player:setStorageValue(Storage.CultsOfTibia.Orcs.LookType, 3) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Orcs.LookType, 3) else player:teleportTo(fromPosition, true) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need the wisdom of an orc shaman to pass this rift.") diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_river_teleport.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_river_teleport.lua index a6a7c3f92b8..2654c116985 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_river_teleport.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_river_teleport.lua @@ -12,12 +12,12 @@ function riverTeleport.onStepIn(creature, item, position, fromPosition) return false end - if player:getStorageValue(Storage.CultsOfTibia.Life.Mission) < 7 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission) < 7 then player:teleportTo(Position(33474, 32281, 10)) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) end - if player:getStorageValue(Storage.CultsOfTibia.Life.Mission) >= 7 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission) >= 7 then player:teleportTo(Position(33479, 32235, 10)) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) end diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_sand_entrance.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_sand_entrance.lua index e8937a4bdf1..ad9557521a8 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_sand_entrance.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_sand_entrance.lua @@ -6,7 +6,7 @@ function sandEntrance.onStepIn(creature, item, position, fromPosition) return true end - if player:getStorageValue(Storage.CultsOfTibia.Life.Mission) < 1 and player:getStorageValue(Storage.CultsOfTibia.MotA.Mission) < 1 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Life.Mission) < 1 and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.MotA.Mission) < 1 then player:teleportTo(fromPosition, true) player:sendCancelMessage("You can't go there yet.") end diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_tar.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_tar.lua index a19273791b5..144ece38ad4 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_tar.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_tar.lua @@ -5,21 +5,16 @@ local configQuest = { fromPos = Position(32737, 31489, 8), toPos = Position(32761, 31512, 8), effect = CONST_ME_HITBYFIRE, - firstSqm = Position(32750, 31508, 8), - secondSqm = Position(32746, 31469, 8), - storageBarkless = Storage.CultsOfTibia.Barkless.Tar, + firstTile = Position(32750, 31508, 8), + secondTile = Position(32746, 31469, 8), + storageBarkless = Storage.Quest.U11_40.CultsOfTibia.Barkless.Tar, msgs = { - "As you enter the tar pits, you feel the heat around you rising dramatically. \z - Survive the heat long enough to prove worthy.", -- on enter + "As you enter the tar pits, you feel the heat around you rising dramatically. Survive the heat long enough to prove worthy.", -- on enter "Your body is heating up, the air around you is flickering.", -- 30/60 seconds - "The heat is now unbearable, the blood in your body feels like lava. \z - There's almost no strength left in you - act quickly!", -- 90 segunds - "Embrace the stigma of bad fortune. The tar does not feel so hot anymore. \z - You passed the tar trial.", -- step in the first tile - "Your body reacts to this strange green substance as you reach out to touch it. \z - You feel an urge for more of this energy.", -- step in the second tile - "The tar covering you has cooled down and tell off for the most part. \z - Your body is not heated up anymore.", -- there's no time to step + "The heat is now unbearable, the blood in your body feels like lava. There's almost no strength left in you - act quickly!", -- 90 seconds + "Embrace the stigma of bad fortune. The tar does not feel so hot anymore. You passed the tar trial.", -- step on the first tile + "Your body reacts to this strange green substance as you reach out to touch it. You feel an urge for more of this energy.", -- step on the second tile + "The tar covering you has cooled down and fell off for the most part. Your body is not heated up anymore.", -- didn't step in time }, }, ["acid"] = { @@ -28,62 +23,66 @@ local configQuest = { fromPos = Position(32647, 31479, 8), toPos = Position(32710, 31519, 8), effect = CONST_ME_YELLOW_RINGS, - firstSqm = Position(32680, 31485, 8), - secondSqm = Position(32664, 31504, 8), - storageBarkless = Storage.CultsOfTibia.Barkless.sulphur, + firstTile = Position(32680, 31485, 8), + secondTile = Position(32664, 31504, 8), + storageBarkless = Storage.Quest.U11_40.CultsOfTibia.Barkless.Sulphur, msgs = { "As you enter the sulphur pits, you feel the dry, burning vapours of the sulphur all around you. Prove worthy, survive the acid.", -- on enter - "The sulphur is burning your skin. You almost feel your body melting away in acid.", -- 30/60 segunds - "The acid burning is now unbearable, you skin feels like a sieve. \z - ]There's almost no strength left in you - act quickly!", -- 90 segunds - "Embrace the stigma of vanity. The sulphur does not burn your skin anymore. \z - You passed the trial.", -- step in the first tile - "You are now ready to prove your worth. \z - Take heart and cross the threshold of sulphur.", -- step in the second tile - "The acid covering you has cooled down and tell off for the most part. \z - Your body is not heated up anymore.", -- there's no time to step + "The sulphur is burning your skin. You almost feel your body melting away in acid.", -- 30/60 seconds + "The acid burning is now unbearable, your skin feels like a sieve. There's almost no strength left in you - act quickly!", -- 90 seconds + "Embrace the stigma of vanity. The sulphur does not burn your skin anymore. You passed the trial.", -- step on the first tile + "You are now ready to prove your worth. Take heart and cross the threshold of sulphur.", -- step on the second tile + "The acid covering you has cooled down and fell off for the most part. Your body is not heated up anymore.", -- didn't step in time }, }, } -function sendConditionCults(playerid, _type, fromPos, toPos, tempo) - local player = Player(playerid) +local function sendConditionCults(playerId, _type, fromPos, toPos, time) + local player = Player(playerId) if not player or not player:getPosition():isInRange(fromPos, toPos) then return false end - local inf = configQuest[_type] - tempo = tempo + 2 - if tempo == 30 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, inf.msgs[2]) - elseif tempo == 60 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, inf.msgs[2]) - elseif tempo >= 90 then - local stg = player:getStorageValue(inf.storageBarkless) < 0 and 0 or player:getStorageValue(inf.storageBarkless) - if stg < 3 and stg ~= 1 and stg ~= 2 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, inf.msgs[3]) - player:setStorageValue(inf.storageBarkless, 1) + local info = configQuest[_type] + time = time + 2 + if time == 30 then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, info.msgs[2]) + elseif time == 60 then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, info.msgs[2]) + elseif time >= 90 then + local stage = player:getStorageValue(info.storageBarkless) < 0 and 0 or player:getStorageValue(info.storageBarkless) + if stage < 3 and stage ~= 1 and stage ~= 2 then + if _type == "acid" and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Tar) ~= 3 then + return true + end + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, info.msgs[3]) + player:setStorageValue(info.storageBarkless, 1) + if _type == "fire" then + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.TarAccessDoor, 1) + elseif _type == "acid" then + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.SulphurAccessDoor, 1) + end end end - player:getPosition():sendMagicEffect(inf.effect) - addEvent(sendConditionCults, 2000, playerid, _type, fromPos, toPos, tempo) + player:getPosition():sendMagicEffect(info.effect) + addEvent(sendConditionCults, 2000, playerId, _type, fromPos, toPos, time) end -function passagemPiso1Piso2(playerid, info, tempo) - local player = Player(playerid) +function passageFloor1ToFloor2(playerId, info, time) + local player = Player(playerId) if not player then return true end - local stg = player:getStorageValue(info.storageBarkless) < 0 and 0 or player:getStorageValue(info.storageBarkless) - if tempo == 0 and stg < 3 then + local stage = player:getStorageValue(info.storageBarkless) < 0 and 0 or player:getStorageValue(info.storageBarkless) + if time == 0 and stage < 3 then player:setStorageValue(info.storageBarkless, 0) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, info.msgs[6]) return true end - if stg == 3 then + if stage == 3 then return true end - addEvent(passagemPiso1Piso2, 1000, playerid, info, tempo - 1) + addEvent(passageFloor1ToFloor2, 1000, playerId, info, time - 1) end local tar = MoveEvent() @@ -99,18 +98,15 @@ function tar.onStepIn(creature, item, position, fromPosition) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, value.msgs[1]) sendConditionCults(player:getId(), index, value.fromPos, value.toPos, 0) return true - elseif position:compare(value.firstSqm) and player:getStorageValue(value.storageBarkless) == 1 then - if player:getStorageValue(value.storageBarkless) ~= 1 then - return true - end + elseif position:compare(value.firstTile) and player:getStorageValue(value.storageBarkless) == 1 then player:setStorageValue(value.storageBarkless, 2) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, value.msgs[4]) - passagemPiso1Piso2(player:getId(), value, 60) - elseif position:compare(value.secondSqm) then + passageFloor1ToFloor2(player:getId(), value, 60) + elseif position:compare(value.secondTile) then if player:getStorageValue(value.storageBarkless) == 2 then player:setStorageValue(value.storageBarkless, 3) - if player:getStorageValue(Storage.CultsOfTibia.Barkless.sulphur) == 3 and player:getStorageValue(Storage.CultsOfTibia.Barkless.Tar) == 3 then - player:setStorageValue(Storage.CultsOfTibia.Barkless.Mission, 2) + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Sulphur) == 3 and player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Tar) == 3 then + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Mission, 2) end player:sendTextMessage(MESSAGE_EVENT_ADVANCE, value.msgs[5]) end @@ -134,7 +130,7 @@ function tar.onStepOut(creature, item, position, fromPosition) if item:getActionId() == 5531 then if fromPosition.x == 32736 then player:getPosition():sendMagicEffect(CONST_ME_POFF) - if player:getStorageValue(Storage.CultsOfTibia.Barkless.Tar) < 3 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Tar) < 3 then player:teleportTo(Position(32737, 31451, 8), true) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are not ready yet.") return false @@ -145,7 +141,7 @@ function tar.onStepOut(creature, item, position, fromPosition) if item:getActionId() == 5530 then if fromPosition.x == 32717 then player:getPosition():sendMagicEffect(CONST_ME_POFF) - if player:getStorageValue(Storage.CultsOfTibia.Barkless.sulphur) < 3 then + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Sulphur) < 3 then player:teleportTo(Position(32718, 31444, 8), true) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are not ready yet.") return false diff --git a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_task_teleport.lua b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_task_teleport.lua index acf48416449..3c254405b38 100644 --- a/data-otservbr-global/scripts/quests/cults_of_tibia/movements_task_teleport.lua +++ b/data-otservbr-global/scripts/quests/cults_of_tibia/movements_task_teleport.lua @@ -1,12 +1,12 @@ local setting = { [32415] = { - storage = Storage.CultsOfTibia.Humans.Decaying, + storage = Storage.Quest.U11_40.CultsOfTibia.Humans.Decaying, max = 10, text = "You absorb the energetic remains of this decaying soul. Its power is very fragile and fleeting", effect = CONST_ME_GREEN_ENERGY_SPARK, }, [32414] = { - storage = Storage.CultsOfTibia.Humans.Vaporized, + storage = Storage.Quest.U11_40.CultsOfTibia.Humans.Vaporized, max = 10, text = "You absorb the energetic remains of this whitering soul. Its power is very fragile and fleeting.", effect = CONST_ME_BLUE_ENERGY_SPARK, @@ -21,11 +21,11 @@ function taskTeleport.onStepIn(creature, item, position, fromPosition) return true end - if player:getStorageValue(Storage.CultsOfTibia.Questline) < 1 then - player:setStorageValue(Storage.CultsOfTibia.Questline, 1) + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Questline) < 1 then + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Questline, 1) end - if player:getStorageValue(Storage.CultsOfTibia.Humans.Mission) < 1 then - player:setStorageValue(Storage.CultsOfTibia.Humans.Mission, 1) + if player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Humans.Mission) < 1 then + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Humans.Mission, 1) end for index, value in pairs(setting) do diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/actions_crude_lava_pump.lua b/data-otservbr-global/scripts/quests/dangerous_depth/actions_crude_lava_pump.lua index 64875337f37..85d4abb87f6 100644 --- a/data-otservbr-global/scripts/quests/dangerous_depth/actions_crude_lava_pump.lua +++ b/data-otservbr-global/scripts/quests/dangerous_depth/actions_crude_lava_pump.lua @@ -15,8 +15,8 @@ function dangerousDepthPump.onUse(player, item) for _, spectator in pairs(spectators) do if spectator:isPlayer() then local playerSpectator = spectator - if playerSpectator:getStorageValue(Storage.DangerousDepths.Access.LavaPumpWarzoneVI) ~= 1 then - playerSpectator:setStorageValue(Storage.DangerousDepths.Access.LavaPumpWarzoneVI, 1) + if playerSpectator:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneVI) ~= 1 then + playerSpectator:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneVI, 1) end end end @@ -33,8 +33,8 @@ function dangerousDepthPump.onUse(player, item) for _, spectator in pairs(spectators) do if spectator:isPlayer() then local playerSpectator = spectator - if playerSpectator:getStorageValue(Storage.DangerousDepths.Access.LavaPumpWarzoneV) ~= 1 then - playerSpectator:setStorageValue(Storage.DangerousDepths.Access.LavaPumpWarzoneV, 1) + if playerSpectator:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneV) ~= 1 then + playerSpectator:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneV, 1) end end end @@ -51,8 +51,8 @@ function dangerousDepthPump.onUse(player, item) for _, spectator in pairs(spectators) do if spectator:isPlayer() then local playerSpectator = spectator - if playerSpectator:getStorageValue(Storage.DangerousDepths.Access.LavaPumpWarzoneIV) ~= 1 then - playerSpectator:setStorageValue(Storage.DangerousDepths.Access.LavaPumpWarzoneIV, 1) + if playerSpectator:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneIV) ~= 1 then + playerSpectator:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneIV, 1) end end end diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/actions_crude_lava_pump_achievements.lua b/data-otservbr-global/scripts/quests/dangerous_depth/actions_crude_lava_pump_achievements.lua index ad6b13cda90..6b3c2fe73c5 100644 --- a/data-otservbr-global/scripts/quests/dangerous_depth/actions_crude_lava_pump_achievements.lua +++ b/data-otservbr-global/scripts/quests/dangerous_depth/actions_crude_lava_pump_achievements.lua @@ -32,30 +32,30 @@ function dangerousDepthAchievements.onUse(player, item) local WarzoneVI = Position(33685, 32304, 15) if positionItem == WarzoneIV then -- Warzone VI - if player:getStorageValue(Storage.DangerousDepths.Bosses.TheBaronFromBelowAchiev) < 1 then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Bosses.TheBaronFromBelowAchiev) < 1 then player:addAchievement("Buried the Baron") - player:setStorageValue(Storage.DangerousDepths.Bosses.TheBaronFromBelowAchiev, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Bosses.TheBaronFromBelowAchiev, 1) end end if positionItem == WarzoneV then -- Warzone V - if player:getStorageValue(Storage.DangerousDepths.Bosses.TheCountOfTheCoreAchiev) < 1 then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Bosses.TheCountOfTheCoreAchiev) < 1 then player:addAchievement("His Days are Counted") - player:setStorageValue(Storage.DangerousDepths.Bosses.TheCountOfTheCoreAchiev, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Bosses.TheCountOfTheCoreAchiev, 1) end end if positionItem == WarzoneVI then -- Warzone IV - if player:getStorageValue(Storage.DangerousDepths.Bosses.TheDukeOfTheDepthsAchiev) < 1 then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Bosses.TheDukeOfTheDepthsAchiev) < 1 then player:addAchievement("Duked It Out") - player:setStorageValue(Storage.DangerousDepths.Bosses.TheDukeOfTheDepthsAchiev, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Bosses.TheDukeOfTheDepthsAchiev, 1) end end - if player:getStorageValue(Storage.DangerousDepths.Bosses.LastAchievement) < 1 then - if player:getStorageValue(Storage.DangerousDepths.Bosses.TheDukeOfTheDepthsAchiev) == 1 and player:getStorageValue(Storage.DangerousDepths.Bosses.TheBaronFromBelowAchiev) == 1 and player:getStorageValue(Storage.DangerousDepths.Bosses.TheCountOfTheCoreAchiev) == 1 then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Bosses.LastAchievement) < 1 then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Bosses.TheDukeOfTheDepthsAchiev) == 1 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Bosses.TheBaronFromBelowAchiev) == 1 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Bosses.TheCountOfTheCoreAchiev) == 1 then player:addAchievement("Death in the Depths") - player:setStorageValue(Storage.DangerousDepths.Bosses.LastAchievement, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Bosses.LastAchievement, 1) end end return true diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnome_chart.lua b/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnome_chart.lua index f3f74036171..f6dbddb003f 100644 --- a/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnome_chart.lua +++ b/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnome_chart.lua @@ -13,51 +13,51 @@ function dangerousDepthChart.onUse(player, item, isHotkey) return true end - if player:getStorageValue(Storage.DangerousDepths.Gnomes.Charting) == 1 then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Charting) == 1 then if player:getPosition():isInRange(config.OldGate.fromPosition, config.OldGate.toPosition) then - if player:getStorageValue(Storage.DangerousDepths.Gnomes.OldGate) < 1 then - player:setStorageValue(Storage.DangerousDepths.Gnomes.OldGate, 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.ChartingCount, player:getStorageValue(Storage.DangerousDepths.Gnomes.ChartingCount) + 1) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.OldGate) < 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.OldGate, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.ChartingCount, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.ChartingCount) + 1) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You charted the location and dimensions of a strange structure, an ancient gate.") player:getPosition():sendMagicEffect(CONST_ME_HITAREA) end end if player:getPosition():isInRange(config.LostRuin.fromPosition, config.LostRuin.toPosition) then - if player:getStorageValue(Storage.DangerousDepths.Gnomes.LostRuin) < 1 then - player:setStorageValue(Storage.DangerousDepths.Gnomes.LostRuin, 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.ChartingCount, player:getStorageValue(Storage.DangerousDepths.Gnomes.ChartingCount) + 1) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LostRuin) < 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LostRuin, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.ChartingCount, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.ChartingCount) + 1) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You charted the location and dimensions of a strange structure, a small ruin.") player:getPosition():sendMagicEffect(CONST_ME_HITAREA) end end if player:getPosition():isInRange(config.TheGaze.fromPosition, config.TheGaze.toPosition) then - if player:getStorageValue(Storage.DangerousDepths.Gnomes.TheGaze) < 1 then - player:setStorageValue(Storage.DangerousDepths.Gnomes.TheGaze, 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.ChartingCount, player:getStorageValue(Storage.DangerousDepths.Gnomes.ChartingCount) + 1) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.TheGaze) < 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.TheGaze, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.ChartingCount, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.ChartingCount) + 1) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You charted the location and dimensions of a strange structure, resembling a stone face.") player:getPosition():sendMagicEffect(CONST_ME_HITAREA) end end if player:getPosition():isInRange(config.Outpost.fromPosition, config.Outpost.toPosition) then - if player:getStorageValue(Storage.DangerousDepths.Gnomes.Outpost) < 1 then - player:setStorageValue(Storage.DangerousDepths.Gnomes.Outpost, 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.ChartingCount, player:getStorageValue(Storage.DangerousDepths.Gnomes.ChartingCount) + 1) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Outpost) < 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Outpost, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.ChartingCount, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.ChartingCount) + 1) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You charted the location and dimensions of a strange structure, an outpost.") player:getPosition():sendMagicEffect(CONST_ME_HITAREA) end end if player:getPosition():isInRange(config.Bastion.fromPosition, config.Bastion.toPosition) then - if player:getStorageValue(Storage.DangerousDepths.Gnomes.Bastion) < 1 then - player:setStorageValue(Storage.DangerousDepths.Gnomes.Bastion, 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.ChartingCount, player:getStorageValue(Storage.DangerousDepths.Gnomes.ChartingCount) + 1) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Bastion) < 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Bastion, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.ChartingCount, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.ChartingCount) + 1) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You charted the location and dimensions of a strange structure, a bastion.") player:getPosition():sendMagicEffect(CONST_ME_HITAREA) end end if player:getPosition():isInRange(config.BrokenTower.fromPosition, config.BrokenTower.toPosition) then - if player:getStorageValue(Storage.DangerousDepths.Gnomes.BrokenTower) < 1 then - player:setStorageValue(Storage.DangerousDepths.Gnomes.BrokenTower, 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.ChartingCount, player:getStorageValue(Storage.DangerousDepths.Gnomes.ChartingCount) + 1) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.BrokenTower) < 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.BrokenTower, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.ChartingCount, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.ChartingCount) + 1) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You charted the location and dimensions of a strange structure, a broken tower.") player:getPosition():sendMagicEffect(CONST_ME_HITAREA) end @@ -66,5 +66,5 @@ function dangerousDepthChart.onUse(player, item, isHotkey) return true end -dangerousDepthChart:id(31931) +dangerousDepthChart:id(27308) dangerousDepthChart:register() diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnome_items.lua b/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnome_items.lua index 4589b865066..bffb5a5bcf5 100644 --- a/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnome_items.lua +++ b/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnome_items.lua @@ -1,22 +1,24 @@ local dangerousDepthItems = Action() + function dangerousDepthItems.onUse(player, item) - if item:getUniqueId() == 57235 then - if player:getStorageValue(Storage.DangerousDepths.Gnomes.GnomeChartPaper) == 1 then -- Permissão para usar o baú == 1 then - player:addItem(31931, 1) + if item.uid == 57235 then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.GnomeChartPaper) == 1 then + player:addItem(27308, 1) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found a gnome charts.") - player:setStorageValue(Storage.DangerousDepths.Gnomes.GnomeChartPaper, 2) - elseif player:getStorageValue(Storage.DangerousDepths.Gnomes.GnomeChartPaper) == 2 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.GnomeChartPaper, 2) + elseif player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.GnomeChartPaper) == 2 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can not use this item again.") end - elseif item:getUniqueId() == 57236 then - if player:getStorageValue(Storage.DangerousDepths.Gnomes.GnomeChartChest) == 1 then -- Permissão para usar o baú == 1 then - player:addItem(31930, 1) + elseif item.uid == 57236 then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.GnomeChartChest) == 1 then + player:addItem(27307, 1) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found a gnome trignometre.") - player:setStorageValue(Storage.DangerousDepths.Gnomes.GnomeChartChest, 2) - elseif player:getStorageValue(Storage.DangerousDepths.Gnomes.GnomeChartChest) == 2 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.GnomeChartChest, 2) + elseif player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.GnomeChartChest) == 2 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "It is empty.") end end + return true end diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnome_trignometre.lua b/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnome_trignometre.lua index 458e9f67c2e..baf96e52253 100644 --- a/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnome_trignometre.lua +++ b/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnome_trignometre.lua @@ -11,43 +11,43 @@ function dangerousDepthTrignometre.onUse(player, item, isHotkey) if not player then return true end - if player:getStorageValue(Storage.DangerousDepths.Gnomes.Measurements) == 1 then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Measurements) == 1 then if player:getPosition():isInRange(config.locationA.fromPosition, config.locationA.toPosition) then - if player:getStorageValue(Storage.DangerousDepths.Gnomes.LocationA) < 1 then - player:setStorageValue(Storage.DangerousDepths.Gnomes.LocationA, 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.LocationCount, player:getStorageValue(Storage.DangerousDepths.Gnomes.LocationCount) + 1) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationA) < 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationA, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationCount, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationCount) + 1) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You probed the location successfully.") player:getPosition():sendMagicEffect(CONST_ME_HITAREA) end end if player:getPosition():isInRange(config.locationB.fromPosition, config.locationB.toPosition) then - if player:getStorageValue(Storage.DangerousDepths.Gnomes.LocationB) < 1 then - player:setStorageValue(Storage.DangerousDepths.Gnomes.LocationB, 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.LocationCount, player:getStorageValue(Storage.DangerousDepths.Gnomes.LocationCount) + 1) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationB) < 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationB, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationCount, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationCount) + 1) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You probed the location successfully.") player:getPosition():sendMagicEffect(CONST_ME_HITAREA) end end if player:getPosition():isInRange(config.locationC.fromPosition, config.locationC.toPosition) then - if player:getStorageValue(Storage.DangerousDepths.Gnomes.LocationC) < 1 then - player:setStorageValue(Storage.DangerousDepths.Gnomes.LocationC, 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.LocationCount, player:getStorageValue(Storage.DangerousDepths.Gnomes.LocationCount) + 1) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationC) < 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationC, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationCount, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationCount) + 1) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You probed the location successfully.") player:getPosition():sendMagicEffect(CONST_ME_HITAREA) end end if player:getPosition():isInRange(config.locationD.fromPosition, config.locationD.toPosition) then - if player:getStorageValue(Storage.DangerousDepths.Gnomes.LocationD) < 1 then - player:setStorageValue(Storage.DangerousDepths.Gnomes.LocationD, 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.LocationCount, player:getStorageValue(Storage.DangerousDepths.Gnomes.LocationCount) + 1) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationD) < 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationD, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationCount, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationCount) + 1) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You probed the location successfully.") player:getPosition():sendMagicEffect(CONST_ME_HITAREA) end end if player:getPosition():isInRange(config.locationE.fromPosition, config.locationE.toPosition) then - if player:getStorageValue(Storage.DangerousDepths.Gnomes.LocationE) < 1 then - player:setStorageValue(Storage.DangerousDepths.Gnomes.LocationE, 1) - player:setStorageValue(Storage.DangerousDepths.Gnomes.LocationCount, player:getStorageValue(Storage.DangerousDepths.Gnomes.LocationCount) + 1) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationE) < 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationE, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationCount, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.LocationCount) + 1) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You probed the location successfully.") player:getPosition():sendMagicEffect(CONST_ME_HITAREA) end @@ -56,5 +56,5 @@ function dangerousDepthTrignometre.onUse(player, item, isHotkey) return true end -dangerousDepthTrignometre:id(31930) +dangerousDepthTrignometre:id(27307) dangerousDepthTrignometre:register() diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnomish_chest.lua b/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnomish_chest.lua index 6219bb88ac3..418d1d16c9b 100644 --- a/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnomish_chest.lua +++ b/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnomish_chest.lua @@ -1,10 +1,11 @@ local dangerousDepthChest = Action() + function dangerousDepthChest.onUse(player, item) - if player:getStorageValue(Storage.DangerousDepths.Scouts.GnomishChest) == 1 then - player:addItem(30733, 1) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.GnomishChest) == 1 then + player:addItem(27498, 1) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found gnomish pesticides.") - player:setStorageValue(Storage.DangerousDepths.Scouts.GnomishChest, 2) - elseif player:getStorageValue(Storage.DangerousDepths.Scouts.GnomishChest) == 2 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.GnomishChest, 2) + elseif player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.GnomishChest) == 2 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "It is empty.") end return true diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnomish_pesticide.lua b/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnomish_pesticide.lua index a256e3bf6c6..55aae368926 100644 --- a/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnomish_pesticide.lua +++ b/data-otservbr-global/scripts/quests/dangerous_depth/actions_gnomish_pesticide.lua @@ -1,4 +1,5 @@ local dangerousDepthPesticide = Action() + function dangerousDepthPesticide.onUse(player, item, fromPosition, target, toPosition, isHotkey) if not player then return true @@ -17,15 +18,15 @@ function dangerousDepthPesticide.onUse(player, item, fromPosition, target, toPos local posTarget = target:getPosition() if target:getId() == corpseId then - if player:getStorageValue(Storage.DangerousDepths.Scouts.Diremaw) == 1 and player:getStorageValue(Storage.DangerousDepths.Scouts.DiremawsCount) < 20 then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Diremaw) == 1 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.DiremawsCount) < 20 then if r <= 50 then posTarget:sendMagicEffect(CONST_ME_POISONAREA) local diremaw = Game.createMonster("Diremaw", target:getPosition()) - player:setStorageValue(Storage.DangerousDepths.Scouts.DiremawsCount, player:getStorageValue(Storage.DangerousDepths.Scouts.DiremawsCount) + 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.DiremawsCount, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.DiremawsCount) + 1) target:transform(30730) else posTarget:sendMagicEffect(CONST_ME_POISONAREA) - player:setStorageValue(Storage.DangerousDepths.Scouts.DiremawsCount, player:getStorageValue(Storage.DangerousDepths.Scouts.DiremawsCount) + 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.DiremawsCount, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.DiremawsCount) + 1) target:transform(30730) end end @@ -37,5 +38,5 @@ function dangerousDepthPesticide.onUse(player, item, fromPosition, target, toPos return true end -dangerousDepthPesticide:id(30733) +dangerousDepthPesticide:id(27498) dangerousDepthPesticide:register() diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/actions_lever_barrel.lua b/data-otservbr-global/scripts/quests/dangerous_depth/actions_lever_barrel.lua index 327878284a0..1a478e3200e 100644 --- a/data-otservbr-global/scripts/quests/dangerous_depth/actions_lever_barrel.lua +++ b/data-otservbr-global/scripts/quests/dangerous_depth/actions_lever_barrel.lua @@ -5,31 +5,31 @@ local transformid = { local config = { first = { - [1] = { fromPosition = Position(33877, 32060, 14), toPosition = Position(33888, 32068, 14), stgRoom = Storage.DangerousDepths.Scouts.FirstBarrel }, + [1] = { fromPosition = Position(33877, 32060, 14), toPosition = Position(33888, 32068, 14), stgRoom = Storage.Quest.U11_50.DangerousDepths.Scouts.FirstBarrel }, }, second = { - [1] = { fromPosition = Position(33906, 32026, 14), toPosition = Position(33916, 32037, 14), stgRoom = Storage.DangerousDepths.Scouts.SecondBarrel }, + [1] = { fromPosition = Position(33906, 32026, 14), toPosition = Position(33916, 32037, 14), stgRoom = Storage.Quest.U11_50.DangerousDepths.Scouts.SecondBarrel }, }, third = { - [1] = { fromPosition = Position(33865, 32009, 14), toPosition = Position(33874, 32020, 14), stgRoom = Storage.DangerousDepths.Scouts.ThirdBarrel }, + [1] = { fromPosition = Position(33865, 32009, 14), toPosition = Position(33874, 32020, 14), stgRoom = Storage.Quest.U11_50.DangerousDepths.Scouts.ThirdBarrel }, }, fourth = { - [1] = { fromPosition = Position(33837, 31984, 14), toPosition = Position(33852, 31991, 14), stgRoom = Storage.DangerousDepths.Scouts.FourthBarrel }, + [1] = { fromPosition = Position(33837, 31984, 14), toPosition = Position(33852, 31991, 14), stgRoom = Storage.Quest.U11_50.DangerousDepths.Scouts.FourthBarrel }, }, fifth = { - [1] = { fromPosition = Position(33923, 31982, 14), toPosition = Position(33942, 31998, 14), stgRoom = Storage.DangerousDepths.Scouts.FifthBarrel }, + [1] = { fromPosition = Position(33923, 31982, 14), toPosition = Position(33942, 31998, 14), stgRoom = Storage.Quest.U11_50.DangerousDepths.Scouts.FifthBarrel }, }, } -local function checarPos(item) +local function checkPos(item) for _, info1 in pairs(config.first) do local fromPos, toPos, stgRoom = info1.fromPosition, info1.toPosition, info1.stgRoom if item:getPosition():isInRange(fromPos, toPos) then - local stgbarrel = item:getCustomAttribute(Storage.DangerousDepths.Scouts.Barrel) or -1 - local player = Player(stgbarrel) + local stgBarrel = item:getCustomAttribute(Storage.Quest.U11_50.DangerousDepths.Scouts.Barrel) or -1 + local player = Player(stgBarrel) if player then if player:getStorageValue(stgRoom) < 1 then - player:setStorageValue(Storage.DangerousDepths.Scouts.BarrelCount, player:getStorageValue(Storage.DangerousDepths.Scouts.BarrelCount) + 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.BarrelCount, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.BarrelCount) + 1) player:setStorageValue(stgRoom, 1) end end @@ -38,11 +38,11 @@ local function checarPos(item) for _, info2 in pairs(config.second) do local fromPos, toPos, stgRoom = info2.fromPosition, info2.toPosition, info2.stgRoom if item:getPosition():isInRange(fromPos, toPos) then - local stgbarrel = item:getCustomAttribute(Storage.DangerousDepths.Scouts.Barrel) or -1 - local player = Player(stgbarrel) + local stgBarrel = item:getCustomAttribute(Storage.Quest.U11_50.DangerousDepths.Scouts.Barrel) or -1 + local player = Player(stgBarrel) if player then if player:getStorageValue(stgRoom) < 1 then - player:setStorageValue(Storage.DangerousDepths.Scouts.BarrelCount, player:getStorageValue(Storage.DangerousDepths.Scouts.BarrelCount) + 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.BarrelCount, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.BarrelCount) + 1) player:setStorageValue(stgRoom, 1) end end @@ -51,11 +51,11 @@ local function checarPos(item) for _, info3 in pairs(config.third) do local fromPos, toPos, stgRoom = info3.fromPosition, info3.toPosition, info3.stgRoom if item:getPosition():isInRange(fromPos, toPos) then - local stgbarrel = item:getCustomAttribute(Storage.DangerousDepths.Scouts.Barrel) or -1 - local player = Player(stgbarrel) + local stgBarrel = item:getCustomAttribute(Storage.Quest.U11_50.DangerousDepths.Scouts.Barrel) or -1 + local player = Player(stgBarrel) if player then if player:getStorageValue(stgRoom) < 1 then - player:setStorageValue(Storage.DangerousDepths.Scouts.BarrelCount, player:getStorageValue(Storage.DangerousDepths.Scouts.BarrelCount) + 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.BarrelCount, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.BarrelCount) + 1) player:setStorageValue(stgRoom, 1) end end @@ -64,11 +64,11 @@ local function checarPos(item) for _, info4 in pairs(config.fourth) do local fromPos, toPos, stgRoom = info4.fromPosition, info4.toPosition, info4.stgRoom if item:getPosition():isInRange(fromPos, toPos) then - local stgbarrel = item:getCustomAttribute(Storage.DangerousDepths.Scouts.Barrel) or -1 - local player = Player(stgbarrel) + local stgBarrel = item:getCustomAttribute(Storage.Quest.U11_50.DangerousDepths.Scouts.Barrel) or -1 + local player = Player(stgBarrel) if player then if player:getStorageValue(stgRoom) < 1 then - player:setStorageValue(Storage.DangerousDepths.Scouts.BarrelCount, player:getStorageValue(Storage.DangerousDepths.Scouts.BarrelCount) + 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.BarrelCount, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.BarrelCount) + 1) player:setStorageValue(stgRoom, 1) end end @@ -77,11 +77,11 @@ local function checarPos(item) for _, info5 in pairs(config.fifth) do local fromPos, toPos, stgRoom = info5.fromPosition, info5.toPosition, info5.stgRoom if item:getPosition():isInRange(fromPos, toPos) then - local stgbarrel = item:getCustomAttribute(Storage.DangerousDepths.Scouts.Barrel) or -1 - local player = Player(stgbarrel) + local stgBarrel = item:getCustomAttribute(Storage.Quest.U11_50.DangerousDepths.Scouts.Barrel) or -1 + local player = Player(stgBarrel) if player then if player:getStorageValue(stgRoom) < 1 then - player:setStorageValue(Storage.DangerousDepths.Scouts.BarrelCount, player:getStorageValue(Storage.DangerousDepths.Scouts.BarrelCount) + 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.BarrelCount, player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.BarrelCount) + 1) player:setStorageValue(stgRoom, 1) end end @@ -93,57 +93,55 @@ local function explode(item) local position = item:getPosition() local fromPosition = Position(position.x - 6, position.y - 6, position.z) local toPosition = Position(position.x + 6, position.y + 6, position.z) + local c = Game.getPlayers()[1] addEvent(function() for x = fromPosition.x, toPosition.x do for y = fromPosition.y, toPosition.y do for z = fromPosition.z, toPosition.z do - Position(x, y, z):sendMagicEffect(CONST_ME_FIREAREA) + if Tile(Position(x, y, z)) then + if Tile(Position(x, y, z)) then + local posEffect = Tile(Position(x, y, z)):getPosition() + local creature = Tile(Position(x, y, z)):getTopCreature() + posEffect:sendMagicEffect(CONST_ME_FIREAREA) + end + end end end end - checarPos(item) + checkPos(item) + c:say("KABOOM!!", TALKTYPE_MONSTER_SAY, false, false, position) if item then item:remove() end end, 2 * 1000) - item:transform(32401) + item:transform(27491) + c:say("Tsssss...!", TALKTYPE_MONSTER_SAY, false, false, position) end local dangerousDepthLever = Action() + function dangerousDepthLever.onUse(player, item) if not player then return true end local posBarrel = Position(33838, 32077, 14) - local stgCount = player:getStorageValue(Storage.DangerousDepths.Scouts.BarrelCount) - local BarrelTimer = player:getStorageValue(Storage.DangerousDepths.Scouts.BarrelTimer) + local stgCount = player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.BarrelCount) + local barrelTimer = player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.BarrelTimer) if item:getId() == 2772 then - if player:getStorageValue(Storage.DangerousDepths.Scouts.Growth) == 1 and stgCount < 5 and BarrelTimer <= 0 then - local barrel = Game.createItem(27492, 1, posBarrel) - if not barrel then - return false - end - barrel:setCustomAttribute(Storage.DangerousDepths.Scouts.Barrel, player:getId()) - - addEvent(function(pos, pid) - local tile = Tile(pos) - if tile then - local barrel = tile:getItemById(27492) - if barrel then - explode(barrel) - end - end - local player = Player(pid) - if player then - player:setStorageValue(Storage.DangerousDepths.Scouts.BarrelTimer, 0) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Growth) == 1 and stgCount < 5 and barrelTimer <= 0 then + local Barrel = Game.createItem(27492, 1, posBarrel) + local stgBarrel = Barrel:getCustomAttribute(Storage.Quest.U11_50.DangerousDepths.Scouts.Barrel) or -1 + Barrel:setCustomAttribute(Storage.Quest.U11_50.DangerousDepths.Scouts.Barrel, player:getId()) + addEvent(function() + if Barrel then + explode(Barrel) end - end, 2 * 60 * 1000, posBarrel, player:getId()) - - --Time is set to 0 when barrel explodes - player:setStorageValue(Storage.DangerousDepths.Scouts.BarrelTimer, os.time() + 2 * 60) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.BarrelTimer, 0) + end, 2 * 60 * 1000) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.BarrelTimer, os.time() + 2 * 60) end end item:transform(transformid[item:getId()]) diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/actions_using_crystals.lua b/data-otservbr-global/scripts/quests/dangerous_depth/actions_using_crystals.lua index 84dc7e433fa..6f134dab02d 100644 --- a/data-otservbr-global/scripts/quests/dangerous_depth/actions_using_crystals.lua +++ b/data-otservbr-global/scripts/quests/dangerous_depth/actions_using_crystals.lua @@ -420,14 +420,14 @@ function dangerousDepthCrystals.onUse(player, item, fromPosition, target, toPosi local targetPosition = target:getPosition() if targetPosition == WarzoneIV and target:getId() == geodeId then -- Warzone 4 BOSS!!! - if Game.getStorageValue(GlobalStorage.DangerousDepths.Geodes.WarzoneIV) < 30 then + if Game.getStorageValue(Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneIV) < 30 then targetPosition:sendMagicEffect(CONST_ME_HITAREA) item:remove(1) - if Game.getStorageValue(GlobalStorage.DangerousDepths.Geodes.WarzoneIV) < 0 then - Game.setStorageValue(GlobalStorage.DangerousDepths.Geodes.WarzoneIV, 0) + if Game.getStorageValue(Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneIV) < 0 then + Game.setStorageValue(Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneIV, 0) end - Game.setStorageValue(GlobalStorage.DangerousDepths.Geodes.WarzoneIV, Game.getStorageValue(GlobalStorage.DangerousDepths.Geodes.WarzoneIV) + 1) - if Game.getStorageValue(GlobalStorage.DangerousDepths.Geodes.WarzoneIV) == 30 then + Game.setStorageValue(Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneIV, Game.getStorageValue(Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneIV) + 1) + if Game.getStorageValue(Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneIV) == 30 then local spectators = Game.getSpectators(targetPosition, false, true, 3, 3, 3, 3) for _, spectator in pairs(spectators) do if spectator:isPlayer() then @@ -445,7 +445,7 @@ function dangerousDepthCrystals.onUse(player, item, fromPosition, target, toPosi Game.createItem(388, 1, Position(33460, 32267, 15)) end end, 8 * 1000) - addEvent(clearForgotten, 30 * 60 * 1000, Position(33638, 32291, 15), Position(33675, 32313, 15), Position(33462, 32267, 15), GlobalStorage.DangerousDepths.Geodes.WarzoneIV) + addEvent(clearForgotten, 30 * 60 * 1000, Position(33638, 32291, 15), Position(33675, 32313, 15), Position(33462, 32267, 15), Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneIV) startWarzoneIV() end end @@ -455,14 +455,14 @@ function dangerousDepthCrystals.onUse(player, item, fromPosition, target, toPosi end if targetPosition == WarzoneV and target:getId() == geodeId then -- Warzone 5 BOSS!!! - if Game.getStorageValue(GlobalStorage.DangerousDepths.Geodes.WarzoneV) < 30 then + if Game.getStorageValue(Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneV) < 30 then targetPosition:sendMagicEffect(CONST_ME_HITAREA) item:remove(1) - if Game.getStorageValue(GlobalStorage.DangerousDepths.Geodes.WarzoneV) < 0 then - Game.setStorageValue(GlobalStorage.DangerousDepths.Geodes.WarzoneV, 0) + if Game.getStorageValue(Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneV) < 0 then + Game.setStorageValue(Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneV, 0) end - Game.setStorageValue(GlobalStorage.DangerousDepths.Geodes.WarzoneV, Game.getStorageValue(GlobalStorage.DangerousDepths.Geodes.WarzoneV) + 1) - if Game.getStorageValue(GlobalStorage.DangerousDepths.Geodes.WarzoneV) == 30 then + Game.setStorageValue(Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneV, Game.getStorageValue(Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneV) + 1) + if Game.getStorageValue(Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneV) == 30 then local spectators = Game.getSpectators(targetPosition, false, true, 3, 3, 3, 3) for _, spectator in pairs(spectators) do if spectator:isPlayer() then @@ -480,7 +480,7 @@ function dangerousDepthCrystals.onUse(player, item, fromPosition, target, toPosi Game.createItem(388, 1, Position(33324, 32109, 15)) end end, 8 * 1000) - addEvent(clearForgotten, 30 * 60 * 1000, Position(33668, 32310, 15), Position(33695, 32343, 15), Position(33323, 32111, 15), GlobalStorage.DangerousDepths.Geodes.WarzoneV) + addEvent(clearForgotten, 30 * 60 * 1000, Position(33668, 32310, 15), Position(33695, 32343, 15), Position(33323, 32111, 15), Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneV) startWarzoneV() end end @@ -490,14 +490,14 @@ function dangerousDepthCrystals.onUse(player, item, fromPosition, target, toPosi end if targetPosition == WarzoneVI and target:getId() == geodeId then -- Warzone 6 BOSS!!! - if Game.getStorageValue(GlobalStorage.DangerousDepths.Geodes.WarzoneVI) < 30 then + if Game.getStorageValue(Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneVI) < 30 then targetPosition:sendMagicEffect(CONST_ME_HITAREA) item:remove(1) - if Game.getStorageValue(GlobalStorage.DangerousDepths.Geodes.WarzoneVI) < 0 then - Game.setStorageValue(GlobalStorage.DangerousDepths.Geodes.WarzoneVI, 0) + if Game.getStorageValue(Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneVI) < 0 then + Game.setStorageValue(Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneVI, 0) end - Game.setStorageValue(GlobalStorage.DangerousDepths.Geodes.WarzoneVI, Game.getStorageValue(GlobalStorage.DangerousDepths.Geodes.WarzoneVI) + 1) - if Game.getStorageValue(GlobalStorage.DangerousDepths.Geodes.WarzoneVI) == 30 then + Game.setStorageValue(Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneVI, Game.getStorageValue(Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneVI) + 1) + if Game.getStorageValue(Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneVI) == 30 then local spectators = Game.getSpectators(targetPosition, false, true, 3, 3, 3, 3) for _, spectator in pairs(spectators) do if spectator:isPlayer() then @@ -515,7 +515,7 @@ function dangerousDepthCrystals.onUse(player, item, fromPosition, target, toPosi Game.createItem(388, 1, Position(33275, 32316, 15)) end end, 8 * 1000) - addEvent(clearForgotten, 30 * 60 * 1000, Position(33684, 32293, 15), Position(33724, 32314, 15), Position(33275, 32318, 15), GlobalStorage.DangerousDepths.Geodes.WarzoneVI) + addEvent(clearForgotten, 30 * 60 * 1000, Position(33684, 32293, 15), Position(33724, 32314, 15), Position(33275, 32318, 15), Storage.Quest.U11_50.DangerousDepths.Geodes.WarzoneVI) startWarzoneVI() end end diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/actions_warzone_crystals.lua b/data-otservbr-global/scripts/quests/dangerous_depth/actions_warzone_crystals.lua index 910b900c4da..64ebb40f399 100644 --- a/data-otservbr-global/scripts/quests/dangerous_depth/actions_warzone_crystals.lua +++ b/data-otservbr-global/scripts/quests/dangerous_depth/actions_warzone_crystals.lua @@ -2,28 +2,28 @@ local crystalDuration = 5 * 60 -- 5 minutes local crystals = { -- Warzone IV - [57350] = Storage.DangerousDepths.Crystals.WarzoneVI.MediumCrystal1, - [57351] = Storage.DangerousDepths.Crystals.WarzoneVI.BigCrystal1, - [57352] = Storage.DangerousDepths.Crystals.WarzoneVI.BigCrystal2, - [57353] = Storage.DangerousDepths.Crystals.WarzoneVI.MediumCrystal2, - [57354] = Storage.DangerousDepths.Crystals.WarzoneVI.SmallCrystal1, - [57355] = Storage.DangerousDepths.Crystals.WarzoneVI.SmallCrystal2, + [57350] = Storage.Quest.U11_50.DangerousDepths.Crystals.WarzoneVI.MediumCrystal1, + [57351] = Storage.Quest.U11_50.DangerousDepths.Crystals.WarzoneVI.BigCrystal1, + [57352] = Storage.Quest.U11_50.DangerousDepths.Crystals.WarzoneVI.BigCrystal2, + [57353] = Storage.Quest.U11_50.DangerousDepths.Crystals.WarzoneVI.MediumCrystal2, + [57354] = Storage.Quest.U11_50.DangerousDepths.Crystals.WarzoneVI.SmallCrystal1, + [57355] = Storage.Quest.U11_50.DangerousDepths.Crystals.WarzoneVI.SmallCrystal2, -- Warzone V - [57356] = Storage.DangerousDepths.Crystals.WarzoneV.BigCrystal1, - [57357] = Storage.DangerousDepths.Crystals.WarzoneIV.MediumCrystal1, - [57358] = Storage.DangerousDepths.Crystals.WarzoneV.BigCrystal2, - [57359] = Storage.DangerousDepths.Crystals.WarzoneIV.MediumCrystal2, - [57360] = Storage.DangerousDepths.Crystals.WarzoneV.SmallCrystal1, - [57361] = Storage.DangerousDepths.Crystals.WarzoneV.SmallCrystal2, + [57356] = Storage.Quest.U11_50.DangerousDepths.Crystals.WarzoneV.BigCrystal1, + [57357] = Storage.Quest.U11_50.DangerousDepths.Crystals.WarzoneIV.MediumCrystal1, + [57358] = Storage.Quest.U11_50.DangerousDepths.Crystals.WarzoneV.BigCrystal2, + [57359] = Storage.Quest.U11_50.DangerousDepths.Crystals.WarzoneIV.MediumCrystal2, + [57360] = Storage.Quest.U11_50.DangerousDepths.Crystals.WarzoneV.SmallCrystal1, + [57361] = Storage.Quest.U11_50.DangerousDepths.Crystals.WarzoneV.SmallCrystal2, -- Warzone IV - [57362] = Storage.DangerousDepths.Crystals.WarzoneIV.BigCrystal1, - [57363] = Storage.DangerousDepths.Crystals.WarzoneIV.MediumCrystal1, - [57364] = Storage.DangerousDepths.Crystals.WarzoneIV.BigCrystal2, - [57365] = Storage.DangerousDepths.Crystals.WarzoneIV.MediumCrystal2, - [57366] = Storage.DangerousDepths.Crystals.WarzoneIV.SmallCrystal1, - [57367] = Storage.DangerousDepths.Crystals.WarzoneIV.SmallCrystal2, + [57362] = Storage.Quest.U11_50.DangerousDepths.Crystals.WarzoneIV.BigCrystal1, + [57363] = Storage.Quest.U11_50.DangerousDepths.Crystals.WarzoneIV.MediumCrystal1, + [57364] = Storage.Quest.U11_50.DangerousDepths.Crystals.WarzoneIV.BigCrystal2, + [57365] = Storage.Quest.U11_50.DangerousDepths.Crystals.WarzoneIV.MediumCrystal2, + [57366] = Storage.Quest.U11_50.DangerousDepths.Crystals.WarzoneIV.SmallCrystal1, + [57367] = Storage.Quest.U11_50.DangerousDepths.Crystals.WarzoneIV.SmallCrystal2, } local crystalsChance = { diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/actions_wooden_trash.lua b/data-otservbr-global/scripts/quests/dangerous_depth/actions_wooden_trash.lua index 15aa8ec6f4b..0ca92bacb45 100644 --- a/data-otservbr-global/scripts/quests/dangerous_depth/actions_wooden_trash.lua +++ b/data-otservbr-global/scripts/quests/dangerous_depth/actions_wooden_trash.lua @@ -1,12 +1,13 @@ local dangerousDepthWooden = Action() + function dangerousDepthWooden.onUse(creature, item) if not creature or not creature:isPlayer() then return true end local r = math.random(1, 100) - local stgValueP = creature:getStorageValue(Storage.DangerousDepths.Dwarves.Prisoners) + local stgValueP = creature:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Prisoners) - if creature:getStorageValue(Storage.DangerousDepths.Dwarves.Home) == 1 and stgValueP < 3 then + if creature:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Home) == 1 and stgValueP < 3 then if r <= 25 then creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You started an escort, get your prisoner to the dwarf outpost!") local prisoner = Game.createMonster("Captured Dwarf", item:getPosition()) diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/creaturescripts_lost_exile_task.lua b/data-otservbr-global/scripts/quests/dangerous_depth/creaturescripts_lost_exile_task.lua index 24abf74281c..2802324b3c3 100644 --- a/data-otservbr-global/scripts/quests/dangerous_depth/creaturescripts_lost_exile_task.lua +++ b/data-otservbr-global/scripts/quests/dangerous_depth/creaturescripts_lost_exile_task.lua @@ -1,40 +1,62 @@ local fromPos = Position(33768, 32227, 14) -local toPos = Position(33781, 32249, 14) +local toPos = Position(33851, 32352, 14) +local radius = 10 local lostExileKill = CreatureEvent("LastExileDeath") + function lostExileKill.onDeath(creature, _corpse, _lastHitKiller, mostDamageKiller) + local function isMakeshiftHomeNearby(creature) + local creaturePosition = creature:getPosition() + local spectators = Game.getSpectators(creaturePosition, false, false, radius, radius, radius, radius) -- Busca criaturas em torno da posição da morte + + for _, spectator in ipairs(spectators) do + if spectator:isMonster() and spectator:getName():lower() == "makeshift home" then + return true + end + end + return false + end + onDeathForParty(creature, mostDamageKiller, function(creature, player) - if player:getStorageValue(Storage.DangerousDepths.Dwarves.Home) ~= 1 then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Home) ~= 1 then return end + if not creature:getPosition():isInRange(fromPos, toPos) then return end - local storage = player:getStorageValue(Storage.DangerousDepths.Dwarves.LostExiles) + + if isMakeshiftHomeNearby(creature) then + return + end + + local storage = player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.LostExiles) if storage < 20 then if storage < 0 then - player:setStorageValue(Storage.DangerousDepths.Dwarves.LostExiles, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.LostExiles, 1) end - player:setStorageValue(Storage.DangerousDepths.Dwarves.LostExiles, storage + 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.LostExiles, storage + 1) end end) + return true end lostExileKill:register() local wormKill = CreatureEvent("WarzoneWormDeath") + function wormKill.onDeath(creature, _corpse, _lastHitKiller, mostDamageKiller) - local storage = mostDamageKiller:getStorageValue(Storage.DangerousDepths.Dwarves.Organisms) + local storage = mostDamageKiller:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Organisms) onDeathForParty(creature, mostDamageKiller, function(creature, player) - if player:getStorageValue(Storage.DangerousDepths.Dwarves.Subterranean) ~= 1 then + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Subterranean) ~= 1 then return end if storage < 50 then if storage < 0 then - player:setStorageValue(Storage.DangerousDepths.Dwarves.Organisms, 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Organisms, 1) end - player:setStorageValue(Storage.DangerousDepths.Dwarves.Organisms, storage + 1) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Organisms, storage + 1) end end) @@ -44,6 +66,7 @@ end wormKill:register() local makeshiftKill = CreatureEvent("MakeshiftHomeDeath") + function makeshiftKill.onDeath(creature, _corpse, _lastHitKiller, mostDamageKiller) local woodenTrash = Game.createItem(398, 1, creature:getPosition()) woodenTrash:setActionId(57233) diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/movements_energy_entrance.lua b/data-otservbr-global/scripts/quests/dangerous_depth/movements_energy_entrance.lua index c4d76a05488..ebfd9a1f574 100644 --- a/data-otservbr-global/scripts/quests/dangerous_depth/movements_energy_entrance.lua +++ b/data-otservbr-global/scripts/quests/dangerous_depth/movements_energy_entrance.lua @@ -14,8 +14,14 @@ function energyEntrance.onStepIn(creature, item, position, fromPosition, toPosit if not player then return true end + + local function hasWarzoneAccess() + return player:getStorageValue(Storage.Quest.U9_60.BigfootsBurden.Warzone1Access) == 1 and player:getStorageValue(Storage.Quest.U9_60.BigfootsBurden.Warzone2Access) == 1 and player:getStorageValue(Storage.Quest.U9_60.BigfootsBurden.Warzone3Access) == 1 + end + if player:getPosition() == Position(33831, 32138, 14) then - if player:getStorageValue(Storage.DangerousDepths.Scouts.Status) >= -50 then + local status = player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Scouts.Points) + if (hasWarzoneAccess() and status >= 10) or (not hasWarzoneAccess() and status >= 15) then player:teleportTo(Position(34023, 32037, 14)) else player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can not use this portal yet.") @@ -23,10 +29,9 @@ function energyEntrance.onStepIn(creature, item, position, fromPosition, toPosit end elseif player:getPosition() == Position(34021, 32037, 14) then player:teleportTo(Position(33831, 32141, 14)) - end - - if player:getPosition() == Position(33784, 32205, 14) then - if player:getStorageValue(Storage.DangerousDepths.Dwarves.Status) >= -50 then + elseif player:getPosition() == Position(33784, 32205, 14) then + local status = player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Points) + if (hasWarzoneAccess() and status >= 10) or (not hasWarzoneAccess() and status >= 15) then player:teleportTo(Position(33921, 32401, 14)) else player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can not use this portal yet.") @@ -34,10 +39,9 @@ function energyEntrance.onStepIn(creature, item, position, fromPosition, toPosit end elseif player:getPosition() == Position(33921, 32402, 14) then player:teleportTo(Position(33782, 32205, 14)) - end - - if player:getPosition() == Position(33829, 32187, 14) then - if player:getStorageValue(Storage.DangerousDepths.Gnomes.Status) >= -50 then + elseif player:getPosition() == Position(33829, 32187, 14) then + local status = player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Points) + if (hasWarzoneAccess() and status >= 10) or (not hasWarzoneAccess() and status >= 15) then player:teleportTo(Position(33982, 32236, 14)) else player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can not use this portal yet.") @@ -46,6 +50,7 @@ function energyEntrance.onStepIn(creature, item, position, fromPosition, toPosit elseif player:getPosition() == Position(33982, 32234, 14) then player:teleportTo(Position(33829, 32186, 14)) end + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) return true @@ -54,4 +59,5 @@ end for index, value in pairs(positions) do energyEntrance:position(value) end + energyEntrance:register() diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/movements_gnome_ordnance.lua b/data-otservbr-global/scripts/quests/dangerous_depth/movements_gnome_ordnance.lua index c74f3db575c..555ba543a26 100644 --- a/data-otservbr-global/scripts/quests/dangerous_depth/movements_gnome_ordnance.lua +++ b/data-otservbr-global/scripts/quests/dangerous_depth/movements_gnome_ordnance.lua @@ -6,8 +6,8 @@ function gnomeOrdnance.onStepIn(creature, position, fromPosition, toPosition) return true end - if player:getStorageValue(Storage.DangerousDepths.Gnomes.Ordnance) == 1 then - player:setStorageValue(Storage.DangerousDepths.Gnomes.Ordnance, 2) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Ordnance) == 1 then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Ordnance, 2) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You started an escort, get everyone to safety!") end return true diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/movements_gnomes_ordnance_end.lua b/data-otservbr-global/scripts/quests/dangerous_depth/movements_gnomes_ordnance_end.lua index 3bb8c2f8c9e..cda301170c1 100644 --- a/data-otservbr-global/scripts/quests/dangerous_depth/movements_gnomes_ordnance_end.lua +++ b/data-otservbr-global/scripts/quests/dangerous_depth/movements_gnomes_ordnance_end.lua @@ -17,9 +17,9 @@ function gnomesOrdnanceEnd.onStepIn(creature, position, fromPosition, toPosition local c = Tile(Position(x, y, z)):getTopCreature() if c then if c:isPlayer() then - if c:getStorageValue(Storage.DangerousDepths.Gnomes.Ordnance) == 2 then - if c:getStorageValue(Storage.DangerousDepths.Gnomes.GnomesCount) < 5 then - c:setStorageValue(Storage.DangerousDepths.Gnomes.GnomesCount, c:getStorageValue(Storage.DangerousDepths.Gnomes.GnomesCount) + 1) + if c:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Ordnance) == 2 then + if c:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.GnomesCount) < 5 then + c:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.GnomesCount, c:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.GnomesCount) + 1) c:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your escort has end.") end end @@ -41,9 +41,9 @@ function gnomesOrdnanceEnd.onStepIn(creature, position, fromPosition, toPosition local c = Tile(Position(x, y, z)):getTopCreature() if c then if c:isPlayer() then - if c:getStorageValue(Storage.DangerousDepths.Gnomes.Ordnance) == 2 then - if c:getStorageValue(Storage.DangerousDepths.Gnomes.CrawlersCount) < 3 then - c:setStorageValue(Storage.DangerousDepths.Gnomes.CrawlersCount, c:getStorageValue(Storage.DangerousDepths.Gnomes.CrawlersCount) + 1) + if c:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.Ordnance) == 2 then + if c:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.CrawlersCount) < 3 then + c:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.CrawlersCount, c:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Gnomes.CrawlersCount) + 1) c:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your escort has end.") end end diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/movements_lost_exiles.lua b/data-otservbr-global/scripts/quests/dangerous_depth/movements_lost_exiles.lua index 2c53dbaba75..5dbb5225fc9 100644 --- a/data-otservbr-global/scripts/quests/dangerous_depth/movements_lost_exiles.lua +++ b/data-otservbr-global/scripts/quests/dangerous_depth/movements_lost_exiles.lua @@ -17,9 +17,9 @@ function lostExiles.onStepIn(creature, position, fromPosition, toPosition) local creature = Tile(Position(x, y, z)):getTopCreature() if creature then if creature:isPlayer() then - if creature:getStorageValue(Storage.DangerousDepths.Dwarves.Home) == 1 then - if creature:getStorageValue(Storage.DangerousDepths.Dwarves.Prisoners) < 3 then - creature:setStorageValue(Storage.DangerousDepths.Dwarves.Prisoners, creature:getStorageValue(Storage.DangerousDepths.Dwarves.Prisoners) + 1) + if creature:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Home) == 1 then + if creature:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Prisoners) < 3 then + creature:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Prisoners, creature:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Dwarves.Prisoners) + 1) creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your escort has end.") end end diff --git a/data-otservbr-global/scripts/quests/dangerous_depth/movements_warzone_entrance.lua b/data-otservbr-global/scripts/quests/dangerous_depth/movements_warzone_entrance.lua index 83120868640..ecb404fe5ef 100644 --- a/data-otservbr-global/scripts/quests/dangerous_depth/movements_warzone_entrance.lua +++ b/data-otservbr-global/scripts/quests/dangerous_depth/movements_warzone_entrance.lua @@ -8,65 +8,54 @@ function warzoneEntrance.onStepIn(creature, item, position, fromPosition, toPosi local warzoneVI = Position(33367, 32307, 15) if item:getPosition() == Position(33829, 32128, 14) then - if player:getStorageValue(Storage.DangerousDepths.Access.LavaPumpWarzoneVI) == 1 and player:getStorageValue(Storage.DangerousDepths.Access.TimerWarzoneVI) <= os.time() then - player:setStorageValue(Storage.DangerousDepths.Access.LavaPumpWarzoneVI, 0) - player:setStorageValue(Storage.DangerousDepths.Scouts.Status, player:getStorageValue(Storage.DangerousDepths.Scouts.Status) - 10) - player:setStorageValue(Storage.DangerousDepths.Access.TimerWarzoneVI, os.time() + 8 * 60 * 60) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneVI) == 1 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.TimerWarzoneVI) <= os.time() then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneVI, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.TimerWarzoneVI, os.time() + 8 * 60 * 60) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Entering the warzone (you can enter freely for 8 hours from now).") player:teleportTo(warzoneVI) - elseif player:getStorageValue(Storage.DangerousDepths.Access.LavaPumpWarzoneVI) ~= 1 and player:getStorageValue(Storage.DangerousDepths.Access.TimerWarzoneVI) <= os.time() then + elseif player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneVI) ~= 1 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.TimerWarzoneVI) <= os.time() then player:teleportTo(Position(fromPosition.x + 1, fromPosition.y, fromPosition.z)) - player:sendTextMessage( - MESSAGE_EVENT_ADVANCE, - "You cannot enter this warzone. \z - The enemy still pumps lava into this area. Find a way to stop the pumps!" - ) - elseif player:getStorageValue(Storage.DangerousDepths.Access.TimerWarzoneVI) > os.time() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You cannot enter this warzone. The enemy still pumps lava into this area. Find a way to stop the pumps!") + elseif player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.TimerWarzoneVI) > os.time() then player:teleportTo(warzoneVI) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Entering the warzone.") end end + local warzoneV = Position(33208, 32119, 15) if item:getPosition() == Position(33777, 32192, 14) then - if player:getStorageValue(Storage.DangerousDepths.Access.LavaPumpWarzoneV) == 1 and player:getStorageValue(Storage.DangerousDepths.Access.TimerWarzoneV) <= os.time() then - player:setStorageValue(Storage.DangerousDepths.Access.LavaPumpWarzoneV, 0) - player:setStorageValue(Storage.DangerousDepths.Dwarves.Status, player:getStorageValue(Storage.DangerousDepths.Dwarves.Status) - 10) - player:setStorageValue(Storage.DangerousDepths.Access.TimerWarzoneV, os.time() + 8 * 60 * 60) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneV) == 1 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.TimerWarzoneV) <= os.time() then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneV, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.TimerWarzoneV, os.time() + 8 * 60 * 60) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Entering the warzone (you can enter freely for 8 hours from now).") player:teleportTo(warzoneV) - elseif player:getStorageValue(Storage.DangerousDepths.Access.LavaPumpWarzoneV) ~= 1 and player:getStorageValue(Storage.DangerousDepths.Access.TimerWarzoneV) <= os.time() then + elseif player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneV) ~= 1 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.TimerWarzoneV) <= os.time() then player:teleportTo(Position(fromPosition.x, fromPosition.y + 1, fromPosition.z)) - player:sendTextMessage( - MESSAGE_EVENT_ADVANCE, - "You cannot enter this warzone. \z - The enemy still pumps lava into this area. Find a way to stop the pumps!" - ) - elseif player:getStorageValue(Storage.DangerousDepths.Access.TimerWarzoneV) > os.time() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You cannot enter this warzone.The enemy still pumps lava into this area. Find a way to stop the pumps!") + elseif player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.TimerWarzoneV) > os.time() then player:teleportTo(warzoneV) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Entering the warzone.") end end + local warzoneIV = Position(33534, 32184, 15) if item:getPosition() == Position(33827, 32172, 14) then - if player:getStorageValue(Storage.DangerousDepths.Access.LavaPumpWarzoneIV) == 1 and player:getStorageValue(Storage.DangerousDepths.Access.TimerWarzoneIV) <= os.time() then - player:setStorageValue(Storage.DangerousDepths.Access.LavaPumpWarzoneIV, 0) - player:setStorageValue(Storage.DangerousDepths.Gnomes.Status, player:getStorageValue(Storage.DangerousDepths.Gnomes.Status) - 10) - player:setStorageValue(Storage.DangerousDepths.Access.TimerWarzoneIV, os.time() + 8 * 60 * 60) + if player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneIV) == 1 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.TimerWarzoneIV) <= os.time() then + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneIV, 0) + player:setStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.TimerWarzoneIV, os.time() + 8 * 60 * 60) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Entering the warzone (you can enter freely for 8 hours from now).") player:teleportTo(warzoneIV) - elseif player:getStorageValue(Storage.DangerousDepths.Access.LavaPumpWarzoneIV) ~= 1 and player:getStorageValue(Storage.DangerousDepths.Access.TimerWarzoneIV) <= os.time() then + elseif player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneIV) ~= 1 and player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.TimerWarzoneIV) <= os.time() then player:teleportTo(Position(fromPosition.x, fromPosition.y + 1, fromPosition.z)) - player:sendTextMessage( - MESSAGE_EVENT_ADVANCE, - "You cannot enter this warzone. \z - The enemy still pumps lava into this area. Find a way to stop the pumps!" - ) - elseif player:getStorageValue(Storage.DangerousDepths.Access.TimerWarzoneIV) > os.time() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You cannot enter this warzone. The enemy still pumps lava into this area. Find a way to stop the pumps!") + elseif player:getStorageValue(Storage.Quest.U11_50.DangerousDepths.Access.TimerWarzoneIV) > os.time() then player:teleportTo(warzoneIV) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Entering the warzone.") end end + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + return true end diff --git a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_ferumbras_lever.lua b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_ferumbras_lever.lua index 161aaafb3e7..7828b7d935e 100644 --- a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_ferumbras_lever.lua +++ b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_ferumbras_lever.lua @@ -1,103 +1,90 @@ +local crystals = { + [1] = { crystalPosition = Position(33390, 31468, 14), globalStorage = Storage.Quest.U10_90.FerumbrasAscension.Crystals.Crystal1 }, + [2] = { crystalPosition = Position(33394, 31468, 14), globalStorage = Storage.Quest.U10_90.FerumbrasAscension.Crystals.Crystal2 }, + [3] = { crystalPosition = Position(33397, 31471, 14), globalStorage = Storage.Quest.U10_90.FerumbrasAscension.Crystals.Crystal3 }, + [4] = { crystalPosition = Position(33397, 31475, 14), globalStorage = Storage.Quest.U10_90.FerumbrasAscension.Crystals.Crystal4 }, + [5] = { crystalPosition = Position(33394, 31478, 14), globalStorage = Storage.Quest.U10_90.FerumbrasAscension.Crystals.Crystal5 }, + [6] = { crystalPosition = Position(33390, 31478, 14), globalStorage = Storage.Quest.U10_90.FerumbrasAscension.Crystals.Crystal6 }, + [7] = { crystalPosition = Position(33387, 31475, 14), globalStorage = Storage.Quest.U10_90.FerumbrasAscension.Crystals.Crystal7 }, + [8] = { crystalPosition = Position(33387, 31471, 14), globalStorage = Storage.Quest.U10_90.FerumbrasAscension.Crystals.Crystal8 }, +} + local config = { - boss = { - name = "Ferumbras Mortal Shell", - position = Position(33392, 31473, 14), - }, + centerRoom = Position(33392, 31473, 14), + BossPosition = Position(33392, 31473, 14), playerPositions = { - { pos = Position(33269, 31477, 14), teleport = Position(33390, 31483, 14), effect = CONST_ME_TELEPORT }, - }, - specPos = { - from = Position(33374, 31458, 14), - to = Position(33414, 31498, 14), + Position(33269, 31477, 14), + Position(33269, 31478, 14), + Position(33269, 31479, 14), + Position(33269, 31480, 14), + Position(33269, 31481, 14), + Position(33270, 31477, 14), + Position(33270, 31478, 14), + Position(33270, 31479, 14), + Position(33270, 31480, 14), + Position(33270, 31481, 14), + Position(33271, 31477, 14), + Position(33271, 31478, 14), + Position(33271, 31479, 14), + Position(33271, 31480, 14), + Position(33271, 31481, 14), }, - exit = Position(33319, 32318, 13), - centerRoom = Position(33392, 31473, 14), - summonName = "Rift Fragment", - maxSummon = 3, + newPosition = Position(33392, 31479, 14), } local leverFerumbras = Action() function leverFerumbras.onUse(player, item, fromPosition, target, toPosition, isHotkey) - local spectators = Game.getSpectators(config.specPos.from, false, false, 0, 0, 0, 0, config.specPos.to) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - player:say("Someone is already inside the room.", TALKTYPE_MONSTER_SAY) - return true - end - end - - if isBossInRoom(config.specPos.from, config.specPos.to, config.boss.name) then - player:say("The room is being cleared. Please wait a moment.", TALKTYPE_MONSTER_SAY) - return true - end - - local players = {} - for i = 1, #config.playerPositions do - local pos = config.playerPositions[i].pos - local creature = Tile(pos):getTopCreature() - if not creature or not creature:isPlayer() then - player:sendCancelMessage("You need " .. #config.playerPositions .. " players to challenge " .. config.boss.name .. ".") + local playersTable = {} + if item.itemid == 8911 then + if player:getPosition() ~= Position(33270, 31477, 14) then + item:transform(8912) return true end - table.insert(players, creature) end - - for i = 1, #players do - local playerToTeleport = players[i] - local teleportPos = config.playerPositions[i].teleport - local effect = config.playerPositions[i].effect - playerToTeleport:teleportTo(teleportPos) - teleportPos:sendMagicEffect(effect) - end - - Game.createMonster(config.boss.name, config.boss.position, true, true) - for b = 1, config.maxSummon do - local xrand = math.random(-5, 5) - local yrand = math.random(-5, 5) - local position = Position(config.boss.position.x + xrand, config.boss.position.y + yrand, config.boss.position.z) - Game.createMonster(config.summonName, position) - end - if item.itemid == 8911 then - item:transform(8912) - else - item:transform(8911) - end - - return true -end - -function clearBossRoom(fromPos, toPos, exitPos) - local spectators = Game.getSpectators(fromPos, false, false, 0, 0, 0, 0, toPos) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - spec:teleportTo(exitPos) - exitPos:sendMagicEffect(CONST_ME_TELEPORT) - spec:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You took too long, the battle has ended.") - else - spec:remove() - end - end -end - -function isBossInRoom(fromPos, toPos, bossName) - local monstersRemoved = false - for x = fromPos.x, toPos.x do - for y = fromPos.y, toPos.y do - for z = fromPos.z, toPos.z do - local tile = Tile(Position(x, y, z)) - if tile then - local creature = tile:getTopCreature() - if creature and creature:isMonster() then - creature:remove() - monstersRemoved = true + if player:doCheckBossRoom("Ascending Ferumbras", Position(33379, 31460, 14), Position(33405, 31485, 14)) then + Game.createMonster("Ascending Ferumbras", config.BossPosition, true, true) + for b = 1, 10 do + local xrand = math.random(-10, 10) + local yrand = math.random(-10, 10) + local position = Position(33392 + xrand, 31473 + yrand, 14) + if Game.createMonster("rift invader", position) then + end + end + for x = 33269, 33271 do + for y = 31477, 31481 do + local playerTile = Tile(Position(x, y, 14)):getTopCreature() + if playerTile and playerTile:isPlayer() then + playerTile:getPosition():sendMagicEffect(CONST_ME_POFF) + playerTile:teleportTo(config.newPosition) + playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + playerTile:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.FerumbrasTimer, os.time() + 60 * 60 * 20 * 24) + table.insert(playersTable, playerTile:getId()) + end + end + end + Game.setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.Crystals.AllCrystals, 0) + Game.setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.FerumbrasEssence, 0) + for _, crystal in pairs(crystals) do + local pos = crystal.crystalPosition + local stg = crystal.globalStorage + local sqm = Tile(pos) + if sqm then + local item = sqm:getItemById(14961) + if item then + item:transform(14955) end end + Game.setStorageValue(stg, 0) end + addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(33379, 31460, 14), Position(33405, 31485, 14), Position(33319, 32318, 13)) + item:transform(8912) end + elseif item.itemid == 8912 then + item:transform(8911) end - return monstersRemoved + return true end leverFerumbras:uid(1021) diff --git a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_mazoran.lua b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_mazoran.lua index 56cda9fb65b..aa564b94fd6 100644 --- a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_mazoran.lua +++ b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_mazoran.lua @@ -1,104 +1,56 @@ local config = { - boss = { - name = "Mazoran", - position = Position(33584, 32689, 14), - }, - timeToDefeat = 17 * 60, -- 17 minutes in seconds + centerRoom = Position(33584, 32689, 14), + BossPosition = Position(33584, 32689, 14), playerPositions = { - { pos = Position(33593, 32644, 14), teleport = Position(33585, 32693, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(33593, 32645, 14), teleport = Position(33585, 32693, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(33593, 32646, 14), teleport = Position(33585, 32693, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(33593, 32647, 14), teleport = Position(33585, 32693, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(33593, 32648, 14), teleport = Position(33585, 32693, 14), effect = CONST_ME_TELEPORT }, - }, - specPos = { - from = Position(33570, 32677, 14), - to = Position(33597, 32700, 14), + Position(33593, 32644, 14), + Position(33593, 32645, 14), + Position(33593, 32646, 14), + Position(33593, 32647, 14), + Position(33593, 32648, 14), }, - exit = Position(33319, 32318, 13), + newPosition = Position(33585, 32693, 14), } local leverMazoran = Action() function leverMazoran.onUse(player, item, fromPosition, target, toPosition, isHotkey) - local spectators = Game.getSpectators(config.specPos.from, false, false, 0, 0, 0, 0, config.specPos.to) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - player:say("Someone is already inside the room.", TALKTYPE_MONSTER_SAY) - return true - end - end - - if isBossInRoom(config.specPos.from, config.specPos.to, config.boss.name) then - player:say("The room is being cleared. Please wait a moment.", TALKTYPE_MONSTER_SAY) - return true - end - - local players = {} - for i = 1, #config.playerPositions do - local pos = config.playerPositions[i].pos - local creature = Tile(pos):getTopCreature() - if not creature or not creature:isPlayer() then - player:sendCancelMessage("You need " .. #config.playerPositions .. " players to challenge " .. config.boss.name .. ".") + if item.itemid == 8911 then + if player:getPosition() ~= Position(33593, 32644, 14) then + item:transform(8912) return true end - table.insert(players, creature) - end - - for i = 1, #players do - local playerToTeleport = players[i] - local teleportPos = config.playerPositions[i].teleport - local effect = config.playerPositions[i].effect - playerToTeleport:teleportTo(teleportPos) - teleportPos:sendMagicEffect(effect) end - local boss = Game.createMonster(config.boss.name, config.boss.position) - if boss then - boss:setReward(true) - end - - addEvent(clearBossRoom, config.timeToDefeat * 1000, config.specPos.from, config.specPos.to, config.exit) - if item.itemid == 8911 then - item:transform(8912) - else - item:transform(8911) - end - - return true -end - -function clearBossRoom(fromPos, toPos, exitPos) - local spectators = Game.getSpectators(fromPos, false, false, 0, 0, 0, 0, toPos) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - spec:teleportTo(exitPos) - exitPos:sendMagicEffect(CONST_ME_TELEPORT) - spec:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You took too long, the battle has ended.") - else - spec:remove() - end - end -end - -function isBossInRoom(fromPos, toPos, bossName) - local monstersRemoved = false - for x = fromPos.x, toPos.x do - for y = fromPos.y, toPos.y do - for z = fromPos.z, toPos.z do - local tile = Tile(Position(x, y, z)) - if tile then - local creature = tile:getTopCreature() - if creature and creature:isMonster() then - creature:remove() - monstersRemoved = true - end + local playersTable = {} + if player:doCheckBossRoom("Mazoran", Position(33572, 32679, 14), Position(33599, 32701, 14)) then + local specs, spec = Game.getSpectators(config.centerRoom, false, false, 15, 15, 15, 15) + for i = 1, #specs do + spec = specs[i] + if spec:isPlayer() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting with Mazoran.") + return true + end + end + Game.createMonster("Mazoran", config.BossPosition, true, true) + for y = 32644, 32648 do + local playerTile = Tile(Position(33593, y, 14)):getTopCreature() + if playerTile and playerTile:isPlayer() then + playerTile:getPosition():sendMagicEffect(CONST_ME_POFF) + playerTile:teleportTo(config.newPosition) + playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + playerTile:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.MazoranTimer, os.time() + os.time() + 60 * 60 * 2 * 24) + table.insert(playersTable, playerTile:getId()) end end + addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(33572, 32679, 14), Position(33599, 32701, 14), Position(33319, 32318, 13)) + item:transform(8912) end + elseif item.itemid == 8912 then + item:transform(8911) end - return monstersRemoved + + return true end leverMazoran:uid(1025) diff --git a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_plagirath.lua b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_plagirath.lua index b49a0d58db8..82d5535f8e6 100644 --- a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_plagirath.lua +++ b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_plagirath.lua @@ -1,102 +1,57 @@ local config = { - boss = { - name = "Plagirath", - position = Position(33172, 31501, 13), - }, - timeToDefeat = 17 * 60, -- 17 minutes in seconds + centerRoom = Position(33172, 31501, 13), + BossPosition = Position(33172, 31501, 13), playerPositions = { - { pos = Position(33229, 31500, 13), teleport = Position(33173, 31504, 13), effect = CONST_ME_TELEPORT }, - { pos = Position(33229, 31501, 13), teleport = Position(33173, 31504, 13), effect = CONST_ME_TELEPORT }, - { pos = Position(33229, 31502, 13), teleport = Position(33173, 31504, 13), effect = CONST_ME_TELEPORT }, - { pos = Position(33229, 31503, 13), teleport = Position(33173, 31504, 13), effect = CONST_ME_TELEPORT }, - { pos = Position(33229, 31504, 13), teleport = Position(33173, 31504, 13), effect = CONST_ME_TELEPORT }, - }, - specPos = { - from = Position(33159, 31488, 13), - to = Position(33190, 31515, 13), + Position(33229, 31500, 13), + Position(33229, 31501, 13), + Position(33229, 31502, 13), + Position(33229, 31503, 13), + Position(33229, 31504, 13), }, - exit = Position(33319, 32318, 13), + newPosition = Position(33173, 31504, 13), } local leverPlagirath = Action() function leverPlagirath.onUse(player, item, fromPosition, target, toPosition, isHotkey) - local spectators = Game.getSpectators(config.specPos.from, false, false, 0, 0, 0, 0, config.specPos.to) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - player:say("Someone is already inside the room.", TALKTYPE_MONSTER_SAY) + if item.itemid == 8911 then + if player:getPosition() ~= Position(33229, 31500, 13) then + item:transform(8912) return true end end - - if isBossInRoom(config.specPos.from, config.specPos.to, config.boss.name) then - player:say("The room is being cleared. Please wait a moment.", TALKTYPE_MONSTER_SAY) - return true - end - - local players = {} - for i = 1, #config.playerPositions do - local pos = config.playerPositions[i].pos - local creature = Tile(pos):getTopCreature() - if not creature or not creature:isPlayer() then - player:sendCancelMessage("You need " .. #config.playerPositions .. " players to challenge " .. config.boss.name .. ".") + if item.itemid == 8911 then + if Game.getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.PlagirathTimer) >= 1 then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need to wait a while, recently someone challenge Plagirath.") return true end - table.insert(players, creature) - end - - for i = 1, #players do - local playerToTeleport = players[i] - local teleportPos = config.playerPositions[i].teleport - local effect = config.playerPositions[i].effect - playerToTeleport:teleportTo(teleportPos) - teleportPos:sendMagicEffect(effect) - end - - Game.createMonster(config.boss.name, config.boss.position) - - addEvent(clearBossRoom, config.timeToDefeat * 1000, config.specPos.from, config.specPos.to, config.exit) - - if item.itemid == 8911 then + local specs, spec = Game.getSpectators(config.centerRoom, false, false, 15, 15, 15, 15) + for i = 1, #specs do + spec = specs[i] + if spec:isPlayer() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting with Plagirath.") + return true + end + end + Game.createMonster("Plagirath", config.BossPosition, true, true) + for y = 31500, 31504 do + local playerTile = Tile(Position(33229, y, 13)):getTopCreature() + if playerTile and playerTile:isPlayer() then + playerTile:getPosition():sendMagicEffect(CONST_ME_POFF) + playerTile:teleportTo(config.newPosition) + playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + playerTile:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.PlagirathTimer, os.time() + 60 * 60 * 24 * 2) + end + end + Game.setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.PlagirathTimer, 1) + addEvent(clearForgotten, 30 * 60 * 1000, Position(33159, 31491, 13), Position(33185, 31513, 13), Position(33319, 32318, 13), Storage.Quest.U10_90.FerumbrasAscension.PlagirathTimer) item:transform(8912) - else + elseif item.itemid == 8912 then item:transform(8911) end return true end -function clearBossRoom(fromPos, toPos, exitPos) - local spectators = Game.getSpectators(fromPos, false, false, 0, 0, 0, 0, toPos) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - spec:teleportTo(exitPos) - exitPos:sendMagicEffect(CONST_ME_TELEPORT) - spec:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You took too long, the battle has ended.") - else - spec:remove() - end - end -end - -function isBossInRoom(fromPos, toPos, bossName) - local monstersRemoved = false - for x = fromPos.x, toPos.x do - for y = fromPos.y, toPos.y do - for z = fromPos.z, toPos.z do - local tile = Tile(Position(x, y, z)) - if tile then - local creature = tile:getTopCreature() - if creature and creature:isMonster() then - creature:remove() - monstersRemoved = true - end - end - end - end - end - return monstersRemoved -end - leverPlagirath:uid(1022) leverPlagirath:register() diff --git a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_ragiaz.lua b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_ragiaz.lua index cbe9d13ba35..e63df82d82d 100644 --- a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_ragiaz.lua +++ b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_ragiaz.lua @@ -1,113 +1,58 @@ local config = { - boss = { - name = "Ragiaz", - position = Position(33481, 32334, 13), + centerRoom = Position(33481, 32334, 13), + BossPosition = Position(33481, 32334, 13), + newPosition = Position(33482, 32339, 13), + deathDragons = { + Position(33476, 32331, 13), + Position(33476, 32340, 13), + Position(33487, 32340, 13), + Position(33488, 32331, 13), }, - - timeToDefeat = 17 * 60, -- 17 minutes in seconds - playerPositions = { - { pos = Position(33456, 32356, 13), teleport = Position(33482, 32339, 13), effect = CONST_ME_TELEPORT }, - { pos = Position(33457, 32356, 13), teleport = Position(33482, 32339, 13), effect = CONST_ME_TELEPORT }, - { pos = Position(33458, 32356, 13), teleport = Position(33482, 32339, 13), effect = CONST_ME_TELEPORT }, - { pos = Position(33459, 32356, 13), teleport = Position(33482, 32339, 13), effect = CONST_ME_TELEPORT }, - { pos = Position(33460, 32356, 13), teleport = Position(33482, 32339, 13), effect = CONST_ME_TELEPORT }, - }, - monsters = { - { name = "Death Dragon", pos = Position(33476, 32331, 13) }, - { name = "Death Dragon", pos = Position(33476, 32340, 13) }, - { name = "Death Dragon", pos = Position(33487, 32340, 13) }, - { name = "Death Dragon", pos = Position(33488, 32331, 13) }, - }, - specPos = { - from = Position(33468, 32319, 13), - to = Position(33495, 32347, 13), - }, - exit = Position(33319, 32318, 13), } local leverRagiaz = Action() function leverRagiaz.onUse(player, item, fromPosition, target, toPosition, isHotkey) - local spectators = Game.getSpectators(config.specPos.from, false, false, 0, 0, 0, 0, config.specPos.to) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - player:say("Someone is already inside the room.", TALKTYPE_MONSTER_SAY) - return true - end - end - - if isBossInRoom(config.specPos.from, config.specPos.to, config.boss.name) then - player:say("The room is being cleared. Please wait a moment.", TALKTYPE_MONSTER_SAY) - return true - end - - local players = {} - for i = 1, #config.playerPositions do - local pos = config.playerPositions[i].pos - local creature = Tile(pos):getTopCreature() - if not creature or not creature:isPlayer() then - player:sendCancelMessage("You need " .. #config.playerPositions .. " players to challenge " .. config.boss.name .. ".") + if item.itemid == 8911 then + if player:getPosition() ~= Position(33456, 32356, 13) then + item:transform(8912) return true end - table.insert(players, creature) - end - - for i = 1, #players do - local playerToTeleport = players[i] - local teleportPos = config.playerPositions[i].teleport - local effect = config.playerPositions[i].effect - playerToTeleport:teleportTo(teleportPos) - teleportPos:sendMagicEffect(effect) end - Game.createMonster(config.boss.name, config.boss.position) - - for i = 1, #config.monsters do - local monsterConfig = config.monsters[i] - Game.createMonster(monsterConfig.name, monsterConfig.pos, true, true) - end - - addEvent(clearBossRoom, config.timeToDefeat * 1000, config.specPos.from, config.specPos.to, config.exit) - if item.itemid == 8911 then - item:transform(8912) - else - item:transform(8911) - end - - return true -end - -function clearBossRoom(fromPos, toPos, exitPos) - local spectators = Game.getSpectators(fromPos, false, false, 0, 0, 0, 0, toPos) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - spec:teleportTo(exitPos) - exitPos:sendMagicEffect(CONST_ME_TELEPORT) - spec:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You took too long, the battle has ended.") - else - spec:remove() - end - end -end - -function isBossInRoom(fromPos, toPos, bossName) - local monstersRemoved = false - for x = fromPos.x, toPos.x do - for y = fromPos.y, toPos.y do - for z = fromPos.z, toPos.z do - local tile = Tile(Position(x, y, z)) - if tile then - local creature = tile:getTopCreature() - if creature and creature:isMonster() then - creature:remove() - monstersRemoved = true - end + local playersTable = {} + if player:doCheckBossRoom("Ragiaz", Position(33472, 32323, 13), Position(33493, 32347, 13)) then + local specs, spec = Game.getSpectators(config.centerRoom, false, false, 15, 15, 15, 15) + for i = 1, #specs do + spec = specs[i] + if spec:isPlayer() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting with Ragiaz.") + return true + end + end + Game.createMonster("Ragiaz", config.BossPosition, true, true) + for d = 1, #config.deathDragons do + Game.createMonster("Death Dragon", config.deathDragons[d], true, true) + end + for x = 33456, 33460 do + local playerTile = Tile(Position(x, 32356, 13)):getTopCreature() + if playerTile and playerTile:isPlayer() then + playerTile:getPosition():sendMagicEffect(CONST_ME_POFF) + playerTile:teleportTo(config.newPosition) + playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + playerTile:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.RagiazTimer, os.time() + 60 * 60 * 2 * 24) + table.insert(playersTable, playerTile:getId()) end end + addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(33472, 32323, 13), Position(33493, 32347, 13), Position(33319, 32318, 13)) + item:transform(8912) end + elseif item.itemid == 8912 then + item:transform(8911) end - return monstersRemoved + + return true end leverRagiaz:uid(1023) diff --git a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_rat_lever.lua b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_rat_lever.lua index 13ef7bb84e2..e9592b71585 100644 --- a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_rat_lever.lua +++ b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_rat_lever.lua @@ -1,101 +1,56 @@ local config = { - boss = { - name = "The Lord of The Lice", - position = Position(33220, 31460, 12), - }, - timeToDefeat = 17 * 60, -- 17 minutes in seconds + centerRoom = Position(33215, 31456, 12), + BossPosition = Position(33220, 31460, 12), playerPositions = { - { pos = Position(33197, 31475, 11), teleport = Position(33215, 31470, 12), effect = CONST_ME_TELEPORT }, - { pos = Position(33198, 31475, 11), teleport = Position(33215, 31470, 12), effect = CONST_ME_TELEPORT }, - { pos = Position(33199, 31475, 11), teleport = Position(33215, 31470, 12), effect = CONST_ME_TELEPORT }, - { pos = Position(33200, 31475, 11), teleport = Position(33215, 31470, 12), effect = CONST_ME_TELEPORT }, - { pos = Position(33201, 31475, 11), teleport = Position(33215, 31470, 12), effect = CONST_ME_TELEPORT }, - }, - specPos = { - from = Position(33187, 31429, 12), - to = Position(33242, 31487, 12), + Position(33197, 31475, 11), + Position(33198, 31475, 11), + Position(33199, 31475, 11), + Position(33200, 31475, 11), + Position(33201, 31475, 11), }, - exit = Position(33319, 32318, 13), + newPosition = Position(33215, 31470, 12), } local ferumbrasAscendantRatLever = Action() function ferumbrasAscendantRatLever.onUse(player, item, fromPosition, target, toPosition, isHotkey) - local spectators = Game.getSpectators(config.specPos.from, false, false, 0, 0, 0, 0, config.specPos.to) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - player:say("Someone is already inside the room.", TALKTYPE_MONSTER_SAY) - return true - end - end - - if isBossInRoom(config.specPos.from, config.specPos.to, config.boss.name) then - player:say("The room is being cleared. Please wait a moment.", TALKTYPE_MONSTER_SAY) - return true - end - - local players = {} - for i = 1, #config.playerPositions do - local pos = config.playerPositions[i].pos - local creature = Tile(pos):getTopCreature() - if not creature or not creature:isPlayer() then - player:sendCancelMessage("You need " .. #config.playerPositions .. " players to challenge " .. config.boss.name .. ".") + if item.itemid == 8911 then + if player:getPosition() ~= Position(33201, 31475, 11) then + item:transform(8912) return true end - table.insert(players, creature) - end - - for i = 1, #players do - local playerToTeleport = players[i] - local teleportPos = config.playerPositions[i].teleport - local effect = config.playerPositions[i].effect - playerToTeleport:teleportTo(teleportPos) - teleportPos:sendMagicEffect(effect) end - Game.createMonster(config.boss.name, config.boss.position) - - addEvent(clearBossRoom, config.timeToDefeat * 1000, config.specPos.from, config.specPos.to, config.exit) - if item.itemid == 8911 then - item:transform(8912) - else - item:transform(8911) - end - - return true -end - -function clearBossRoom(fromPos, toPos, exitPos) - local spectators = Game.getSpectators(fromPos, false, false, 0, 0, 0, 0, toPos) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - spec:teleportTo(exitPos) - exitPos:sendMagicEffect(CONST_ME_TELEPORT) - spec:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You took too long, the battle has ended.") - else - spec:remove() - end - end -end - -function isBossInRoom(fromPos, toPos, bossName) - local bossRemoved = false - for x = fromPos.x, toPos.x do - for y = fromPos.y, toPos.y do - for z = fromPos.z, toPos.z do - local tile = Tile(Position(x, y, z)) - if tile then - local creature = tile:getTopCreature() - if creature and creature:isMonster() and creature:getName() == bossName then - creature:remove() - bossRemoved = true - end + local playersTable = {} + if player:doCheckBossRoom("The Lord of the Lice", Position(33187, 31429, 12), Position(33242, 31487, 12)) then + local specs, spec = Game.getSpectators(config.centerRoom, false, false, 30, 30, 30, 30) + for i = 1, #specs do + spec = specs[i] + if spec:isPlayer() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting with The Lord of The Lice.") + return true + end + end + Game.createMonster("the lord of the lice", config.BossPosition, true, true) + for x = 33197, 33201 do + local playerTile = Tile(Position(x, 31475, 11)):getTopCreature() + if playerTile and playerTile:isPlayer() then + playerTile:getPosition():sendMagicEffect(CONST_ME_POFF) + playerTile:teleportTo(config.newPosition) + playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + playerTile:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheLordOfTheLiceTimer, os.time() + 60 * 60 * 2 * 24) + table.insert(playersTable, playerTile:getId()) end end + addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(33187, 31429, 12), Position(33242, 31487, 12), Position(33319, 32318, 13)) + item:transform(8912) end + elseif item.itemid == 8912 then + item:transform(8911) end - return bossRemoved + + return true end ferumbrasAscendantRatLever:uid(1030) diff --git a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_razzagorn.lua b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_razzagorn.lua index c24ec5d2e37..fe5b4e06ed9 100644 --- a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_razzagorn.lua +++ b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_razzagorn.lua @@ -1,102 +1,50 @@ local config = { - boss = { - name = "Razzagorn", - position = Position(33422, 32467, 14), - }, - timeToDefeat = 17 * 60, -- 17 minutes in seconds - playerPositions = { - { pos = Position(33386, 32455, 14), teleport = Position(33419, 32467, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(33387, 32455, 14), teleport = Position(33419, 32467, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(33388, 32455, 14), teleport = Position(33419, 32467, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(33389, 32455, 14), teleport = Position(33419, 32467, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(33390, 32455, 14), teleport = Position(33419, 32467, 14), effect = CONST_ME_TELEPORT }, - }, - specPos = { - from = Position(33407, 32453, 14), - to = Position(33439, 32481, 14), - }, - exit = Position(33319, 32318, 13), + centerRoom = Position(33422, 32467, 14), + BossPosition = Position(33422, 32467, 14), + newPosition = Position(33419, 32467, 14), } local leverRazzagorn = Action() function leverRazzagorn.onUse(player, item, fromPosition, target, toPosition, isHotkey) - local spectators = Game.getSpectators(config.specPos.from, false, false, 0, 0, 0, 0, config.specPos.to) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - player:say("Someone is already inside the room.", TALKTYPE_MONSTER_SAY) + if item.itemid == 8911 then + if player:getPosition() ~= Position(33386, 32455, 14) then + item:transform(8912) return true end end - - if isBossInRoom(config.specPos.from, config.specPos.to, config.boss.name) then - player:say("The room is being cleared. Please wait a moment.", TALKTYPE_MONSTER_SAY) - return true - end - - local players = {} - for i = 1, #config.playerPositions do - local pos = config.playerPositions[i].pos - local creature = Tile(pos):getTopCreature() - if not creature or not creature:isPlayer() then - player:sendCancelMessage("You need " .. #config.playerPositions .. " players to challenge " .. config.boss.name .. ".") + if item.itemid == 8911 then + if Game.getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.RazzagornTimer) >= 1 then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need to wait a while, recently someone challenge Razzagorn.") return true end - table.insert(players, creature) - end - - for i = 1, #players do - local playerToTeleport = players[i] - local teleportPos = config.playerPositions[i].teleport - local effect = config.playerPositions[i].effect - playerToTeleport:teleportTo(teleportPos) - teleportPos:sendMagicEffect(effect) - end - - Game.createMonster(config.boss.name, config.boss.position) - - addEvent(clearBossRoom, config.timeToDefeat * 1000, config.specPos.from, config.specPos.to, config.exit) - - if item.itemid == 8911 then + local specs, spec = Game.getSpectators(config.centerRoom, false, false, 15, 15, 15, 15) + for i = 1, #specs do + spec = specs[i] + if spec:isPlayer() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting with Razzagorn.") + return true + end + end + Game.createMonster("Razzagorn", config.BossPosition, true, true) + for x = 33386, 33390 do + local playerTile = Tile(Position(x, 32455, 14)):getTopCreature() + if playerTile and playerTile:isPlayer() then + playerTile:getPosition():sendMagicEffect(CONST_ME_POFF) + playerTile:teleportTo(config.newPosition) + playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + playerTile:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.RazzagornTimer, os.time() + 60 * 60 * 2 * 24) + end + end + Game.setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.RazzagornTimer, 1) + addEvent(clearForgotten, 30 * 60 * 1000, Position(33408, 32454, 14), Position(33440, 32480, 14), Position(33319, 32318, 13), Storage.Quest.U10_90.FerumbrasAscension.RazzagornTimer) item:transform(8912) - else + elseif item.itemid == 8912 then item:transform(8911) end return true end -function clearBossRoom(fromPos, toPos, exitPos) - local spectators = Game.getSpectators(fromPos, false, false, 0, 0, 0, 0, toPos) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - spec:teleportTo(exitPos) - exitPos:sendMagicEffect(CONST_ME_TELEPORT) - spec:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You took too long, the battle has ended.") - else - spec:remove() - end - end -end - -function isBossInRoom(fromPos, toPos, bossName) - local monstersRemoved = false - for x = fromPos.x, toPos.x do - for y = fromPos.y, toPos.y do - for z = fromPos.z, toPos.z do - local tile = Tile(Position(x, y, z)) - if tile then - local creature = tile:getTopCreature() - if creature and creature:isMonster() then - creature:remove() - monstersRemoved = true - end - end - end - end - end - return monstersRemoved -end - leverRazzagorn:uid(1024) leverRazzagorn:register() diff --git a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_shulgrax.lua b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_shulgrax.lua index 43f5029a6ac..2b1792a9261 100644 --- a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_shulgrax.lua +++ b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_shulgrax.lua @@ -1,101 +1,55 @@ local config = { - boss = { - name = "Shulgrax", - position = Position(33485, 32786, 13), - }, - timeToDefeat = 17 * 60, -- 17 minutes in seconds + centerRoom = Position(33485, 32786, 13), + BossPosition = Position(33485, 32786, 13), playerPositions = { - { pos = Position(33434, 32785, 13), teleport = Position(33485, 32790, 13), effect = CONST_ME_TELEPORT }, - { pos = Position(33434, 32786, 13), teleport = Position(33485, 32790, 13), effect = CONST_ME_TELEPORT }, - { pos = Position(33434, 32787, 13), teleport = Position(33485, 32790, 13), effect = CONST_ME_TELEPORT }, - { pos = Position(33434, 32788, 13), teleport = Position(33485, 32790, 13), effect = CONST_ME_TELEPORT }, - { pos = Position(33434, 32789, 13), teleport = Position(33485, 32790, 13), effect = CONST_ME_TELEPORT }, - }, - specPos = { - from = Position(33474, 32775, 13), - to = Position(33496, 32798, 13), + Position(33434, 32785, 13), + Position(33434, 32786, 13), + Position(33434, 32787, 13), + Position(33434, 32788, 13), + Position(33434, 32789, 13), }, - exit = Position(33319, 32318, 13), + newPosition = Position(33485, 32790, 13), } local leverShulgrax = Action() function leverShulgrax.onUse(player, item, fromPosition, target, toPosition, isHotkey) - local spectators = Game.getSpectators(config.specPos.from, false, false, 0, 0, 0, 0, config.specPos.to) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - player:say("Someone is already inside the room.", TALKTYPE_MONSTER_SAY) - return true - end - end - - if isBossInRoom(config.specPos.from, config.specPos.to, config.boss.name) then - player:say("The room is being cleared. Please wait a moment.", TALKTYPE_MONSTER_SAY) - return true - end - - local players = {} - for i = 1, #config.playerPositions do - local pos = config.playerPositions[i].pos - local creature = Tile(pos):getTopCreature() - if not creature or not creature:isPlayer() then - player:sendCancelMessage("You need " .. #config.playerPositions .. " players to challenge " .. config.boss.name .. ".") + if item.itemid == 8911 then + if player:getPosition() ~= Position(33434, 32785, 13) then + item:transform(8912) return true end - table.insert(players, creature) - end - - for i = 1, #players do - local playerToTeleport = players[i] - local teleportPos = config.playerPositions[i].teleport - local effect = config.playerPositions[i].effect - playerToTeleport:teleportTo(teleportPos) - teleportPos:sendMagicEffect(effect) end - - Game.createMonster(config.boss.name, config.boss.position) - - addEvent(clearBossRoom, config.timeToDefeat * 1000, config.specPos.from, config.specPos.to, config.exit) - if item.itemid == 8911 then - item:transform(8912) - else - item:transform(8911) - end - - return true -end - -function clearBossRoom(fromPos, toPos, exitPos) - local spectators = Game.getSpectators(fromPos, false, false, 0, 0, 0, 0, toPos) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - spec:teleportTo(exitPos) - exitPos:sendMagicEffect(CONST_ME_TELEPORT) - spec:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You took too long, the battle has ended.") - else - spec:remove() - end - end -end - -function isBossInRoom(fromPos, toPos, bossName) - local monstersRemoved = false - for x = fromPos.x, toPos.x do - for y = fromPos.y, toPos.y do - for z = fromPos.z, toPos.z do - local tile = Tile(Position(x, y, z)) - if tile then - local creature = tile:getTopCreature() - if creature and creature:isMonster() then - creature:remove() - monstersRemoved = true - end + local playersTable = {} + if player:doCheckBossRoom("Shulgrax", Position(33473, 32776, 13), Position(33496, 32798, 13)) then + local specs, spec = Game.getSpectators(config.centerRoom, false, false, 15, 15, 15, 15) + for i = 1, #specs do + spec = specs[i] + if spec:isPlayer() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting with Shulgrax.") + return true + end + end + Game.createMonster("Shulgrax", config.BossPosition, true, true) + for y = 32785, 32789 do + local playerTile = Tile(Position(33434, y, 13)):getTopCreature() + if playerTile and playerTile:isPlayer() then + playerTile:getPosition():sendMagicEffect(CONST_ME_POFF) + playerTile:teleportTo(config.newPosition) + playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + playerTile:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.ShulgraxTimer, os.time() + 60 * 60 * 2 * 24) + table.insert(playersTable, playerTile:getId()) end end + addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(33473, 32776, 13), Position(33496, 32798, 13), Position(33319, 32318, 13)) + item:transform(8912) end + elseif item.itemid == 8912 then + item:transform(8911) end - return monstersRemoved + + return true end leverShulgrax:uid(1028) diff --git a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_tarbaz.lua b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_tarbaz.lua index dfda8c76e8d..7423b8f3820 100644 --- a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_tarbaz.lua +++ b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_tarbaz.lua @@ -1,101 +1,48 @@ local config = { - boss = { - name = "Tarbaz", - position = Position(33459, 32844, 11), - }, - timeToDefeat = 17 * 60, -- 17 minutes in seconds - playerPositions = { - { pos = Position(33418, 32849, 11), teleport = Position(33459, 32848, 11), effect = CONST_ME_TELEPORT }, - { pos = Position(33418, 32850, 11), teleport = Position(33459, 32848, 11), effect = CONST_ME_TELEPORT }, - { pos = Position(33418, 32851, 11), teleport = Position(33459, 32848, 11), effect = CONST_ME_TELEPORT }, - { pos = Position(33418, 32852, 11), teleport = Position(33459, 32848, 11), effect = CONST_ME_TELEPORT }, - { pos = Position(33418, 32853, 11), teleport = Position(33459, 32848, 11), effect = CONST_ME_TELEPORT }, - }, - specPos = { - from = Position(33447, 32832, 11), - to = Position(33473, 32856, 11), - }, - exit = Position(33319, 32318, 13), + centerRoom = Position(33459, 32844, 11), + BossPosition = Position(33459, 32844, 11), + newPosition = Position(33459, 32848, 11), } local leverTarbaz = Action() function leverTarbaz.onUse(player, item, fromPosition, target, toPosition, isHotkey) - local spectators = Game.getSpectators(config.specPos.from, false, false, 0, 0, 0, 0, config.specPos.to) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - player:say("Someone is already inside the room.", TALKTYPE_MONSTER_SAY) - return true - end - end - - if isBossInRoom(config.specPos.from, config.specPos.to, config.boss.name) then - player:say("The room is being cleared. Please wait a moment.", TALKTYPE_MONSTER_SAY) - return true - end - - local players = {} - for i = 1, #config.playerPositions do - local pos = config.playerPositions[i].pos - local creature = Tile(pos):getTopCreature() - if not creature or not creature:isPlayer() then - player:sendCancelMessage("You need " .. #config.playerPositions .. " players to challenge " .. config.boss.name .. ".") + if item.itemid == 8911 then + if player:getPosition() ~= Position(33418, 32849, 11) then + item:transform(8912) return true end - table.insert(players, creature) end - - for i = 1, #players do - local playerToTeleport = players[i] - local teleportPos = config.playerPositions[i].teleport - local effect = config.playerPositions[i].effect - playerToTeleport:teleportTo(teleportPos) - teleportPos:sendMagicEffect(effect) - end - - Game.createMonster(config.boss.name, config.boss.position) - - addEvent(clearBossRoom, config.timeToDefeat * 1000, config.specPos.from, config.specPos.to, config.exit) - if item.itemid == 8911 then - item:transform(8912) - else - item:transform(8911) - end - - return true -end - -function clearBossRoom(fromPos, toPos, exitPos) - local spectators = Game.getSpectators(fromPos, false, false, 0, 0, 0, 0, toPos) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - spec:teleportTo(exitPos) - exitPos:sendMagicEffect(CONST_ME_TELEPORT) - spec:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You took too long, the battle has ended.") - else - spec:remove() - end - end -end - -function isBossInRoom(fromPos, toPos, bossName) - local monstersRemoved = false - for x = fromPos.x, toPos.x do - for y = fromPos.y, toPos.y do - for z = fromPos.z, toPos.z do - local tile = Tile(Position(x, y, z)) - if tile then - local creature = tile:getTopCreature() - if creature and creature:isMonster() then - creature:remove() - monstersRemoved = true - end + local playersTable = {} + if player:doCheckBossRoom("Tarbaz", Position(33446, 32833, 11), Position(33515, 32875, 12)) then + local specs, spec = Game.getSpectators(config.centerRoom, false, false, 15, 15, 15, 15) + for i = 1, #specs do + spec = specs[i] + if spec:isPlayer() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting with Tarbaz.") + return true + end + end + Game.createMonster("Tarbaz", config.BossPosition, true, true) + for y = 32849, 32853 do + local playerTile = Tile(Position(33418, y, 11)):getTopCreature() + if playerTile and playerTile:isPlayer() then + playerTile:getPosition():sendMagicEffect(CONST_ME_POFF) + playerTile:teleportTo(config.newPosition) + playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + playerTile:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TarbazTimer, os.time() + 60 * 60 * 2 * 24) + table.insert(playersTable, playerTile:getId()) end end + addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(33446, 32833, 11), Position(33515, 32875, 12), Position(33319, 32318, 13)) + item:transform(8912) end + elseif item.itemid == 8912 then + item:transform(8911) end - return monstersRemoved + + return true end leverTarbaz:uid(1027) diff --git a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_the_shatterer_lever.lua b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_the_shatterer_lever.lua index 221a4bdc850..9e061a2f4a4 100644 --- a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_the_shatterer_lever.lua +++ b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_the_shatterer_lever.lua @@ -1,112 +1,56 @@ local config = { - boss = { - name = "The Shatterer", - position = Position(33406, 32418, 14), - }, - timeToDefeat = 17 * 60, -- 17 minutes in seconds - playerPositions = { - { pos = Position(33403, 32465, 13), teleport = Position(33398, 32414, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(33404, 32465, 13), teleport = Position(33398, 32414, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(33405, 32465, 13), teleport = Position(33398, 32414, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(33406, 32465, 13), teleport = Position(33398, 32414, 14), effect = CONST_ME_TELEPORT }, - { pos = Position(33407, 32465, 13), teleport = Position(33398, 32414, 14), effect = CONST_ME_TELEPORT }, - }, - specPos = { - from = Position(33377, 32390, 14), - to = Position(33446, 32447, 14), - }, - exit = Position(33319, 32318, 13), centerRoom = Position(33406, 32418, 14), - storage = { - lever = Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever, - timer = Storage.Quest.U10_90.FerumbrasAscension.TheShattererTimer, + BossPosition = Position(33406, 32418, 14), + playerPositions = { + Position(33403, 32465, 13), + Position(33404, 32465, 13), + Position(33405, 32465, 13), + Position(33406, 32465, 13), + Position(33407, 32465, 13), }, + newPosition = Position(33398, 32414, 14), } local ferumbrasAscendantTheShattererLever = Action() function ferumbrasAscendantTheShattererLever.onUse(player, item, fromPosition, target, toPosition, isHotkey) - local spectators = Game.getSpectators(config.centerRoom, false, false, 30, 30, 30, 30) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting with The Shatterer.") + if item.itemid == 8912 then + if player:getPosition() ~= Position(33403, 32465, 13) then + item:transform(8911) return true end end - - if isBossInRoom(config.specPos.from, config.specPos.to, config.boss.name) then - player:say("The room is being cleared. Please wait a moment.", TALKTYPE_MONSTER_SAY) - return true - end - - local players = {} - for i = 1, #config.playerPositions do - local pos = config.playerPositions[i].pos - local creature = Tile(pos):getTopCreature() - if not creature or not creature:isPlayer() then - player:sendCancelMessage("You need " .. #config.playerPositions .. " players to challenge " .. config.boss.name .. ".") - return true + if item.itemid == 8912 then + local playersTable = {} + if player:doCheckBossRoom("The Shatterer", Position(33377, 32390, 14), Position(33446, 32447, 14)) then + local specs, spec = Game.getSpectators(config.centerRoom, false, false, 30, 30, 30, 30) + for i = 1, #specs do + spec = specs[i] + if spec:isPlayer() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting with The Shatterer.") + return true + end + end + Game.createMonster("The Shatterer", config.BossPosition, true, true) + for x = 33403, 33407 do + local playerTile = Tile(Position(x, 32465, 13)):getTopCreature() + if playerTile and playerTile:isPlayer() then + playerTile:getPosition():sendMagicEffect(CONST_ME_POFF) + playerTile:teleportTo(config.newPosition) + playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + playerTile:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererTimer, os.time() + 60 * 60 * 2 * 24) + table.insert(playersTable, playerTile:getId()) + end + end + addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(33377, 32390, 14), Position(33446, 32447, 14), Position(33319, 32318, 13)) + item:transform(8911) end - table.insert(players, creature) - end - - for i = 1, #players do - local playerToTeleport = players[i] - local teleportPos = config.playerPositions[i].teleport - local effect = config.playerPositions[i].effect - playerToTeleport:teleportTo(teleportPos) - teleportPos:sendMagicEffect(effect) - end - - Game.createMonster(config.boss.name, config.boss.position, true, true) - - for _, participant in pairs(players) do - participant:setStorageValue(config.storage.lever, 0) - participant:setStorageValue(config.storage.timer, 1) - end - - addEvent(clearBossRoom, config.timeToDefeat * 1000, config.specPos.from, config.specPos.to, config.exit, config.storage.timer) - - if item.itemid == 8911 then + elseif item.itemid == 8911 then item:transform(8912) - else - item:transform(8911) end return true end -function clearBossRoom(fromPos, toPos, exitPos) - local spectators = Game.getSpectators(fromPos, false, false, 0, 0, 0, 0, toPos) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - spec:teleportTo(exitPos) - exitPos:sendMagicEffect(CONST_ME_TELEPORT) - spec:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You took too long, the battle has ended.") - else - spec:remove() - end - end -end - -function isBossInRoom(fromPos, toPos, bossName) - local bossRemoved = false - for x = fromPos.x, toPos.x do - for y = fromPos.y, toPos.y do - for z = fromPos.z, toPos.z do - local tile = Tile(Position(x, y, z)) - if tile then - local creature = tile:getTopCreature() - if creature and creature:isMonster() and creature:getName() == bossName then - creature:remove() - bossRemoved = true - end - end - end - end - end - return bossRemoved -end - ferumbrasAscendantTheShattererLever:uid(1029) ferumbrasAscendantTheShattererLever:register() diff --git a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_the_shatterer_levers.lua b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_the_shatterer_levers.lua index b7e7916ebdf..3189947919b 100644 --- a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_the_shatterer_levers.lua +++ b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_the_shatterer_levers.lua @@ -39,11 +39,13 @@ local chains = { } local ferumbrasAscendantTheShattererLevers = Action() + function ferumbrasAscendantTheShattererLevers.onUse(player, item, fromPosition, target, toPosition, isHotkey) if player:getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShatterer) >= 1 then player:sendCancelMessage("You cannot use this lever again.") return true end + if item.itemid == 8911 then item:transform(8912) if item:getPosition() == Position(33385, 32410, 14) then @@ -55,10 +57,10 @@ function ferumbrasAscendantTheShattererLevers.onUse(player, item, fromPosition, Game.createMonster("Fury", { x = player:getPosition().x + math.random(-3, 3), y = player:getPosition().y + math.random(-3, 3), z = player:getPosition().z }) end end - player:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever, math.max(player:getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever), 0) + 1) - if player:getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever) >= 4 then + Game.setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever, Game.getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever) + 1) + if Game.getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever) >= 4 then local boss = Tile(Position(33406, 32418, 14)):getTopCreature() - if boss and boss:getName():lower() == "the shatterer" then + if boss:getName():lower() == "the shatterer" then boss:teleportTo(Position(33400, 32415, 14)) boss:getPosition():sendMagicEffect(CONST_ME_TELEPORT) return true @@ -73,10 +75,10 @@ function ferumbrasAscendantTheShattererLevers.onUse(player, item, fromPosition, Game.createMonster("Destroyer", { x = player:getPosition().x + math.random(-3, 3), y = player:getPosition().y + math.random(-3, 3), z = player:getPosition().z }) end end - player:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever, math.max(player:getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever), 0) + 1) - if player:getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever) >= 4 then + Game.setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever, Game.getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever) + 1) + if Game.getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever) >= 4 then local boss = Tile(Position(33406, 32418, 14)):getTopCreature() - if boss and boss:getName():lower() == "the shatterer" then + if boss:getName():lower() == "the shatterer" then boss:teleportTo(Position(33400, 32415, 14)) boss:getPosition():sendMagicEffect(CONST_ME_TELEPORT) return true @@ -90,10 +92,10 @@ function ferumbrasAscendantTheShattererLevers.onUse(player, item, fromPosition, bla:remove() end end - player:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever, math.max(player:getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever), 0) + 1) - if player:getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever) >= 4 then + Game.setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever, Game.getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever) + 1) + if Game.getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever) >= 4 then local boss = Tile(Position(33406, 32418, 14)):getTopCreature() - if boss and boss:getName():lower() == "the shatterer" then + if boss:getName():lower() == "the shatterer" then boss:teleportTo(Position(33400, 32415, 14)) boss:getPosition():sendMagicEffect(CONST_ME_TELEPORT) return true @@ -107,10 +109,10 @@ function ferumbrasAscendantTheShattererLevers.onUse(player, item, fromPosition, bla:remove() end end - player:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever, math.max(player:getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever), 0) + 1) - if player:getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever) >= 4 then + Game.setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever, Game.getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever) + 1) + if Game.getStorageValue(Storage.Quest.U10_90.FerumbrasAscension.TheShattererLever) >= 4 then local boss = Tile(Position(33406, 32418, 14)):getTopCreature() - if boss and boss:getName():lower() == "the shatterer" then + if boss:getName():lower() == "the shatterer" then boss:teleportTo(Position(33400, 32415, 14)) boss:getPosition():sendMagicEffect(CONST_ME_TELEPORT) return true @@ -122,6 +124,7 @@ function ferumbrasAscendantTheShattererLevers.onUse(player, item, fromPosition, player:sendCancelMessage("Sorry, not possible.") return true end + return true end diff --git a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_zamulosh.lua b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_zamulosh.lua index 74e7740bc9f..fc3e34a8219 100644 --- a/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_zamulosh.lua +++ b/data-otservbr-global/scripts/quests/ferumbras_ascension/actions_zamulosh.lua @@ -1,18 +1,8 @@ local config = { - boss = { - name = "Zamulosh", - position = Position(33643, 32756, 11), - }, - timeToDefeat = 17 * 60, -- 17 minutes in seconds - playerPositions = { - { pos = Position(33680, 32741, 11), teleport = Position(33644, 32760, 11), effect = CONST_ME_TELEPORT }, - }, - specPos = { - from = Position(33632, 32747, 11), - to = Position(33654, 32765, 11), - }, - exit = Position(33319, 32318, 13), - summons = { + centerRoom = Position(33643, 32756, 11), + BossPosition = Position(33643, 32756, 11), + newPosition = Position(33644, 32760, 11), + zamuloshSummons = { Position(33642, 32756, 11), Position(33642, 32756, 11), Position(33642, 32756, 11), @@ -25,91 +15,45 @@ local config = { local leverZamulosh = Action() function leverZamulosh.onUse(player, item, fromPosition, target, toPosition, isHotkey) - local spectators = Game.getSpectators(config.specPos.from, false, false, 0, 0, 0, 0, config.specPos.to) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - player:say("Someone is already inside the room.", TALKTYPE_MONSTER_SAY) - return true - end - end - - if isBossInRoom(config.specPos.from, config.specPos.to, config.boss.name) then - player:say("The room is being cleared. Please wait a moment.", TALKTYPE_MONSTER_SAY) - return true - end - - local players = {} - for i = 1, #config.playerPositions do - local pos = config.playerPositions[i].pos - local creature = Tile(pos):getTopCreature() - if not creature or not creature:isPlayer() then - player:sendCancelMessage("You need " .. #config.playerPositions .. " players to challenge " .. config.boss.name .. ".") + if item.itemid == 8911 then + if player:getPosition() ~= Position(33680, 32741, 11) then + item:transform(8912) return true end - table.insert(players, creature) - end - - for i = 1, #players do - local playerToTeleport = players[i] - local teleportPos = config.playerPositions[i].teleport - local effect = config.playerPositions[i].effect - playerToTeleport:teleportTo(teleportPos) - teleportPos:sendMagicEffect(effect) end - - Game.createMonster(config.boss.name, config.boss.position) - - local zamuloshIndex = math.random(#config.summons) - - for i, summonPos in ipairs(config.summons) do - if i == zamuloshIndex then - Game.createMonster(config.boss.name, summonPos) - else - Game.createMonster("Zamulosh3", summonPos) - end - end - - addEvent(clearBossRoom, config.timeToDefeat * 1000, config.specPos.from, config.specPos.to, config.exit) - if item.itemid == 8911 then - item:transform(8912) - else - item:transform(8911) - end - - return true -end - -function clearBossRoom(fromPos, toPos, exitPos) - local spectators = Game.getSpectators(fromPos, false, false, 0, 0, 0, 0, toPos) - for _, spec in pairs(spectators) do - if spec:isPlayer() then - spec:teleportTo(exitPos) - exitPos:sendMagicEffect(CONST_ME_TELEPORT) - spec:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You took too long, the battle has ended.") - else - spec:remove() - end - end -end - -function isBossInRoom(fromPos, toPos, bossName) - local monstersRemoved = false - for x = fromPos.x, toPos.x do - for y = fromPos.y, toPos.y do - for z = fromPos.z, toPos.z do - local tile = Tile(Position(x, y, z)) - if tile then - local creature = tile:getTopCreature() - if creature and creature:isMonster() then - creature:remove() - monstersRemoved = true - end + local playersTable = {} + if player:doCheckBossRoom("Zamulosh", Position(33634, 32749, 11), Position(33654, 32765, 11)) then + local specs, spec = Game.getSpectators(config.centerRoom, false, false, 15, 15, 15, 15) + for i = 1, #specs do + spec = specs[i] + if spec:isPlayer() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting with Zamulosh.") + return true + end + end + Game.createMonster("Zamulosh", config.BossPosition, true, true) + for d = 1, #config.zamuloshSummons do + Game.createMonster("Zamulosh3", config.zamuloshSummons[d], true, true) + end + for y = 32741, 32745 do + local playerTile = Tile(Position(33680, y, 11)):getTopCreature() + if playerTile and playerTile:isPlayer() then + playerTile:getPosition():sendMagicEffect(CONST_ME_POFF) + playerTile:teleportTo(config.newPosition) + playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + playerTile:setStorageValue(Storage.Quest.U10_90.FerumbrasAscension.ZamuloshTimer, os.time() + 60 * 60 * 2 * 24) + table.insert(playersTable, playerTile:getId()) end end + addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(33634, 32749, 11), Position(33654, 32765, 11), Position(33319, 32318, 13)) + item:transform(8912) end + elseif item.itemid == 8912 then + item:transform(8911) end - return monstersRemoved + + return true end leverZamulosh:uid(1026) diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_bird_cage.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_bird_cage.lua index 36206829675..53d507e6b73 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_bird_cage.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_bird_cage.lua @@ -15,10 +15,10 @@ function forgottenKnowledgeBird.onUse(player, item, fromPosition, target, toPosi end player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You open the cage and let the cave parrot roam free!") item:transform(23812) - if player:getStorageValue(Storage.ForgottenKnowledge.BirdCounter) < 0 then - player:setStorageValue(Storage.ForgottenKnowledge.BirdCounter, 0) + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.BirdCounter) < 0 then + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.BirdCounter, 0) end - player:setStorageValue(Storage.ForgottenKnowledge.BirdCounter, player:getStorageValue(Storage.ForgottenKnowledge.BirdCounter) + 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.BirdCounter, player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.BirdCounter) + 1) end player:getPosition():sendMagicEffect(CONST_ME_HEARTS) return true diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_dragonking_zyrtarch.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_dragonking_zyrtarch.lua new file mode 100644 index 00000000000..426177864b2 --- /dev/null +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_dragonking_zyrtarch.lua @@ -0,0 +1,52 @@ +local config = { + bossPosition = Position(33357, 31182, 10), + newPosition = Position(33359, 31186, 10), + soulPosition = Position(33359, 31182, 12), +} + +local monsters = { + { position = Position(33352, 31187, 10) }, + { position = Position(33363, 31187, 10) }, + { position = Position(33353, 31176, 10) }, + { position = Position(33363, 31176, 10) }, +} + +local leverZyrtarch = Action() + +function leverZyrtarch.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if item.itemid == 8911 then + if player:getPosition() ~= Position(33391, 31178, 10) then + item:transform(8912) + return true + end + end + if item.itemid == 8911 then + local playersTable = {} + if player:doCheckBossRoom("Dragonking Zyrtarch", Position(33348, 31172, 10), Position(33368, 31190, 12)) then + for d = 1, #monsters do + Game.createMonster("soulcatcher", monsters[d].position, true, true) + end + Game.createMonster("dragonking zyrtarch", config.bossPosition, true, true) + Game.createMonster("soul of dragonking zyrtarch", config.soulPosition, true, true) + for y = 31178, 31182 do + local playerTile = Tile(Position(33391, y, 10)):getTopCreature() + if playerTile and playerTile:isPlayer() then + playerTile:getPosition():sendMagicEffect(CONST_ME_POFF) + playerTile:teleportTo(config.newPosition) + playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + playerTile:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.DragonkingTimer, os.time() + 20 * 60 * 60) + table.insert(playersTable, playerTile:getId()) + end + end + addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(33348, 31172, 10), Position(33368, 31190, 12), Position(33407, 31172, 10)) + item:transform(8912) + end + elseif item.itemid == 8912 then + item:transform(8911) + end + + return true +end + +leverZyrtarch:position(Position(33391, 31177, 10)) +leverZyrtarch:register() diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_fount.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_fount.lua index 09501839c9e..1b7b0a4a3e9 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_fount.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_fount.lua @@ -1,11 +1,11 @@ local forgottenKnowledgeFount = Action() function forgottenKnowledgeFount.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.ForgottenKnowledge.Phial) >= 1 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.Phial) >= 1 then return false end player:addItem(23810) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "A phial of fresh pond water. It looks crystal clear and sparkles a little.") - player:setStorageValue(Storage.ForgottenKnowledge.Phial, 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.Phial, 1) return true end diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_frozen_horror.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_frozen_horror.lua new file mode 100644 index 00000000000..fc7e5049b6a --- /dev/null +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_frozen_horror.lua @@ -0,0 +1,59 @@ +local config = { + centerRoom = Position(32269, 31091, 14), + BossPosition = Position(32269, 31091, 14), + newPosition = Position(32271, 31097, 14), +} + +local monsters = { + { monster = "icicle", pos = Position(32266, 31084, 14) }, + { monster = "icicle", pos = Position(32272, 31084, 14) }, + { monster = "dragon egg", pos = Position(32269, 31084, 14) }, + { monster = "melting frozen horror", pos = Position(32267, 31071, 14) }, +} + +local leverMeltingFrozenHorror = Action() + +function leverMeltingFrozenHorror.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if item.itemid == 8911 then + if player:getPosition() ~= Position(32302, 31088, 14) then + item:transform(8912) + return true + end + end + if item.itemid == 8911 then + if Game.getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.HorrorTimer) >= 1 then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need to wait a while, recently someone challenge Frozen Horror.") + return true + end + local specs, spec = Game.getSpectators(config.centerRoom, false, false, 15, 15, 15, 15) + for i = 1, #specs do + spec = specs[i] + if spec:isPlayer() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting with Frozen Horror.") + return true + end + end + for n = 1, #monsters do + Game.createMonster(monsters[n].monster, monsters[n].pos, true, true) + end + Game.createMonster("solid frozen horror", config.BossPosition, true, true) + for y = 31088, 31092 do + local playerTile = Tile(Position(32302, y, 14)):getTopCreature() + if playerTile and playerTile:isPlayer() then + playerTile:getPosition():sendMagicEffect(CONST_ME_POFF) + playerTile:teleportTo(config.newPosition) + playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + playerTile:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.HorrorTimer, os.stime() + 20 * 60 * 60) + end + end + Game.setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.HorrorTimer, 1) + addEvent(clearForgotten, 30 * 60 * 1000, Position(32264, 31070, 14), Position(32284, 31104, 14), Position(32319, 31091, 14), Storage.Quest.U11_02.ForgottenKnowledge.HorrorTimer) + item:transform(8912) + elseif item.itemid == 8912 then + item:transform(8911) + end + return true +end + +leverMeltingFrozenHorror:position(Position(32302, 31087, 14)) +leverMeltingFrozenHorror:register() diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_girl_picture.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_girl_picture.lua index b065f3e3de2..c7995ab4a94 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_girl_picture.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_girl_picture.lua @@ -1,9 +1,9 @@ local forgottenKnowledgeGirl = Action() function forgottenKnowledgeGirl.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.ForgottenKnowledge.GirlPicture) >= 1 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.GirlPicture) >= 1 then return false end - player:setStorageValue(Storage.ForgottenKnowledge.GirlPicture, 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.GirlPicture, 1) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Seems that an old silver key appears in the drower.") item:remove() return true diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_lady_tenebris.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_lady_tenebris.lua new file mode 100644 index 00000000000..7f784c4516f --- /dev/null +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_lady_tenebris.lua @@ -0,0 +1,43 @@ +local config = { + centerRoom = Position(32912, 31599, 14), + bossPosition = Position(32912, 31599, 14), + newPosition = Position(32911, 31603, 14), +} + +local leverLadyTenebris = Action() + +function leverLadyTenebris.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if item.itemid == 8911 then + if player:getPosition() ~= Position(32902, 31623, 14) then + return true + end + end + if item.itemid == 8911 then + local playersTable = {} + if player:doCheckBossRoom("Lady Tenebris", Position(32902, 31589, 14), Position(32924, 31610, 14)) then + for d = 1, 6 do + Game.createMonster("shadow tentacle", Position(math.random(32909, 32914), math.random(31596, 31601), 14), true, true) + end + Game.createMonster("lady tenebris", config.bossPosition, true, true) + for y = 31623, 31627 do + local playerTile = Tile(Position(32902, y, 14)):getTopCreature() + if playerTile and playerTile:isPlayer() then + playerTile:getPosition():sendMagicEffect(CONST_ME_POFF) + playerTile:teleportTo(config.newPosition) + playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + playerTile:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.LadyTenebrisTimer, os.time() + 20 * 60 * 60) + table.insert(playersTable, playerTile:getId()) + end + end + addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(32902, 31589, 14), Position(32924, 31610, 14), Position(32919, 31639, 14)) + item:transform(8912) + end + elseif item.itemid == 8912 then + item:transform(8911) + end + + return true +end + +leverLadyTenebris:position(Position(32902, 31622, 14)) +leverLadyTenebris:register() diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_lloyd.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_lloyd.lua new file mode 100644 index 00000000000..2a6a48b84c6 --- /dev/null +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_lloyd.lua @@ -0,0 +1,51 @@ +local config = { + centerRoom = Position(32799, 32832, 14), + bossPosition = Position(32799, 32827, 14), + newPosition = Position(32800, 32831, 14), +} + +local monsters = { + { cosmic = "cosmic energy prism a", pos = Position(32801, 32827, 14) }, + { cosmic = "cosmic energy prism b", pos = Position(32798, 32827, 14) }, + { cosmic = "cosmic energy prism c", pos = Position(32803, 32826, 14) }, + { cosmic = "cosmic energy prism d", pos = Position(32796, 32826, 14) }, +} + +local leverLloyd = Action() + +function leverLloyd.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if item.itemid == 8911 then + if player:getPosition() ~= Position(32759, 32868, 14) then + item:transform(8912) + return true + end + end + if item.itemid == 8911 then + local playersTable = {} + if player:doCheckBossRoom(player:getId(), "Lloyd", Position(32785, 32814, 14), Position(32812, 32838, 14)) then + for n = 1, #monsters do + Game.createMonster(monsters[n].cosmic, monsters[n].pos, true, true) + end + Game.createMonster("lloyd", config.bossPosition, true, true) + for y = 32868, 32872 do + local playerTile = Tile(Position(32759, y, 14)):getTopCreature() + if playerTile and playerTile:isPlayer() then + playerTile:getPosition():sendMagicEffect(CONST_ME_POFF) + playerTile:teleportTo(config.newPosition) + playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + playerTile:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.LloydTimer, os.time() + 20 * 60 * 60) + table.insert(playersTable, playerTile:getId()) + end + end + addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(32785, 32814, 14), Position(32812, 32838, 14), Position(32815, 32873, 13)) + item:transform(8912) + end + elseif item.itemid == 8912 then + item:transform(8911) + end + + return true +end + +leverLloyd:position(Position(32759, 32867, 14)) +leverLloyd:register() diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_old_desk.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_old_desk.lua index 3bbc0175e5d..a33e1aa4cfd 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_old_desk.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_old_desk.lua @@ -1,22 +1,22 @@ local forgottenKnowledgeOldDesk = Action() function forgottenKnowledgeOldDesk.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.ForgottenKnowledge.SilverKey) >= 1 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.SilverKey) >= 1 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You already get an old silver key.") return true end - if player:getStorageValue(Storage.ForgottenKnowledge.GirlPicture) >= 1 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.GirlPicture) >= 1 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You find an old silver key inside of the drower.") player:addItem(23733, true, true) - player:setStorageValue(Storage.ForgottenKnowledge.SilverKey, 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.SilverKey, 1) return true end - if player:getStorageValue(Storage.ForgottenKnowledge.OldDesk) >= 1 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.OldDesk) >= 1 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You already get an old note.") return true end player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "As you open the drower a ghostly apparition shortly appears hovering over the desk. You find an old note inside of the drower.") player:addItem(23731, true, true) - player:setStorageValue(Storage.ForgottenKnowledge.OldDesk, 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.OldDesk, 1) return true end diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_plant.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_plant.lua index d0306018ce5..65715560a67 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_plant.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_plant.lua @@ -403,7 +403,7 @@ local function revertTree() tree:getPosition():sendMagicEffect(CONST_ME_POFF) tree:transform(6189) end - Game.setStorageValue(GlobalStorage.ForgottenKnowledge.ActiveTree, 0) + Game.setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.ActiveTree, 0) for v = 1, #dry do Game.createItem(dry[v].itemid, 1, dry[v].position) local pos = Position(dry[v].position) @@ -429,16 +429,16 @@ function forgottenKnowledgePlant.onUse(player, item, fromPosition, target, toPos target:getPosition():sendMagicEffect(CONST_ME_SMALLPLANTS) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The birch grows as you pour the sparkling water from the phial over it.") addEvent(revertItem, 10 * 60 * 1000, target:getPosition(), 26483, 26482) - if player:getStorageValue(Storage.ForgottenKnowledge.PlantCounter) < 0 then - player:setStorageValue(Storage.ForgottenKnowledge.PlantCounter, 0) + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.PlantCounter) < 0 then + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.PlantCounter, 0) end - player:setStorageValue(Storage.ForgottenKnowledge.PlantCounter, player:getStorageValue(Storage.ForgottenKnowledge.PlantCounter) + 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.PlantCounter, player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.PlantCounter) + 1) elseif target.itemid == 6180 and target:getPosition() == Position(32737, 32116, 10) then - if Game.getStorageValue(GlobalStorage.ForgottenKnowledge.ActiveTree) >= 1 then + if Game.getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.ActiveTree) >= 1 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You cannot use the phial while the trees still have life and colour.") return true end - if player:getStorageValue(Storage.ForgottenKnowledge.PlantCounter) < 5 or player:getStorageValue(Storage.ForgottenKnowledge.BirdCounter) < 3 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.PlantCounter) < 5 or player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.BirdCounter) < 3 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Seems that you don't make grow enough trees or not free the parrots.") return true end @@ -477,7 +477,7 @@ function forgottenKnowledgePlant.onUse(player, item, fromPosition, target, toPos end end end - Game.setStorageValue(GlobalStorage.ForgottenKnowledge.ActiveTree, 1) + Game.setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.ActiveTree, 1) addEvent(revertTree, 1 * 60 * 1000) Game.createItem(11108, 1, Position(32736, 32117, 10)) local teleport = Game.createItem(775, 1, Position(32736, 32117, 10)) diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_secret_wall.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_secret_wall.lua index 674e9fd5b42..0d9e3a3acff 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_secret_wall.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_secret_wall.lua @@ -7,7 +7,7 @@ function forgottenKnowledgeSecret.onUse(player, item, fromPosition, target, toPo if not player:getItemById(23738, true) then return false end - if player:getStorageValue(Storage.ForgottenKnowledge.SilverKey) < 1 or not player:getItemById(23733, true) then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.SilverKey) < 1 or not player:getItemById(23733, true) then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You don't have the fitting key.") return true end diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_servants_mechanism.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_servants_mechanism.lua index eb2d9588662..8c2ca9f5bf1 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_servants_mechanism.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_servants_mechanism.lua @@ -1,19 +1,19 @@ local config = { - [26663] = { storage = GlobalStorage.ForgottenKnowledge.MechanismDiamond, counter = GlobalStorage.ForgottenKnowledge.DiamondServant, msg = "5 diamond entities are consuming too much raw energy for the cosmic chamber to awaken, it will be put to rest again in 10 minutes." }, - [26664] = { storage = GlobalStorage.ForgottenKnowledge.MechanismGolden, counter = GlobalStorage.ForgottenKnowledge.GoldenServant, msg = "5 golden entities are consuming too much raw energy for the cosmic chamber to awaken, it will be put to rest again in 10 minutes." }, + [26663] = { storage = Storage.Quest.U11_02.ForgottenKnowledge.MechanismDiamond, counter = Storage.Quest.U11_02.ForgottenKnowledge.DiamondServant, msg = "5 diamond entities are consuming too much raw energy for the cosmic chamber to awaken, it will be put to rest again in 10 minutes." }, + [26664] = { storage = Storage.Quest.U11_02.ForgottenKnowledge.MechanismGolden, counter = Storage.Quest.U11_02.ForgottenKnowledge.GoldenServant, msg = "5 golden entities are consuming too much raw energy for the cosmic chamber to awaken, it will be put to rest again in 10 minutes." }, } local function clearGolems() local specs, spec = Game.getSpectators(Position(32815, 32874, 13), false, false, 63, 63, 63, 63) for i = 1, #specs do spec = specs[i] - if Game.getStorageValue(GlobalStorage.ForgottenKnowledge.MechanismDiamond) < 1 then + if Game.getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.MechanismDiamond) < 1 then if spec:isMonster() and spec:getName():lower() == "diamond servant replica" then spec:getPosition():sendMagicEffect(CONST_ME_POFF) spec:remove() end end - if Game.getStorageValue(GlobalStorage.ForgottenKnowledge.MechanismGolden) < 1 then + if Game.getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.MechanismGolden) < 1 then if spec:isMonster() and spec:getName():lower() == "golden servant replica" then spec:getPosition():sendMagicEffect(CONST_ME_POFF) spec:remove() diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_last_lore_keeper.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_last_lore_keeper.lua new file mode 100644 index 00000000000..aa704ba6435 --- /dev/null +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_last_lore_keeper.lua @@ -0,0 +1,51 @@ +local config = { + { newPosition = Position(31985, 32851, 14) }, + { pos = Position(31986, 32840, 14), monster = "a shielded astral glyph" }, + { pos = Position(31975, 32856, 15), monster = "bound astral power" }, + { pos = Position(31987, 32839, 14), monster = "the astral source" }, + { pos = Position(31986, 32823, 15), monster = "the distorted astral source" }, + { pos = Position(31989, 32823, 15), monster = "an astral glyph" }, +} + +local leverLoreKeeper = Action() + +function leverLoreKeeper.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if item.itemid == 8911 then + if player:getPosition() ~= Position(32019, 32844, 14) then + item:transform(8912) + return true + end + end + if item.itemid == 8911 then + local playersTable = {} + if player:doCheckBossRoom("The Last Lorekeeper", Position(31968, 32821, 14), Position(32004, 32865, 15)) then + for x = 32018, 32020 do + for y = 32844, 32848 do + local playerTile = Tile(Position(x, y, 14)):getTopCreature() + if playerTile and playerTile:isPlayer() then + playerTile:getPosition():sendMagicEffect(CONST_ME_POFF) + playerTile:teleportTo(config[1].newPosition) + playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + playerTile:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.LastLoreTimer, os.time() + 60 * 60 * 14 * 24) + table.insert(playersTable, playerTile:getId()) + end + end + end + for b = 2, #config do + Game.createMonster(config[b].monster, config[b].pos, true, true) + end + Game.setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AstralPowerCounter, 1) + Game.setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AstralGlyph, 0) + player:say("The Astral Glyph begins to draw upon bound astral power to expel you from the room!", TALKTYPE_MONSTER_SAY) + addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(31968, 32821, 14), Position(32004, 32865, 15), Position(32035, 32859, 14)) + item:transform(8912) + end + elseif item.itemid == 8912 then + item:transform(8911) + end + + return true +end + +leverLoreKeeper:position(Position(32019, 32843, 14)) +leverLoreKeeper:register() diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_thorn_knight.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_thorn_knight.lua new file mode 100644 index 00000000000..16b22300ea1 --- /dev/null +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_thorn_knight.lua @@ -0,0 +1,43 @@ +local config = { + centerRoom = Position(32624, 32880, 14), + bossPosition = Position(32624, 32880, 14), + newPosition = Position(32624, 32886, 14), +} + +local leverThornKnight = Action() + +function leverThornKnight.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if item.itemid == 8911 then + if player:getPosition() ~= Position(32657, 32877, 14) then + item:transform(8912) + return true + end + end + if item.itemid == 8911 then + local playersTable = {} + if player:doCheckBossRoom("Thorn Knight", Position(32613, 32869, 14), Position(32636, 32892, 14)) then + for d = 1, 6 do + Game.createMonster("possessed tree", Position(math.random(32619, 32629), math.random(32877, 32884), 14), true, true) + end + Game.createMonster("mounted thorn knight", config.bossPosition, true, true) + for y = 32877, 32881 do + local playerTile = Tile(Position(32657, y, 14)):getTopCreature() + if playerTile and playerTile:isPlayer() then + playerTile:getPosition():sendMagicEffect(CONST_ME_POFF) + playerTile:teleportTo(config.newPosition) + playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + playerTile:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.ThornKnightTimer, os.stime() + 20 * 60 * 60) + table.insert(playersTable, playerTile:getId()) + end + end + addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(32613, 32869, 14), Position(32636, 32892, 14), Position(32678, 32888, 14)) + item:transform(8912) + end + elseif item.itemid == 8912 then + item:transform(8911) + end + return true +end + +leverThornKnight:position(Position(32657, 32876, 14)) +leverThornKnight:register() diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_time_guardian.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_time_guardian.lua new file mode 100644 index 00000000000..75dcd0da028 --- /dev/null +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_the_time_guardian.lua @@ -0,0 +1,47 @@ +local config = { + centerRoom = Position(32977, 31662, 14), + newPosition = Position(32977, 31667, 14), +} + +local bosses = { + { bossPosition = Position(32977, 31662, 14), bossName = "The Time Guardian" }, + { bossPosition = Position(32975, 31664, 13), bossName = "The Freezing Time Guardian" }, + { bossPosition = Position(32980, 31664, 13), bossName = "The Blazing Time Guardian" }, +} + +local leverTimeGuardian = Action() + +function leverTimeGuardian.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if item.itemid == 8911 then + if player:getPosition() ~= Position(33010, 31660, 14) then + item:transform(8912) + return true + end + end + if item.itemid == 8911 then + local playersTable = {} + if player:doCheckBossRoom("The Time Guardian", Position(32967, 31654, 13), Position(32989, 31677, 14)) then + for q = 1, #bosses do + Game.createMonster(bosses[q].bossName, bosses[q].bossPosition, true, true) + end + for y = 31660, 31664 do + local playerTile = Tile(Position(33010, y, 14)):getTopCreature() + if playerTile and playerTile:isPlayer() then + playerTile:getPosition():sendMagicEffect(CONST_ME_POFF) + playerTile:teleportTo(config.newPosition) + playerTile:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + playerTile:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.TimeGuardianTimer, os.time() + 20 * 60 * 60) + table.insert(playersTable, playerTile:getId()) + end + end + addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, Position(32967, 31654, 13), Position(32989, 31677, 14), Position(32870, 32724, 14)) + item:transform(8912) + end + elseif item.itemid == 8912 then + item:transform(8911) + end + return true +end + +leverTimeGuardian:position(Position(33010, 31659, 14)) +leverTimeGuardian:register() diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_time_machine.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_time_machine.lua index bc92b403adf..52188508827 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_time_machine.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/actions_time_machine.lua @@ -1,4 +1,5 @@ local timeMachine = Action() + function timeMachine.onUse(player, item, fromPosition, target, toPosition, isHotkey) if player:getPosition() == Position(32870, 32723, 15) then player:teleportTo(Position(32870, 32724, 14)) diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_astral_source.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_astral_source.lua index 9d243f4be65..d7d0d869513 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_astral_source.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_astral_source.lua @@ -2,7 +2,7 @@ local astralSource = CreatureEvent("AstralSource") function astralSource.onThink(creature) local hp = (creature:getHealth() / creature:getMaxHealth()) * 100 local health, difference, glyph, pos = 0, 0, Tile(Position(31989, 32823, 15)):getTopCreature(), creature:getPosition() - if hp < 5.5 and Game.getStorageValue(GlobalStorage.ForgottenKnowledge.AstralGlyph) >= 1 then + if hp < 5.5 and Game.getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AstralGlyph) >= 1 then sourcePos = creature:getPosition() creature:say("Your damage distorted the source and prevents the Glyph to draw on its power.", TALKTYPE_MONSTER_SAY) creature:remove() @@ -23,7 +23,7 @@ function astralSource.onThink(creature) glyph:say("Without the power of the source the Glyph loses its protection!", TALKTYPE_MONSTER_SAY) end end - elseif Game.getStorageValue(GlobalStorage.ForgottenKnowledge.AstralGlyph) < 1 then + elseif Game.getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AstralGlyph) < 1 then creature:addHealth(10000, false) end end diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_bosses_kill.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_bosses_kill.lua index 6bd27821be6..00ced55f16d 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_bosses_kill.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_bosses_kill.lua @@ -1,19 +1,20 @@ local bosses = { -- bosses - ["lady tenebris"] = { storage = Storage.ForgottenKnowledge.LadyTenebrisKilled }, - ["the enraged thorn knight"] = { storage = Storage.ForgottenKnowledge.ThornKnightKilled }, - ["lloyd"] = { storage = Storage.ForgottenKnowledge.LloydKilled }, - ["soul of dragonking zyrtarch"] = { storage = Storage.ForgottenKnowledge.DragonkingKilled }, - ["melting frozen horror"] = { storage = Storage.ForgottenKnowledge.HorrorKilled }, - ["the time guardian"] = { storage = Storage.ForgottenKnowledge.TimeGuardianKilled }, - ["the blazing time guardian"] = { storage = Storage.ForgottenKnowledge.TimeGuardianKilled }, - ["the freezing time guardian"] = { storage = Storage.ForgottenKnowledge.TimeGuardianKilled }, - ["the last lore keeper"] = { storage = Storage.ForgottenKnowledge.LastLoreKilled }, + ["lady tenebris"] = { storage = Storage.Quest.U11_02.ForgottenKnowledge.LadyTenebrisKilled }, + ["the enraged thorn knight"] = { storage = Storage.Quest.U11_02.ForgottenKnowledge.ThornKnightKilled }, + ["lloyd"] = { storage = Storage.Quest.U11_02.ForgottenKnowledge.LloydKilled }, + ["soul of dragonking zyrtarch"] = { storage = Storage.Quest.U11_02.ForgottenKnowledge.DragonkingKilled }, + ["melting frozen horror"] = { storage = Storage.Quest.U11_02.ForgottenKnowledge.HorrorKilled }, + ["the time guardian"] = { storage = Storage.Quest.U11_02.ForgottenKnowledge.TimeGuardianKilled }, + ["the blazing time guardian"] = { storage = Storage.Quest.U11_02.ForgottenKnowledge.TimeGuardianKilled }, + ["the freezing time guardian"] = { storage = Storage.Quest.U11_02.ForgottenKnowledge.TimeGuardianKilled }, + ["the last lore keeper"] = { storage = Storage.Quest.U11_02.ForgottenKnowledge.LastLoreKilled }, -- IA interactions ["an astral glyph"] = {}, } local bossesForgottenKill = CreatureEvent("ForgottenKnowledgeBossDeath") + function bossesForgottenKill.onDeath(creature) local bossConfig = bosses[creature:getName():lower()] if not bossConfig then @@ -22,10 +23,14 @@ function bossesForgottenKill.onDeath(creature) onDeathForDamagingPlayers(creature, function(creature, player) if bossConfig.storage then - player:setStorageValue(bossConfig.storage, os.time() + 20 * 3600) + if creature:getName():lower() == "the last lore keeper" then + player:setStorageValue(bossConfig.storage, os.time() + (13 * 24 * 3600) + (20 * 3600)) + else + player:setStorageValue(bossConfig.storage, os.time() + 20 * 3600) + end elseif creature:getName():lower() == "the enraged thorn knight" then - player:setStorageValue(Storage.ForgottenKnowledge.PlantCounter, 0) - player:setStorageValue(Storage.ForgottenKnowledge.BirdCounter, 0) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.PlantCounter, 0) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.BirdCounter, 0) end end) diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_bound_astral_power.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_bound_astral_power.lua index 3709d5c559a..be6286b7e88 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_bound_astral_power.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_bound_astral_power.lua @@ -7,9 +7,9 @@ local positions = { local astralPower = CreatureEvent("BoundAstralPowerDeath") function astralPower.onDeath(creature, _corpse, _lastHitKiller, mostDamageKiller) - Game.setStorageValue(GlobalStorage.ForgottenKnowledge.AstralPowerCounter, Game.getStorageValue(GlobalStorage.ForgottenKnowledge.AstralPowerCounter) + 1) - if Game.getStorageValue(GlobalStorage.ForgottenKnowledge.AstralPowerCounter) >= 4 then - Game.setStorageValue(GlobalStorage.ForgottenKnowledge.AstralPowerCounter, 1) + Game.setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AstralPowerCounter, Game.getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AstralPowerCounter) + 1) + if Game.getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AstralPowerCounter) >= 4 then + Game.setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AstralPowerCounter, 1) end local msg = "The destruction of the power source gained you more time until the glyph is powered up!" @@ -21,8 +21,8 @@ function astralPower.onDeath(creature, _corpse, _lastHitKiller, mostDamageKiller if player:getPosition():getDistance(positions[i].pos) < 7 then creature:say(msg, TALKTYPE_MONSTER_SAY, false, nil, positions[i].pos) Game.createMonster("bound astral power", positions[i].nextPos, true, true) - Game.setStorageValue(GlobalStorage.ForgottenKnowledge.AstralGlyph, 1) - addEvent(Game.setStorageValue, 1 * 60 * 1000, GlobalStorage.ForgottenKnowledge.AstralGlyph, 0) + Game.setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AstralGlyph, 1) + addEvent(Game.setStorageValue, 1 * 60 * 1000, Storage.Quest.U11_02.ForgottenKnowledge.AstralGlyph, 0) end end return true diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_distorted_source.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_distorted_source.lua index 334c7bc8cf6..e8f7c799dbb 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_distorted_source.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_distorted_source.lua @@ -3,7 +3,7 @@ function distortedSource.onThink(creature) local health, difference, glyph, pos = 0, 0, Tile(Position(31989, 32823, 15)):getTopCreature(), creature:getPosition() if creature:getHealth() <= 40000 then creature:addHealth(10000, false) - elseif creature:getHealth() >= 55000 or Game.getStorageValue(GlobalStorage.ForgottenKnowledge.AstralGlyph) < 1 then + elseif creature:getHealth() >= 55000 or Game.getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AstralGlyph) < 1 then local spectators = Game.getSpectators(Position(31986, 32847, 14), false, false, 12, 12, 12, 12) for i = 1, #spectators do local spec = spectators[i] @@ -20,7 +20,7 @@ function distortedSource.onThink(creature) end end - if Game.getStorageValue(GlobalStorage.ForgottenKnowledge.AstralGlyph) >= 1 then + if Game.getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AstralGlyph) >= 1 then local spectators = Game.getSpectators(Position(31986, 32847, 14), false, false, 12, 12, 12, 12) for i = 1, #spectators do local spec2 = spectators[i] diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_energy_prism_death.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_energy_prism_death.lua index 140362d92b3..5b4213feaed 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_energy_prism_death.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_energy_prism_death.lua @@ -1,6 +1,6 @@ local energyPrismDeath = CreatureEvent("EnergyPrismDeath") function energyPrismDeath.onDeath(creature) - stopEvent(Storage.ForgottenKnowledge.LloydEvent) + stopEvent(Storage.Quest.U11_02.ForgottenKnowledge.LloydEvent) local tile = Tile(Position(32799, 32826, 14)) if not tile then return false diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_lloyd_preparedeath.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_lloyd_preparedeath.lua index e00624aa53d..5804a21283c 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_lloyd_preparedeath.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_lloyd_preparedeath.lua @@ -49,7 +49,7 @@ function lloydPrepareDeath.onPrepareDeath(creature, lastHitKiller, mostDamageKil creature:getPosition():sendMagicEffect(CONST_ME_TELEPORT) creature:addHealth(300000, true) creature:say("The cosmic energies in the chamber refocus on Lloyd.", TALKTYPE_MONSTER_SAY) - Storage.ForgottenKnowledge.LloydEvent = addEvent(revertLloyd, 10 * 1000, prismCount) + Storage.Quest.U11_02.ForgottenKnowledge.LloydEvent = addEvent(revertLloyd, 10 * 1000, prismCount) end return true end diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_replica_servants.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_replica_servants.lua index acb25740216..d2dbe4a1b4e 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_replica_servants.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/creaturescripts_replica_servants.lua @@ -1,11 +1,11 @@ local servants = { ["golden servant replica"] = { - storage = GlobalStorage.ForgottenKnowledge.GoldenServant, - playerStorage = Storage.ForgottenKnowledge.GoldenServantCounter, + storage = Storage.Quest.U11_02.ForgottenKnowledge.GoldenServant, + playerStorage = Storage.Quest.U11_02.ForgottenKnowledge.GoldenServantCounter, }, ["diamond servant replica"] = { - storage = GlobalStorage.ForgottenKnowledge.DiamondServant, - playerStorage = Storage.ForgottenKnowledge.DiamondServantCounter, + storage = Storage.Quest.U11_02.ForgottenKnowledge.DiamondServant, + playerStorage = Storage.Quest.U11_02.ForgottenKnowledge.DiamondServantCounter, }, } local replicaServant = CreatureEvent("ReplicaServantDeath") @@ -18,7 +18,7 @@ function replicaServant.onDeath(creature, _corpse, _lastHitKiller, mostDamageKil Game.setStorageValue(bossConfig.storage, 0) end Game.setStorageValue(bossConfig.storage, Game.getStorageValue(bossConfig.storage) + 1) - if Game.getStorageValue(GlobalStorage.ForgottenKnowledge.GoldenServant) >= 5 and Game.getStorageValue(GlobalStorage.ForgottenKnowledge.DiamondServant) >= 5 then + if Game.getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.GoldenServant) >= 5 and Game.getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.DiamondServant) >= 5 then if not Tile(Position(32815, 32870, 13)):getItemById(10840) then local teleport = Game.createItem(10840, 1, Position(32815, 32870, 13)) if teleport then diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_challenger.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_challenger.lua index d204cd119a9..edc8ffbc61e 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_challenger.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_challenger.lua @@ -45,7 +45,7 @@ function challenger.onStepIn(creature, item, position, fromPosition) end if player:canFightBoss(teleport.boss) then if item.uid == 24882 then - if player:getStorageValue(Storage.ForgottenKnowledge.BabyDragon) < 1 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.BabyDragon) < 1 then player:teleportTo(teleport.backPos) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You not have permission to use this teleport!") player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_entrance_teleport.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_entrance_teleport.lua index 5b327430c4b..64847b1560a 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_entrance_teleport.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_entrance_teleport.lua @@ -1,72 +1,72 @@ local destination = { [25047] = { newPos = Position(32807, 31657, 8), - storage = Storage.ForgottenKnowledge.AccessDeath, + storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessDeath, effect = CONST_ME_MORTAREA, }, [25048] = { newPos = Position(32325, 32089, 7), - storage = Storage.ForgottenKnowledge.AccessDeath, + storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessDeath, effect = CONST_ME_MORTAREA, }, [25051] = { newPos = Position(32786, 32820, 13), - storage = Storage.ForgottenKnowledge.AccessViolet, + storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessViolet, effect = CONST_ME_PURPLEENERGY, }, [25052] = { newPos = Position(32328, 32089, 7), - storage = Storage.ForgottenKnowledge.AccessViolet, + storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessViolet, effect = CONST_ME_PURPLEENERGY, }, [25049] = { newPos = Position(32637, 32256, 7), - storage = Storage.ForgottenKnowledge.AccessEarth, + storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessEarth, effect = CONST_ME_SMALLPLANTS, }, [25050] = { newPos = Position(32331, 32089, 7), - storage = Storage.ForgottenKnowledge.AccessEarth, + storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessEarth, effect = CONST_ME_SMALLPLANTS, }, [25053] = { newPos = Position(33341, 31168, 7), - storage = Storage.ForgottenKnowledge.AccessFire, + storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessFire, effect = CONST_ME_FIREAREA, }, [25054] = { newPos = Position(32334, 32089, 7), - storage = Storage.ForgottenKnowledge.AccessFire, + storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessFire, effect = CONST_ME_FIREAREA, }, [25057] = { newPos = Position(32207, 31036, 10), - storage = Storage.ForgottenKnowledge.AccessIce, + storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessIce, effect = CONST_ME_ICEATTACK, }, [25058] = { newPos = Position(32337, 32089, 7), - storage = Storage.ForgottenKnowledge.AccessIce, + storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessIce, effect = CONST_ME_ICEATTACK, }, [25055] = { newPos = Position(32780, 32686, 14), - storage = Storage.ForgottenKnowledge.AccessGolden, + storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessGolden, effect = CONST_ME_YELLOWENERGY, }, [25056] = { newPos = Position(32340, 32089, 7), - storage = Storage.ForgottenKnowledge.AccessGolden, + storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessGolden, effect = CONST_ME_YELLOWENERGY, }, [10840] = { newPos = Position(32907, 32848, 13), - storage = Storage.ForgottenKnowledge.AccessLast, + storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessLast, effect = CONST_ME_ENERGYHIT, }, [10842] = { newPos = Position(32332, 32092, 7), - storage = Storage.ForgottenKnowledge.AccessLast, + storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessLast, effect = CONST_ME_ENERGYHIT, }, } @@ -84,16 +84,16 @@ function entranceTeleport.onStepIn(creature, item, position, fromPosition) return end if item.itemid == 10840 then - if player:getStorageValue(Storage.ForgottenKnowledge.AccessLast) < 1 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AccessLast) < 1 then if - player:getStorageValue(Storage.ForgottenKnowledge.LadyTenebrisKilled) >= 1 - and player:getStorageValue(Storage.ForgottenKnowledge.LloydKilled) >= 1 - and player:getStorageValue(Storage.ForgottenKnowledge.ThornKnightKilled) >= 1 - and player:getStorageValue(Storage.ForgottenKnowledge.DragonkingKilled) >= 1 - and player:getStorageValue(Storage.ForgottenKnowledge.HorrorKilled) >= 1 - and player:getStorageValue(Storage.ForgottenKnowledge.TimeGuardianKilled) >= 1 + player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.LadyTenebrisKilled) >= 1 + and player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.LloydKilled) >= 1 + and player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.ThornKnightKilled) >= 1 + and player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.DragonkingKilled) >= 1 + and player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.HorrorKilled) >= 1 + and player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.TimeGuardianKilled) >= 1 then - player:setStorageValue(Storage.ForgottenKnowledge.AccessLast, 1) + player:setStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.AccessLast, 1) end end end diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_fount.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_fount.lua index e1a11083da7..c38b3b56cc7 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_fount.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_fount.lua @@ -5,7 +5,7 @@ function fount.onStepIn(creature, item, position, fromPosition) if not player then return end - if player:getStorageValue(Storage.ForgottenKnowledge.Phial) >= 1 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.Phial) >= 1 then player:teleportTo(Position(32722, 32242, 8)) player:getPosition():sendMagicEffect(CONST_ME_WATERSPLASH) return true diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_ice_teleport.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_ice_teleport.lua index b855169c234..7854fa63f4f 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_ice_teleport.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_ice_teleport.lua @@ -1,7 +1,7 @@ local destination = { [26667] = { position = Position(32273, 31053, 13), - storage = Storage.ForgottenKnowledge.AccessMachine, + storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessMachine, }, } diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_lava_teleport.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_lava_teleport.lua index 1e352d3d7ea..005b6546644 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_lava_teleport.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_lava_teleport.lua @@ -1,7 +1,7 @@ local destination = { [26668] = { position = Position(33411, 31082, 10), - storage = Storage.ForgottenKnowledge.AccessLavaTeleport, + storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessLavaTeleport, }, } diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_servant_teleport.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_servant_teleport.lua index 42921e1712f..bba0c98bfa8 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_servant_teleport.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_servant_teleport.lua @@ -13,7 +13,7 @@ function servantTeleport.onStepIn(creature, item, position, fromPosition) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have to wait to challenge this enemy again!") return true end - if player:getStorageValue(Storage.ForgottenKnowledge.GoldenServantCounter) >= 5 and player:getStorageValue(Storage.ForgottenKnowledge.DiamondServantCounter) >= 5 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.GoldenServantCounter) >= 5 and player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.DiamondServantCounter) >= 5 then player:teleportTo(Position(32760, 32876, 14)) player:getPosition():sendMagicEffect(CONST_ME_ENERGYHIT) return true diff --git a/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_teleport_tree.lua b/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_teleport_tree.lua index ea3bf54a854..d1135f65fa0 100644 --- a/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_teleport_tree.lua +++ b/data-otservbr-global/scripts/quests/forgotten_knowledge/movements_teleport_tree.lua @@ -6,7 +6,7 @@ function teleportTree.onStepIn(creature, item, position, fromPosition) return end - if player:getStorageValue(Storage.ForgottenKnowledge.PlantCounter) < 5 or player:getStorageValue(Storage.ForgottenKnowledge.BirdCounter) < 3 then + if player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.PlantCounter) < 5 or player:getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.BirdCounter) < 3 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You don't help in anything to enter here") player:teleportTo(Position(32737, 32117, 10)) position:sendMagicEffect(CONST_ME_TELEPORT) diff --git a/data-otservbr-global/scripts/quests/roshamuul_quest/actions_mixture.lua b/data-otservbr-global/scripts/quests/roshamuul_quest/actions_mixture.lua index 9ba1ecf88cb..cb2e30d97c8 100644 --- a/data-otservbr-global/scripts/quests/roshamuul_quest/actions_mixture.lua +++ b/data-otservbr-global/scripts/quests/roshamuul_quest/actions_mixture.lua @@ -4,6 +4,7 @@ local buckets = { } local lowerRoshamuulMixtune = Action() + function lowerRoshamuulMixtune.onUse(player, item, fromPosition, target, toPosition, isHotkey) if (target == nil) or not target:isItem() then return false diff --git a/data-otservbr-global/scripts/quests/the_djinn_war_quest/action_water_fountain.lua b/data-otservbr-global/scripts/quests/the_djinn_war_quest/action_water_fountain.lua index 0f8ae9f8ac5..9586aae2d80 100644 --- a/data-otservbr-global/scripts/quests/the_djinn_war_quest/action_water_fountain.lua +++ b/data-otservbr-global/scripts/quests/the_djinn_war_quest/action_water_fountain.lua @@ -11,5 +11,5 @@ function action_water_fountain.onUse(player, item, fromPosition, target, toPosit return true end -action_water_fountain:aid(5390) +action_water_fountain:aid(12105) action_water_fountain:register() diff --git a/data-otservbr-global/scripts/quests/the_first_dragon/actions_lair_entrance.lua b/data-otservbr-global/scripts/quests/the_first_dragon/actions_lair_entrance.lua index 2c03f4d86e3..c2666382616 100644 --- a/data-otservbr-global/scripts/quests/the_first_dragon/actions_lair_entrance.lua +++ b/data-otservbr-global/scripts/quests/the_first_dragon/actions_lair_entrance.lua @@ -1,7 +1,7 @@ local lairEntrance = Action() function lairEntrance.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.FirstDragon.AccessCave) <= 3 then + if player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.AccessCave) <= 3 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are not worthy to enter in The First Dragon's Lair yet.") return true end diff --git a/data-otservbr-global/scripts/quests/the_first_dragon/actions_lever.lua b/data-otservbr-global/scripts/quests/the_first_dragon/actions_lever.lua index 2e794aa7fa8..698ada382b2 100644 --- a/data-otservbr-global/scripts/quests/the_first_dragon/actions_lever.lua +++ b/data-otservbr-global/scripts/quests/the_first_dragon/actions_lever.lua @@ -3,7 +3,7 @@ local lever = Action() local config = { centerRoom = { x = 33616, y = 31022, z = 14 }, range = 10, - storage = Storage.FirstDragon.FirstDragonTimer, + storage = Storage.Quest.U11_02.TheFirstDragon.FirstDragonTimer, monsterPosition = { { position = Position(33574, 31013, 14) }, { position = Position(33592, 31013, 14) }, @@ -58,23 +58,63 @@ local config = { { fromPosition = Position(33583, 30997, 14) }, { fromPosition = Position(33584, 30997, 14) }, }, + clearArea = { + from = Position(33566, 31006, 14), + to = Position(33626, 31032, 14), + }, } +local function clearMonstersInArea(fromPos, toPos) + for z = fromPos.z, toPos.z do + for y = fromPos.y, toPos.y do + for x = fromPos.x, toPos.x do + local tile = Tile(Position(x, y, z)) + if tile then + local creature = tile:getTopCreature() + if creature and creature:isMonster() then + creature:remove() + end + end + end + end + end +end + +local function isRoomOccupied(fromPos, toPos) + for z = fromPos.z, toPos.z do + for y = fromPos.y, toPos.y do + for x = fromPos.x, toPos.x do + local tile = Tile(Position(x, y, z)) + if tile then + local creature = tile:getTopCreature() + if creature and creature:isPlayer() then + return true + end + end + end + end + end + return false +end + function lever.onUse(player, item, fromPosition, target, toPosition, isHotkey) if item.itemid == 8911 then for i = 1, #config.playerPositions do local creature = Tile(config.playerPositions[i]):getTopCreature() if not creature then item:transform(8912) - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need 5 players to fight with this boss.") + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need 15 players to fight with this boss.") return true end end end + if item.itemid == 8911 then - if roomIsOccupied(config.centerRoom, false, config.range, config.range) then - player:say("Someone is fighting against the boss! You need wait awhile.", TALKTYPE_MONSTER_SAY) + if isRoomOccupied(config.clearArea.from, config.clearArea.to) then + player:say("Someone is fighting against the boss! You need to wait awhile.", TALKTYPE_MONSTER_SAY) return true + else + clearMonstersInArea(config.clearArea.from, config.clearArea.to) end for d = 1, 5 do @@ -83,6 +123,7 @@ function lever.onUse(player, item, fromPosition, target, toPosition, isHotkey) for b = 1, #config.monsterPosition do Game.createMonster("fallen challenger", config.monsterPosition[b].position, true, true) end + for i = 1, #config.playerPositions do local creature = Tile(config.playerPositions[i]):getTopCreature() if creature then @@ -110,10 +151,10 @@ function lever.onUse(player, item, fromPosition, target, toPosition, isHotkey) end creature:getPosition():sendMagicEffect(CONST_ME_TELEPORT) creature:setStorageValue(config.storage, os.time() + 20 * 3600) - creature:setStorageValue(Storage.FirstDragon.SomewhatBeatable, 0) + creature:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.SomewhatBeatable, 0) end end - -- One hour for clean the room + addEvent(clearRoom, 60 * 60 * 1000, Position(33583, 31022, 14), 50, 50, config.storage) Game.createMonster("spirit of fertility", Position(33625, 31021, 14), true, true) item:transform(8912) diff --git a/data-otservbr-global/scripts/quests/the_first_dragon/actions_rewards.lua b/data-otservbr-global/scripts/quests/the_first_dragon/actions_rewards.lua index 2d1c8ac2a90..bbef9b99c1b 100644 --- a/data-otservbr-global/scripts/quests/the_first_dragon/actions_rewards.lua +++ b/data-otservbr-global/scripts/quests/the_first_dragon/actions_rewards.lua @@ -6,13 +6,13 @@ local bpItems = { { name = "gold token", count = 3 }, { name = "blue gem", count = 1 }, { name = "yellow gem", count = 1 }, - { id = 3039, count = 1 }, -- red gem + { name = "red gem", count = 1 }, { name = "demon horn", count = 2 }, { name = "slime heart", count = 2 }, { name = "energy vein", count = 2 }, { name = "petrified scream", count = 2 }, { name = "brimstone shell", count = 2 }, - { name = "deepling wart", count = 2 }, + { name = "deepling warts", count = 2 }, { name = "wyrm scale", count = 2 }, { name = "hellspawn tail", count = 2 }, } @@ -33,36 +33,44 @@ local chests = { } local finalReward = Action() + function finalReward.onUse(player, item, fromPosition, target, toPosition, isHotkey) local setting = chests[item.uid] if not setting then return true end - if item.uid == 14021 and player:getStorageValue(Storage.FirstDragon.RewardFeather) < os.time() then + + if player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.Feathers) ~= 2 then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are not eligible to claim these rewards yet.") + return true + end + + if item.uid == 14021 and player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.RewardMask) < os.time() then player:addItem(setting.name, setting.count, true) - player:setStorageValue(Storage.FirstDragon.RewardFeather, os.time() + 24 * 3600) + player:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.RewardMask, os.time() + 60 * 60 * 24 * 5) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You found " .. setting.count .. " " .. setting.name .. ".") - elseif item.uid == 14022 and player:getStorageValue(Storage.FirstDragon.RewardBackpack) < os.time() then + elseif item.uid == 14022 and player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.RewardBackpack) < os.time() then local bp = Game.createItem("Backpack", 1) if bp then for i = 1, #bpItems do if bpItems[i].id then - bp:addItem(bpItems[i].id, count) + bp:addItem(bpItems[i].id, bpItems[i].count) else - bp:addItem(bpItems[i].name, count) + bp:addItem(bpItems[i].name, bpItems[i].count) end end bp:moveTo(player) end player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You found a backpack.") - player:setStorageValue(Storage.FirstDragon.RewardBackpack, os.time() + 60 * 60 * 365 * 24) - elseif item.uid == 14023 and player:getStorageValue(Storage.FirstDragon.RewardMask) < os.time() then + player:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.RewardBackpack, os.time() + 60 * 60 * 24 * 365) + elseif item.uid == 14023 and player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.RewardFeather) < os.time() then player:addItem(setting.name, setting.count, true) - player:setStorageValue(Storage.FirstDragon.RewardMask, os.time() + 60 * 60 * 5 * 24) + player:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.RewardFeather, os.time() + 24 * 3600) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You found " .. setting.count .. " " .. setting.name .. ".") else - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The " .. getItemName(setting.itemId) .. " is empty.") + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The chest is empty.") end + return true end diff --git a/data-otservbr-global/scripts/quests/the_first_dragon/actions_sacrifice_items.lua b/data-otservbr-global/scripts/quests/the_first_dragon/actions_sacrifice_items.lua index 19144117c54..0a9cd03ccaf 100644 --- a/data-otservbr-global/scripts/quests/the_first_dragon/actions_sacrifice_items.lua +++ b/data-otservbr-global/scripts/quests/the_first_dragon/actions_sacrifice_items.lua @@ -1,8 +1,8 @@ local config = { - [24939] = { storage = Storage.FirstDragon.Scale }, - [24940] = { storage = Storage.FirstDragon.Tooth }, - [24941] = { storage = Storage.FirstDragon.Horn }, - [24942] = { storage = Storage.FirstDragon.Bones }, + [24939] = { storage = Storage.Quest.U11_02.TheFirstDragon.Scale }, + [24940] = { storage = Storage.Quest.U11_02.TheFirstDragon.Tooth }, + [24941] = { storage = Storage.Quest.U11_02.TheFirstDragon.Horn }, + [24942] = { storage = Storage.Quest.U11_02.TheFirstDragon.Bones }, } local sacrificeItems = Action() @@ -18,20 +18,20 @@ function sacrificeItems.onUse(player, item, fromPosition, target, toPosition, is return true end - if player:getStorageValue(Storage.FirstDragon.AccessCave) >= 4 then + if player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.AccessCave) >= 4 then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You're plunging " .. item:getName() .. " into the lava. You are now worthy to enter The First Dragon's Lair. Touch the lava pool again.") return true end - if player:getStorageValue(Storage.FirstDragon.AccessCave) < 0 then - player:setStorageValue(Storage.FirstDragon.AccessCave, 0) + if player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.AccessCave) < 0 then + player:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.AccessCave, 0) end local targetPosition = Position(33047, 32712, 3) if toPosition == targetPosition then local targetId = Tile(targetPosition):getItemById(25160) if targetId then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You're plunging " .. item:getName() .. " into the lava.") - player:setStorageValue(Storage.FirstDragon.AccessCave, player:getStorageValue(Storage.FirstDragon.AccessCave) + 1) + player:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.AccessCave, player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.AccessCave) + 1) player:setStorageValue(setting.storage, 1) item:remove(1) return true diff --git a/data-otservbr-global/scripts/quests/the_first_dragon/actions_treasure_chest.lua b/data-otservbr-global/scripts/quests/the_first_dragon/actions_treasure_chest.lua index d40aec690d2..edbcba3184f 100644 --- a/data-otservbr-global/scripts/quests/the_first_dragon/actions_treasure_chest.lua +++ b/data-otservbr-global/scripts/quests/the_first_dragon/actions_treasure_chest.lua @@ -84,26 +84,30 @@ local UniqueTable = { } local treasureChest = Action() + function treasureChest.onUse(player, item, fromPosition, target, toPosition, isHotkey) local setting = UniqueTable[item.uid] if not setting then return false end - if player:getStorageValue(item.uid) >= 1 then - player:sendTextMessage(string.format(MESSAGE_EVENT_ADVANCE, "The %s is empty.", item:getName())) + local storageValue = player:getStorageValue(item.uid) + if storageValue > 0 then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The " .. item:getName() .. " is empty.") return true end - if player:getStorageValue(Storage.FirstDragon.ChestCounter) >= 19 then + if player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.ChestCounter) >= 19 then player:addAchievement("Treasure Hunter") player:addItem(setting.name or setting.itemId, setting.count, true) player:setStorageValue(item.uid, 1) - player:setStorageValue(Storage.FirstDragon.ChestCounter, player:getStorageValue(Storage.FirstDragon.ChestCounter) + 1) + player:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.ChestCounter, player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.ChestCounter) + 1) return true end + player:setStorageValue(item.uid, 1) - player:setStorageValue(Storage.FirstDragon.ChestCounter, player:getStorageValue(Storage.FirstDragon.ChestCounter) + 1) + player:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.ChestCounter, player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.ChestCounter) + 1) + if setting.name then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You found " .. setting.count .. " " .. setting.name .. ".") player:addItem(setting.name, setting.count, true) diff --git a/data-otservbr-global/scripts/quests/the_first_dragon/creaturescripts_death_first_dragon.lua b/data-otservbr-global/scripts/quests/the_first_dragon/creaturescripts_death_first_dragon.lua index 90a2451066c..c347f907e09 100644 --- a/data-otservbr-global/scripts/quests/the_first_dragon/creaturescripts_death_first_dragon.lua +++ b/data-otservbr-global/scripts/quests/the_first_dragon/creaturescripts_death_first_dragon.lua @@ -25,7 +25,9 @@ function deathFirstDragon.onDeath(creature, corpse, lasthitkiller, mostdamagekil if spec:isPlayer() then spec:teleportTo(Position(33617, 31020, 13)) spec:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - spec:setStorageValue(Storage.FirstDragon.Feathers, 1) + if spec:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.Feathers) < 1 then + spec:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.Feathers, 1) + end end end return true diff --git a/data-otservbr-global/scripts/quests/the_first_dragon/creaturescripts_death_somewhat_beatable.lua b/data-otservbr-global/scripts/quests/the_first_dragon/creaturescripts_death_somewhat_beatable.lua index be0cab0cd7b..13308f03d97 100644 --- a/data-otservbr-global/scripts/quests/the_first_dragon/creaturescripts_death_somewhat_beatable.lua +++ b/data-otservbr-global/scripts/quests/the_first_dragon/creaturescripts_death_somewhat_beatable.lua @@ -6,15 +6,15 @@ function deathSomewhatBeatable.onDeath(creature, target) local spec = spectators[i] if spec:isPlayer() then if creature:getName():lower() == "somewhat beatable" then - if spec:getStorageValue(Storage.FirstDragon.SomewhatBeatable) < 5 then - spec:setStorageValue(Storage.FirstDragon.SomewhatBeatable, spec:getStorageValue(Storage.FirstDragon.SomewhatBeatable) + 1) + if spec:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.SomewhatBeatable) < 5 then + spec:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.SomewhatBeatable, spec:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.SomewhatBeatable) + 1) end end - if spec:getStorageValue(Storage.FirstDragon.SomewhatBeatable) == 5 then + if spec:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.SomewhatBeatable) == 5 then for b = 1, 6 do Game.createMonster("dragon essence", Position(math.random(33609, 33624), math.random(31017, 31028), 14), true, true) end - spec:setStorageValue(Storage.FirstDragon.SomewhatBeatable, 0) + spec:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.SomewhatBeatable, 0) end end end diff --git a/data-otservbr-global/scripts/quests/the_first_dragon/creaturescripts_kill_dragon.lua b/data-otservbr-global/scripts/quests/the_first_dragon/creaturescripts_kill_dragon.lua index 55045c65e9d..af6bef08e29 100644 --- a/data-otservbr-global/scripts/quests/the_first_dragon/creaturescripts_kill_dragon.lua +++ b/data-otservbr-global/scripts/quests/the_first_dragon/creaturescripts_kill_dragon.lua @@ -2,9 +2,9 @@ local killDragon = CreatureEvent("TheFirstDragonDragonTaskDeath") function killDragon.onDeath(creature, _corpse, _lastHitKiller, mostDamageKiller) onDeathForParty(creature, mostDamageKiller, function(creature, player) - local storage = player:getStorageValue(Storage.FirstDragon.DragonCounter) + local storage = player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.DragonCounter) if storage >= 0 and storage < 200 then - player:setStorageValue(Storage.FirstDragon.DragonCounter, player:getStorageValue(Storage.FirstDragon.DragonCounter) + 1) + player:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.DragonCounter, player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.DragonCounter) + 1) end end) return true diff --git a/data-otservbr-global/scripts/quests/the_first_dragon/movements_entrance_teleport.lua b/data-otservbr-global/scripts/quests/the_first_dragon/movements_entrance_teleport.lua index 9eb046b74f7..8543eff38c5 100644 --- a/data-otservbr-global/scripts/quests/the_first_dragon/movements_entrance_teleport.lua +++ b/data-otservbr-global/scripts/quests/the_first_dragon/movements_entrance_teleport.lua @@ -1,7 +1,7 @@ local UniqueTable = { -- Tazhadur entrance [35001] = { - storage = Storage.FirstDragon.DragonCounter, + storage = Storage.Quest.U11_02.TheFirstDragon.DragonCounter, value = 200, range = 10, newPos = { x = 32015, y = 32466, z = 8 }, @@ -10,8 +10,8 @@ local UniqueTable = { }, -- Kalyassa entrance [35002] = { - storage = Storage.FirstDragon.ChestCounter, - value = 5, + storage = Storage.Quest.U11_02.TheFirstDragon.ChestCounter, + value = 4, range = 10, newPos = { x = 32078, y = 32456, z = 8 }, bossName = "Kalyassa", @@ -19,7 +19,7 @@ local UniqueTable = { }, -- Zorvorax entrance [35003] = { - storage = Storage.FirstDragon.SecretsCounter, + storage = Storage.Quest.U11_02.TheFirstDragon.SecretsCounter, value = 3, range = 10, newPos = { x = 32008, y = 32396, z = 8 }, @@ -28,7 +28,7 @@ local UniqueTable = { }, -- Gelidrazah entrance [35004] = { - storage = Storage.FirstDragon.GelidrazahAccess, + storage = Storage.Quest.U11_02.TheFirstDragon.GelidrazahAccess, value = 1, range = 10, newPos = { x = 32076, y = 32402, z = 8 }, @@ -65,7 +65,7 @@ function entranceTeleport.onStepIn(creature, item, position, fromPosition) return true end - if player:getStorageValue(Storage.FirstDragon.Questline) < 1 or player:getStorageValue(setting.storage) < setting.value then + if player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.Questline) < 1 or player:getStorageValue(setting.storage) < setting.value then player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) player:teleportTo(fromPosition) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) diff --git a/data-otservbr-global/scripts/quests/the_first_dragon/movements_last_teleport.lua b/data-otservbr-global/scripts/quests/the_first_dragon/movements_last_teleport.lua index 310fabe8cd7..80509399d64 100644 --- a/data-otservbr-global/scripts/quests/the_first_dragon/movements_last_teleport.lua +++ b/data-otservbr-global/scripts/quests/the_first_dragon/movements_last_teleport.lua @@ -1,27 +1,43 @@ local lastTeleport = MoveEvent() +local function isDateWithinEvent() + local currentDate = os.date("*t") + local startDate = { day = 14, month = 1 } + local endDate = { day = 12, month = 2 } + + if (currentDate.month == startDate.month and currentDate.day >= startDate.day) or (currentDate.month == endDate.month and currentDate.day <= endDate.day) or (currentDate.month > startDate.month and currentDate.month < endDate.month) then + return true + end + return false +end + function lastTeleport.onStepIn(creature, item, position, fromPosition) local player = creature:getPlayer() if not player then return end - local setting = UniqueTable[item.uid] - if not setting then + local destination = { x = 33585, y = 30990, z = 14 } + + if not isDateWithinEvent() then + player:teleportTo(fromPosition, true) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "This teleport is only available between January 14 and February 12.") + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) return true end - if player:getStorageValue(Storage.FirstDragon.FirstDragonTimer) < os.time() then + if player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.FirstDragonTimer) < os.time() then position:sendMagicEffect(CONST_ME_TELEPORT) - player:teleportTo(setting.destination) + player:teleportTo(destination) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) else player:teleportTo(fromPosition, true) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have to wait to challenge The First Dragon again!") player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) end + return true end -lastTeleport:uid(24894) +lastTeleport:uid(24889) lastTeleport:register() diff --git a/data-otservbr-global/scripts/quests/the_first_dragon/movements_zorvorax_secrets.lua b/data-otservbr-global/scripts/quests/the_first_dragon/movements_zorvorax_secrets.lua index 16ea14e9fcd..36772967418 100644 --- a/data-otservbr-global/scripts/quests/the_first_dragon/movements_zorvorax_secrets.lua +++ b/data-otservbr-global/scripts/quests/the_first_dragon/movements_zorvorax_secrets.lua @@ -1,18 +1,15 @@ local UniqueTable = { [25002] = { - storage = Storage.FirstDragon.DesertTile, - msg = "You enter the beautiful oasis. \ - By visiting this sacred site you're infused with the power of water bringing life to the desert.", + storage = Storage.Quest.U11_02.TheFirstDragon.DesertTile, + msg = "You enter the beautiful oasis. By visiting this sacred site you're infused with the power of water bringing life to the desert.", }, [25003] = { - storage = Storage.FirstDragon.StoneSculptureTile, - msg = "You enter the circle of trees and flowers. \ - By visiting this sacred site you're infused with the power of nature and plants.", + storage = Storage.Quest.U11_02.TheFirstDragon.StoneSculptureTile, + msg = "You enter the circle of trees and flowers. By visiting this sacred site you're infused with the power of nature and plants.", }, [25004] = { - storage = Storage.FirstDragon.SuntowerTile, - msg = "You entered the suntower of Ab'dendriel. \ - By visiting this sacred site you're infused with the power of the life-giving sun.", + storage = Storage.Quest.U11_02.TheFirstDragon.SuntowerTile, + msg = "You entered the suntower of Ab'dendriel. By visiting this sacred site you're infused with the power of the life-giving sun.", }, } @@ -31,7 +28,7 @@ function zorvoraxSecrets.onStepIn(creature, item, position, fromPosition) if player:getStorageValue(setting.storage) < 1 then player:setStorageValue(setting.storage, 1) - player:setStorageValue(Storage.FirstDragon.SecretsCounter, player:getStorageValue(Storage.FirstDragon.SecretsCounter) + 1) + player:setStorageValue(Storage.Quest.U11_02.TheFirstDragon.SecretsCounter, player:getStorageValue(Storage.Quest.U11_02.TheFirstDragon.SecretsCounter) + 1) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, setting.msg) return true end diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_asura_mirror.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_asura_mirror.lua deleted file mode 100644 index 0659f5f95e0..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_asura_mirror.lua +++ /dev/null @@ -1,21 +0,0 @@ -local goPos = { x = 32813, y = 32754, z = 9 } - -local asuraMirror = Action() -function asuraMirror.onUse(creature, item, position, fromPosition, pos, target, toPosition) - local player = creature:getPlayer() - if not player then - return - end - - if player:getLevel() >= 250 then - position:sendMagicEffect(CONST_ME_TELEPORT) - player:teleportTo(goPos) - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - else - player:sendCancelMessage("You do not have enough level.") - end - return true -end - -asuraMirror:aid(64019) -asuraMirror:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_basinfire.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_basinfire.lua deleted file mode 100644 index b37eede46ca..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_basinfire.lua +++ /dev/null @@ -1,18 +0,0 @@ -local basinFire = Action() - -function basinFire.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.TheSecretLibrary.Mota) == 8 then - if target.itemid == 2114 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found a reward.") - player:setStorageValue(Storage.TheSecretLibrary.Mota, 9) - player:removeItem(32408, 1) - player:addItem(32623, 1) - player:setStorageValue(Storage.TheSecretLibrary.BasinDoor, 1) - end - return true - end - return false -end - -basinFire:id(1334) -basinFire:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bastion_access.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bastion_access.lua deleted file mode 100644 index 285722c4a7a..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bastion_access.lua +++ /dev/null @@ -1,13 +0,0 @@ -local bastionAccess = Action() - -function bastionAccess.onUse(player, item, fromPosition, target, toPosition, isHotkey) - local time = getTibiaTimerDayOrNight() - if time == "night" and target:getId() == 27836 and player:getStorageValue(Storage.TheSecretLibrary.FalconBastionAccess) == 1 then - player:teleportTo(Position({ x = 33357, y = 31308, z = 4 })) - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - end - return true -end - -bastionAccess:id(28468) -bastionAccess:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_blacktp.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_blacktp.lua deleted file mode 100644 index bd7e614d317..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_blacktp.lua +++ /dev/null @@ -1,14 +0,0 @@ -local backTp = Action() - -function backTp.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.TheSecretLibrary.BlackTel) == -1 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found a skull.") - player:addItem(28489, 1) - player:setStorageValue(Storage.TheSecretLibrary.BlackTel, 1) - return true - end - return false -end - -backTp:uid(1098) -backTp:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bluetp.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bluetp.lua deleted file mode 100644 index 2ab728d4113..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bluetp.lua +++ /dev/null @@ -1,14 +0,0 @@ -local greenTp = Action() - -function greenTp.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.TheSecretLibrary.BlueTel) == -1 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found a piece of ebony.") - player:addItem(28491, 1) - player:setStorageValue(Storage.TheSecretLibrary.BlueTel, 1) - return true - end - return false -end - -greenTp:uid(1097) -greenTp:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_boattp.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_boattp.lua deleted file mode 100644 index 257cb2290f1..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_boattp.lua +++ /dev/null @@ -1,17 +0,0 @@ -local boatTp = Action() - -function boatTp.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.TheSecretLibrary.HighDry) == 4 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The raft was not that solid in the end, its parts are floating in the ocean now. But at least you reached dry land.") - player:removeItem(32407, 1) - player:teleportTo(Position(32187, 32474, 7)) - player:setStorageValue(Storage.TheSecretLibrary.HighDry, 5) - player:setStorageValue(Storage.TheSecretLibrary.PinkTel, 3) - player:setStorageValue(Storage.TheSecretLibrary.Mota, 13) - return true - end - return false -end - -boatTp:uid(1104) -boatTp:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bone.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bone.lua deleted file mode 100644 index 2a5244d449b..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bone.lua +++ /dev/null @@ -1,13 +0,0 @@ -local bone = Action() - -function bone.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.TheSecretLibrary.Mota) == 1 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found a reward.") - player:setStorageValue(Storage.TheSecretLibrary.Mota, 2) - return true - end - return false -end - -bone:uid(1083) -bone:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bonedoor.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bonedoor.lua deleted file mode 100644 index a2bf76fd430..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bonedoor.lua +++ /dev/null @@ -1,17 +0,0 @@ -local boneDoor = Action() - -function boneDoor.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.TheSecretLibrary.Mota) == 9 then - if target.itemid == 10876 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found a reward.") - player:setStorageValue(Storage.TheSecretLibrary.Mota, 10) - player:removeItem(32648, 1) - player:setStorageValue(Storage.TheSecretLibrary.SkullDoor, 1) - end - return true - end - return false -end - -boneDoor:id(1335) -boneDoor:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_book.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_book.lua deleted file mode 100644 index b6ce0469168..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_book.lua +++ /dev/null @@ -1,14 +0,0 @@ -local bookdeepling = Action() - -function bookdeepling.onUse(player, item, frompos, item2, topos) - if player:getStorageValue(Storage.TheSecretLibrary.LiquidDeath) == 1 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The descriptions in this book look like plans detailing the launch of a large-scale assault.") - player:setStorageValue(Storage.TheSecretLibrary.LiquidDeath, 2) - else - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "oh sorry") - end - return true -end - -bookdeepling:uid(1073) -bookdeepling:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bosses_killed.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bosses_killed.lua deleted file mode 100644 index a62b922469e..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_bosses_killed.lua +++ /dev/null @@ -1,32 +0,0 @@ -local bosses = { - ["ghulosh"] = { storage = Storage.Quest.U11_80.TheSecretLibrary.GhuloshKilled }, - ["gorzindel"] = { storage = Storage.Quest.U11_80.TheSecretLibrary.GorzindelKilled }, - ["lokathmor"] = { storage = Storage.Quest.U11_80.TheSecretLibrary.LokathmorKilled }, - ["mazzinor"] = { storage = Storage.Quest.U11_80.TheSecretLibrary.MazzinorKilled }, - ["scourge of oblivion"] = { storage = Storage.Quest.U11_80.TheSecretLibrary.ScourgeOfOblivionKilled }, -} - -local bossesSecretLibrary = CreatureEvent("SecretLibraryBossDeath") -function bossesSecretLibrary.onDeath(creature) - local bossConfig = bosses[creature:getName():lower()] - if not bossConfig then - return true - end - onDeathForDamagingPlayers(creature, function(creature, player) - if bossConfig.storage then - player:setStorageValue(bossConfig.storage, 1) - end - local bossesKilled = 0 - for value in pairs(bosses) do - if player:getStorageValue(bosses[value].storage) > 0 then - bossesKilled = bossesKilled + 1 - end - end - if bossesKilled >= 4 then -- number of mini bosses - player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.ScourgeOfOblivionDoor, 1) - end - end) - return true -end - -bossesSecretLibrary:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_corpse.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_corpse.lua deleted file mode 100644 index 7f33db76e36..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_corpse.lua +++ /dev/null @@ -1,14 +0,0 @@ -local corpse = Action() - -function corpse.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.TheSecretLibrary.TheLament) == 5 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found a scribbled notes.") - player:addItem(28515, 1) - player:setStorageValue(Storage.TheSecretLibrary.TheLament, 6) - return true - end - return false -end - -corpse:uid(1095) -corpse:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_energybasin.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_energybasin.lua deleted file mode 100644 index 99109f91ebb..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_energybasin.lua +++ /dev/null @@ -1,44 +0,0 @@ -local config = { - -- yellow - [1085] = { - itemId = 27868, - msg = "Success", - storage = Storage.TheSecretLibrary.Mota, - getValue = 4, - setValue = 5, - basin = 3514, - }, - -- green - [1086] = { - itemId = 27867, - msg = "Success", - storage = Storage.TheSecretLibrary.Mota, - getValue = 5, - setValue = 6, - basin = 3514, - }, - -- red - [1087] = { itemId = 27869, msg = "Success", storage = Storage.TheSecretLibrary.Mota, getValue = 6, setValue = 7, basin = 3514 }, -} - -local energyBasin = Action() - -function energyBasin.onUse(player, item, fromPosition, target, toPosition, isHotkey) - local setting = config[item.uid] - if setting then - if player:getStorageValue(setting.storage) == setting.getValue then - player:removeItem(setting.itemId, 1) - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, setting.msg) - player:setStorageValue(setting.storage, setting.setValue) - else - player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE) - end - end - return true -end - -for index, value in pairs(config) do - energyBasin:uid(index) -end - -energyBasin:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_enterlibrary.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_enterlibrary.lua deleted file mode 100644 index a72a30a9289..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_enterlibrary.lua +++ /dev/null @@ -1,15 +0,0 @@ -local blacktp = Action() - -function blacktp.onUse(player, item, frompos, item2, topos) - if player:getStorageValue(Storage.TheSecretLibrary.HighDry) == 5 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Welcome to the Secret Library.") - player:teleportTo(Position(32516, 32537, 12)) - else - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "sorry") - end - - return true -end - -blacktp:uid(26705) -blacktp:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_eyekey.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_eyekey.lua deleted file mode 100644 index 245b1a0184f..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_eyekey.lua +++ /dev/null @@ -1,14 +0,0 @@ -local eyeKey = Action() - -function eyeKey.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.TheSecretLibrary.TheLament) == 4 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found a eye key.") - player:addItem(28477, 1) - player:setStorageValue(Storage.TheSecretLibrary.TheLament, 5) - return true - end - return false -end - -eyeKey:uid(1094) -eyeKey:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_fish.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_fish.lua deleted file mode 100644 index 0a02d20a344..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_fish.lua +++ /dev/null @@ -1,14 +0,0 @@ -local fish = Action() - -function fish.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.TheSecretLibrary.HighDry) == 2 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found a hawser.") - player:addItem(32407, 1) - player:setStorageValue(Storage.TheSecretLibrary.HighDry, 3) - return true - end - return false -end - -fish:uid(1102) -fish:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_greentp.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_greentp.lua deleted file mode 100644 index 306ca25bf81..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_greentp.lua +++ /dev/null @@ -1,14 +0,0 @@ -local greenTp = Action() - -function greenTp.onUse(player, item, frompos, item2, topos) - if player:getStorageValue(Storage.TheSecretLibrary.GreenTel) == -1 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You see silver chimes dangling on the dragon statue in this room.") - player:addItem(28494, 1) - player:setStorageValue(Storage.TheSecretLibrary.GreenTel, 1) - return true - end - return false -end - -greenTp:uid(1096) -greenTp:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_leverdoor.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_leverdoor.lua deleted file mode 100644 index f8cc5d8af77..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_leverdoor.lua +++ /dev/null @@ -1,14 +0,0 @@ -local leverDoor = Action() - -function leverDoor.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.TheSecretLibrary.Mota) == 3 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found a reward.") - player:setStorageValue(Storage.TheSecretLibrary.Mota, 4) - player:setStorageValue(Storage.TheSecretLibrary.MotaDoor, 1) - return true - end - return false -end - -leverDoor:uid(1084) -leverDoor:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_leverruby.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_leverruby.lua deleted file mode 100644 index c9365895118..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_leverruby.lua +++ /dev/null @@ -1,14 +0,0 @@ -local leverRuby = Action() - -function leverRuby.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.TheSecretLibrary.Mota) == 7 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found a reward.") - player:addItem(32409, 1) - player:setStorageValue(Storage.TheSecretLibrary.Mota, 8) - return true - end - return false -end - -leverRuby:uid(1088) -leverRuby:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_library_entrances.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_library_entrances.lua deleted file mode 100644 index 7f2565746bc..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_library_entrances.lua +++ /dev/null @@ -1,43 +0,0 @@ -local config = { - { position = { x = 32616, y = 32529, z = 13 }, destination = { x = 32719, y = 32770, z = 10 } }, -- mazzinor - { position = { x = 32718, y = 32768, z = 10 }, destination = { x = 32616, y = 32531, z = 13 } }, - { position = { x = 32724, y = 32728, z = 10 }, destination = { x = 32616, y = 32531, z = 13 } }, - { position = { x = 32660, y = 32736, z = 12 }, destination = { x = 32745, y = 32746, z = 10 } }, -- gorzindel - { position = { x = 32744, y = 32744, z = 10 }, destination = { x = 32660, y = 32734, z = 12 } }, - { position = { x = 32687, y = 32726, z = 10 }, destination = { x = 32660, y = 32734, z = 12 } }, - { position = { x = 32662, y = 32713, z = 13 }, destination = { x = 32745, y = 32770, z = 10 } }, -- ghulosh - { position = { x = 32744, y = 32768, z = 10 }, destination = { x = 32660, y = 32713, z = 13 } }, - { position = { x = 32755, y = 32729, z = 10 }, destination = { x = 32660, y = 32713, z = 13 } }, - { position = { x = 32464, y = 32654, z = 12 }, destination = { x = 32719, y = 32746, z = 10 } }, -- lokathmor - { position = { x = 32718, y = 32744, z = 10 }, destination = { x = 32466, y = 32654, z = 12 } }, - { position = { x = 32750, y = 32696, z = 10 }, destination = { x = 32466, y = 32654, z = 12 } }, - { position = { x = 32480, y = 32601, z = 15 }, destination = { x = 32673, y = 32738, z = 11 } }, -- scourge of oblivion - { position = { x = 32672, y = 32736, z = 11 }, destination = { x = 32480, y = 32599, z = 15 } }, - { position = { x = 32726, y = 32748, z = 11 }, destination = { x = 32480, y = 32599, z = 15 } }, -} - -local libraryEntrances = MoveEvent() -function libraryEntrances.onStepIn(creature, item, position, fromPosition) - local player = creature:getPlayer() - if not player then - return false - end - if player:getLevel() < 250 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need at least level 250 to enter.") - player:teleportTo(fromPosition, true) - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - return false - end - for value in pairs(config) do - if Position(config[value].position) == player:getPosition() then - player:teleportTo(Position(config[value].destination)) - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - return true - end - end -end - -for value in pairs(config) do - libraryEntrances:position(config[value].position) -end -libraryEntrances:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_lotuskey.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_lotuskey.lua deleted file mode 100644 index 98f3f3c6f1b..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_lotuskey.lua +++ /dev/null @@ -1,14 +0,0 @@ -local lotusKey = Action() - -function lotusKey.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.TheSecretLibrary.TheLament) == 3 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found a lotus key.") - player:addItem(28476, 1) - player:setStorageValue(Storage.TheSecretLibrary.TheLament, 4) - return true - end - return false -end - -lotusKey:uid(1093) -lotusKey:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_peacock.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_peacock.lua deleted file mode 100644 index 48bcfb14f81..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_peacock.lua +++ /dev/null @@ -1,14 +0,0 @@ -local peacock = Action() - -function peacock.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.TheSecretLibrary.Peacock) == -1 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found the Peacock Ballad.") - player:addItem(28710, 1) - player:setStorageValue(Storage.TheSecretLibrary.Peacock, 1) - return true - end - return false -end - -peacock:uid(1099) -peacock:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_pinktp.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_pinktp.lua deleted file mode 100644 index 204cf10933d..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_pinktp.lua +++ /dev/null @@ -1,14 +0,0 @@ -local blacktp = Action() - -function blacktp.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.TheSecretLibrary.PinkTel) == -1 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You discovered an old writing desk that contains an ancient map.") - player:addItem(24947, 1) - player:setStorageValue(Storage.TheSecretLibrary.PinkTel, 1) - return true - end - return false -end - -blacktp:uid(1100) -blacktp:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_sealedbook.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_sealedbook.lua deleted file mode 100644 index 8739fea2062..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_sealedbook.lua +++ /dev/null @@ -1,14 +0,0 @@ -local sealedBook = Action() - -function sealedBook.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.TheSecretLibrary.PinkTel) == 2 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found a scribbled notes.") - player:addItem(28515, 1) - player:setStorageValue(Storage.TheSecretLibrary.HighDry, 1) - return true - end - return false -end - -sealedBook:uid(1101) -sealedBook:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_skullground.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_skullground.lua deleted file mode 100644 index 7ff399ca43d..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_skullground.lua +++ /dev/null @@ -1,15 +0,0 @@ -local skeleton = Action() - -function skeleton.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.TheSecretLibrary.TheLament) == 2 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found an old letter.") - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have discovered a skeleton. It seems to hold an old letter and its skull is missing.") - player:addItem(28518, 1) - player:setStorageValue(Storage.TheSecretLibrary.TheLament, 3) - return true - end - return false -end - -skeleton:uid(1092) -skeleton:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_statuedeeplings.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_statuedeeplings.lua deleted file mode 100644 index ad186f94e75..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_statuedeeplings.lua +++ /dev/null @@ -1,34 +0,0 @@ -local config = { - [1074] = { itemId = 15895, msg = "The Njey will appreciate your help.", storage = Storage.TheSecretLibrary.LiquidDeath, getValue = 2, setValue = 3 }, - [1075] = { itemId = 15895, msg = "The Njey will appreciate your help.", storage = Storage.TheSecretLibrary.LiquidDeath, getValue = 3, setValue = 4 }, - [1076] = { itemId = 15895, msg = "The Njey will appreciate your help.", storage = Storage.TheSecretLibrary.LiquidDeath, getValue = 4, setValue = 5 }, - [1077] = { itemId = 15895, msg = "The Njey will appreciate your help.", storage = Storage.TheSecretLibrary.LiquidDeath, getValue = 5, setValue = 6 }, - [1078] = { itemId = 15895, msg = "The Njey will appreciate your help.", storage = Storage.TheSecretLibrary.LiquidDeath, getValue = 6, setValue = 7 }, - [1079] = { itemId = 15895, msg = "The Njey will appreciate your help.", storage = Storage.TheSecretLibrary.LiquidDeath, getValue = 7, setValue = 8 }, - [1080] = { itemId = 15895, msg = "The Njey will appreciate your help.", storage = Storage.TheSecretLibrary.LiquidDeath, getValue = 8, setValue = 9 }, - [1081] = { itemId = 15895, msg = "The Njey will appreciate your help.", storage = Storage.TheSecretLibrary.LiquidDeath, getValue = 9, setValue = 10 }, - [1082] = { itemId = 15895, msg = "The Njey will appreciate your help.", storage = Storage.TheSecretLibrary.LiquidDeath, getValue = 10, setValue = 11 }, -} - -local statuedeeplings = Action() - -function statuedeeplings.onUse(player, item, fromPosition, target, toPosition, isHotkey) - local key = config[item.uid] - if key then - if player:getStorageValue(key.storage) == key.getValue then - if table.contains({ key.itemId }, item.itemid) then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, key.msg) - player:setStorageValue(key.storage, key.setValue) - end - else - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Empty.") - end - end - return true -end - -for index, value in pairs(config) do - statuedeeplings:uid(index) -end - -statuedeeplings:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_strandhair.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_strandhair.lua deleted file mode 100644 index 7dfda7e1ed7..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_strandhair.lua +++ /dev/null @@ -1,14 +0,0 @@ -local strandHair = Action() - -function strandHair.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.TheSecretLibrary.TheLament) == 1 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found a strand of hair.") - player:addItem(28490, 1) - player:setStorageValue(Storage.TheSecretLibrary.TheLament, 2) - return true - end - return false -end - -strandHair:uid(1091) -strandHair:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_telescop.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_telescop.lua deleted file mode 100644 index f53d2c875f6..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/actions_telescop.lua +++ /dev/null @@ -1,13 +0,0 @@ -local telescop = Action() - -function telescop.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if player:getStorageValue(Storage.TheSecretLibrary.HighDry) == 3 then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "By using the telescope you observate the stellar constellations. This should help you to navigate your way back to mainland.") - player:setStorageValue(Storage.TheSecretLibrary.HighDry, 4) - return true - end - return false -end - -telescop:uid(1103) -telescop:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_bony_rod.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_bony_rod.lua new file mode 100644 index 00000000000..56b8bd51eef --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_bony_rod.lua @@ -0,0 +1,29 @@ +local basin = 2113 +local finalBasin = Position(33339, 32117, 10) + +local actions_museum_bony_rod = Action() + +function actions_museum_bony_rod.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if item.itemid == 28709 then + if target.itemid == 27847 then + item:remove(1) + target:remove(1) + player:addItem(28708, 1) + end + elseif item.itemid == 28708 then + if target.itemid == basin then + item:setAttribute(ITEM_ATTRIBUTE_DURATION, 15 * 1000) + player:say("Recharging...", TALKTYPE_MONSTER_SAY) + else + if target:getPosition() == finalBasin and player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.FinalBasin) ~= 1 then + target:getPosition():sendMagicEffect(CONST_ME_DRAWBLOOD) + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.FinalBasin, 1) + end + end + end + + return true +end + +actions_museum_bony_rod:id(28708, 28709) +actions_museum_bony_rod:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_doors.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_doors.lua new file mode 100644 index 00000000000..acebff7a338 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_doors.lua @@ -0,0 +1,27 @@ +local doors = { + [1] = { doorPosition = Position(33246, 32122, 8), storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline, value = 2 }, + [2] = { doorPosition = Position(33208, 32071, 8), storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.LeverPermission, value = 1 }, + [3] = { doorPosition = Position(33208, 32074, 8), storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.LeverPermission, value = 1 }, + [4] = { doorPosition = Position(33341, 32117, 10), storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.FinalBasin, value = 1 }, + [5] = { doorPosition = Position(33344, 32120, 10), storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.SkullSample, value = 1 }, +} + +local actions_museum_doors = Action() + +function actions_museum_doors.onUse(player, item, fromPosition, target, toPosition, isHotkey) + for _, p in pairs(doors) do + if (item:getPosition() == p.doorPosition) and not (Tile(item:getPosition()):getTopCreature()) then + if player:getStorageValue(p.storage) >= p.value then + player:teleportTo(toPosition, true) + item:transform(item.itemid + 1) + else + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The door seems to be sealed against unwanted intruders.") + end + end + end + + return true +end + +actions_museum_doors:aid(4905) +actions_museum_doors:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_gems.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_gems.lua new file mode 100644 index 00000000000..bca9fa38e73 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_gems.lua @@ -0,0 +1,27 @@ +local basins = { + [1] = { position = Position(33219, 32100, 9), item = 27868, storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.YellowGem }, + [2] = { position = Position(33260, 32084, 9), item = 27867, storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.GreenGem }, + [3] = { position = Position(33318, 32090, 9), item = 27869, storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.RedGem }, +} + +local actions_museum_gems = Action() + +function actions_museum_gems.onUse(player, item, fromPosition, target, toPosition, isHotkey) + for _, p in pairs(basins) do + if p.item == item.itemid then + if player:getStorageValue(p.storage) < 1 then + target:getPosition():sendMagicEffect(CONST_ME_SOUND_PURPLE) + player:setStorageValue(p.storage, 1) + item:remove(1) + end + end + end + + return true +end + +for _, gem in pairs(basins) do + actions_museum_gems:id(gem.item) +end + +actions_museum_gems:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_levers.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_levers.lua new file mode 100644 index 00000000000..83b2be616a8 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_levers.lua @@ -0,0 +1,55 @@ +local boneLever = Position(33204, 32069, 8) +local middleLever = Position(33251, 32039, 8) +local thirdLever = Position(33218, 32096, 10) + +local transform = { + [9110] = 9111, + [9111] = 9110, +} + +local actions_museum_levers = Action() + +function actions_museum_levers.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.LeverPermission) ~= 6 then + if item:getPosition() == boneLever then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline) == 3 then + player:say("You don't know what to do.", TALKTYPE_MONSTER_SAY) + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline, 4) + elseif player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline) == 5 then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.MuseumTimer) > os.time() then + player:say("back, back, up, right, left", TALKTYPE_MONSTER_SAY) + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.LeverPermission, 1) + else + player:say("You're too late.", TALKTYPE_MONSTER_SAY) + return true + end + end + elseif item:getPosition() == middleLever then + if item.itemid == 9110 then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline) == 5 and player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.MuseumTimer) < os.time() then + player:say("As you turn the lever you can heart it ticking. Maybe you should hurry up!", TALKTYPE_MONSTER_SAY) + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.MuseumTimer, os.time() + 2 * 60) + item:transform(item.itemid + 1) + end + end + end + end + + if item:getPosition() == thirdLever and player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.FinalBasin) ~= 1 then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.TakenRod) < os.time() then + player:addItem(28709, 1) + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.TakenRod, os.time() + 2 * 60) + else + return true + end + end + + if item.itemid == 9110 or item.itemid == 9111 then + item:transform(transform[item:getId()]) + end + + return true +end + +actions_museum_levers:aid(4906) +actions_museum_levers:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_sample_blood.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_sample_blood.lua new file mode 100644 index 00000000000..647e4a5f0e7 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/actions_sample_blood.lua @@ -0,0 +1,15 @@ +local skullPosition = Position(33348, 32117, 10) + +local actions_museum_sample_blood = Action() + +function actions_museum_sample_blood.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if target:getPosition() == skullPosition and player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.SkullSample) ~= 1 then + item:remove(1) + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.SkullSample, 1) + end + + return true +end + +actions_museum_sample_blood:id(27874) +actions_museum_sample_blood:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/movements_teleportTo.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/movements_teleportTo.lua new file mode 100644 index 00000000000..367698c8dc3 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/bursting_at_the_seams_museum/movements_teleportTo.lua @@ -0,0 +1,67 @@ +local teleports = { + [1] = { fromPos = Position(33246, 32107, 8), toPos = Position(33246, 32096, 8), storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline, value = 2, nextValue = 3 }, + [2] = { fromPos = Position(33246, 32098, 8), toPos = Position(33246, 32109, 8), storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline, value = 2 }, +} + +local lastroom_enter = Position(33344, 32117, 10) +local lastroom_exit = Position(33365, 32147, 10) + +local function sendFire(position) + for x = position.x - 1, position.x + 1 do + local newPos = Position(x, position.y, position.z) + newPos:sendMagicEffect(CONST_ME_FIREATTACK) + end +end + +local movements_museum_teleportTo = MoveEvent() + +function movements_museum_teleportTo.onStepIn(creature, item, position, fromPosition) + if not creature:isPlayer() then + return false + end + + local player = Player(creature:getId()) + + if item.actionid == 4905 then + for _, p in pairs(teleports) do + if position == p.fromPos then + if player:getStorageValue(p.storage) >= p.value then + player:teleportTo(p.toPos) + sendFire(p.toPos) + if p.nextValue and player:getStorageValue(p.storage) < p.nextValue then + player:setStorageValue(p.storage, p.nextValue) + end + else + player:teleportTo(fromPosition, true) + end + end + end + elseif item.actionid == 4906 then + local hasPermission = false + + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.YellowGem) >= 1 and player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.GreenGem) >= 1 and player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.RedGem) >= 1 then + hasPermission = true + end + + if not hasPermission then + player:teleportTo(Position(33226, 32084, 9)) + end + elseif item.actionid == 4907 then + if position == lastroom_enter then + player:teleportTo(Position(33363, 32146, 10)) + elseif position == lastroom_exit and player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.TrialTimer) < os.time() then + player:teleportTo(Position(33336, 32117, 10)) + else + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline) < 6 then + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.TrialTimer, os.time() + 3 * 60) + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.MoTA.Questline, 6) + player:say("rkawdmawfjawkjnfjkawnkjnawkdjawkfmalkwmflkmawkfnzxc", TALKTYPE_MONSTER_SAY) + end + end + end + + return true +end + +movements_museum_teleportTo:aid(4905, 4906, 4907) +movements_museum_teleportTo:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/creaturescripts_falcon_minibosses.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/creaturescripts_falcon_minibosses.lua deleted file mode 100644 index 5ed0af0af8a..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/creaturescripts_falcon_minibosses.lua +++ /dev/null @@ -1,56 +0,0 @@ -local grandCommanderSoerenDeath = CreatureEvent("GrandCommanderSoerenDeath") -function grandCommanderSoerenDeath.onDeath(creature, corpse, killer, mostDamage, unjustified, mostDamage_unjustified) - if killer:isPlayer() and killer:getStorageValue(Storage.TheSecretLibrary.MiniBosses.GrandCommanderSoeren) < 1 then - killer:setStorageValue(Storage.TheSecretLibrary.MiniBosses.GrandCommanderSoeren, 1) - if killer:getStorageValue(Storage.TheSecretLibrary.MiniBosses.PreceptorLazare) == 1 then - killer:setStorageValue(Storage.TheSecretLibrary.LowerBastionAccess, 1) -- Access to quest door - end - end -end - -grandCommanderSoerenDeath:register() - -local preceptorLazareDeath = CreatureEvent("PreceptorLazareDeath") -function preceptorLazareDeath.onDeath(creature, corpse, killer, mostDamage, unjustified, mostDamage_unjustified) - if killer:isPlayer() and killer:getStorageValue(Storage.TheSecretLibrary.MiniBosses.PreceptorLazare) < 1 then - killer:setStorageValue(Storage.TheSecretLibrary.MiniBosses.PreceptorLazare, 1) - if killer:getStorageValue(Storage.TheSecretLibrary.MiniBosses.GrandCommanderSoeren) == 1 then - killer:setStorageValue(Storage.TheSecretLibrary.LowerBastionAccess, 1) -- Access to quest door - end - end -end - -preceptorLazareDeath:register() - -local grandChaplainGaunderDeath = CreatureEvent("GrandChaplainGaunderDeath") -function grandChaplainGaunderDeath.onDeath(creature, corpse, killer, mostDamage, unjustified, mostDamage_unjustified) - if killer:isPlayer() and killer:getStorageValue(Storage.TheSecretLibrary.MiniBosses.GrandChaplainGaunder) < 1 then - killer:setStorageValue(Storage.TheSecretLibrary.MiniBosses.GrandChaplainGaunder, 1) - if killer:getStorageValue(Storage.TheSecretLibrary.MiniBosses.GrandCanonDominus) == 1 then - killer:setStorageValue(Storage.TheSecretLibrary.UndergroundBastionAccess, 1) -- Access to quest door - end - end -end - -grandChaplainGaunderDeath:register() - -local grandCanonDominusDeath = CreatureEvent("GrandCanonDominusDeath") -function grandCanonDominusDeath.onDeath(creature, corpse, killer, mostDamage, unjustified, mostDamage_unjustified) - if killer:isPlayer() and killer:getStorageValue(Storage.TheSecretLibrary.MiniBosses.GrandCanonDominus) < 1 then - killer:setStorageValue(Storage.TheSecretLibrary.MiniBosses.GrandCanonDominus, 1) - if killer:getStorageValue(Storage.TheSecretLibrary.MiniBosses.GrandChaplainGaunder) == 1 then - killer:setStorageValue(Storage.TheSecretLibrary.UndergroundBastionAccess, 1) -- Access to quest door - end - end -end - -grandCanonDominusDeath:register() - -local dazedLeafGolemDeath = CreatureEvent("DazedLeafGolemDeath") -function dazedLeafGolemDeath.onDeath(creature, corpse, killer, mostDamage, unjustified, mostDamage_unjustified) - if killer:isPlayer() and killer:getStorageValue(Storage.TheSecretLibrary.OberonAccess) < 1 then - killer:setStorageValue(Storage.TheSecretLibrary.OberonAccess, 1) -- Access to teleport - end -end - -dazedLeafGolemDeath:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/creaturescripts_kill.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/creaturescripts_kill.lua new file mode 100644 index 00000000000..5a3bde3b049 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/creaturescripts_kill.lua @@ -0,0 +1,57 @@ +local defaultTime = 20 + +local creaturescripts_library_bosses = CreatureEvent("killingLibrary") + +function creaturescripts_library_bosses.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUnjustified) + if not creature:isMonster() or creature:getMaster() then + return true + end + + local monsterStorages = { + ["grand commander soeren"] = { stg = Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.KillingBosses, value = 1 }, + ["preceptor lazare"] = { stg = Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.KillingBosses, value = 2 }, + ["grand chaplain gaunder"] = { stg = Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.KillingBosses, value = 3 }, + ["grand canon dominus"] = { stg = Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.KillingBosses, value = 4 }, + ["dazed leaf golem"] = { stg = Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.KillingBosses, value = 5 }, + ["grand master oberon"] = { stg = Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.KillingBosses, value = 6, achievements = { "Millennial Falcon", "Master Debater" }, lastBoss = true }, + ["brokul"] = { stg = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Questline, value = 7 }, + ["the flaming orchid"] = { stg = Storage.Quest.U11_80.TheSecretLibrary.Asuras.FlammingOrchid, value = 1 }, + } + + local monsterName = creature:getName():lower() + local monsterStorage = monsterStorages[monsterName] + + if monsterStorage then + for playerid, damage in pairs(creature:getDamageMap()) do + local p = Player(playerid) + if p then + if p:getStorageValue(monsterStorage.stg) < monsterStorage.value then + p:setStorageValue(monsterStorage.stg, monsterStorage.value) + end + if monsterStorage.achievements then + for i = 1, #monsterStorage.achievements do + p:addAchievement(monsterStorage.achievements[i]) + end + end + if monsterStorage.lastBoss then + if p:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.Questline) < 2 then + p:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.Questline, 2) + end + end + end + end + end + return true +end + +creaturescripts_library_bosses:register() + +local creaturescripts_library_bosses_oberon = CreatureEvent("oberonImmune") + +function creaturescripts_library_bosses_oberon.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin) + primaryDamage = 0 + secondaryDamage = 0 + return primaryDamage, primaryType, secondaryDamage, secondaryType +end + +creaturescripts_library_bosses_oberon:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/creaturescripts_login.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/creaturescripts_login.lua new file mode 100644 index 00000000000..73a1e37c8ce --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/creaturescripts_login.lua @@ -0,0 +1,8 @@ +local creaturescripts_library_login = CreatureEvent("loginLibrary") + +function creaturescripts_library_login.onLogin(player) + player:registerEvent("killingLibrary") + return true +end + +creaturescripts_library_login:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_grand_canon_dominus.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_grand_canon_dominus.lua deleted file mode 100644 index c2527792c0e..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_grand_canon_dominus.lua +++ /dev/null @@ -1,16 +0,0 @@ -local config = { - monsterName = "Grand Canon Dominus", - bossPosition = Position(33384, 31282, 6), - centerPosition = Position(33384, 31282, 6), - rangeX = 50, - rangeY = 50, -} - -local canonDominus = GlobalEvent("canon dominus") -function canonDominus.onThink(interval, lastExecution) - checkBoss(config.centerPosition, config.rangeX, config.rangeY, config.monsterName, config.bossPosition) - return true -end - -canonDominus:interval(15 * 60 * 1000) -canonDominus:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_grand_chaplain_gaunder.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_grand_chaplain_gaunder.lua deleted file mode 100644 index 402005b0aa8..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_grand_chaplain_gaunder.lua +++ /dev/null @@ -1,16 +0,0 @@ -local config = { - monsterName = "Grand Chaplain Gaunder", - bossPosition = Position(33370, 31327, 5), - centerPosition = Position(33370, 31327, 5), - rangeX = 50, - rangeY = 50, -} - -local chaplaingaunder = GlobalEvent("chaplaingaunder") -function chaplaingaunder.onThink(interval, lastExecution) - checkBoss(config.centerPosition, config.rangeX, config.rangeY, config.monsterName, config.bossPosition) - return true -end - -chaplaingaunder:interval(15 * 60 * 1000) -chaplaingaunder:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_grand_commander_soeren.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_grand_commander_soeren.lua deleted file mode 100644 index 64f4ab330e5..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_grand_commander_soeren.lua +++ /dev/null @@ -1,16 +0,0 @@ -local config = { - monsterName = "Grand Commander Soeren", - bossPosition = Position(33376, 31320, 2), - centerPosition = Position(33376, 31320, 2), - rangeX = 50, - rangeY = 50, -} - -local grandCommander = GlobalEvent("grand commander") -function grandCommander.onThink(interval, lastExecution) - checkBoss(config.centerPosition, config.rangeX, config.rangeY, config.monsterName, config.bossPosition) - return true -end - -grandCommander:interval(15 * 60 * 1000) -- 15 minutes -grandCommander:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_preceptor_lazare.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_preceptor_lazare.lua deleted file mode 100644 index ea05353ad09..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_preceptor_lazare.lua +++ /dev/null @@ -1,14 +0,0 @@ -local config = { - monsterName = "Preceptor Lazare", - bossPosition = Position(33374, 31338, 3), - range = 50, -} - -local preceptorLazare = GlobalEvent("PreceptorLazareRespawn") -function preceptorLazare.onThink(interval, lastExecution) - checkBoss(config.bossPosition, config.range, config.range, config.monsterName, config.bossPosition) - return true -end - -preceptorLazare:interval(15 * 60 * 1000) -- 15 minutes -preceptorLazare:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_spawn_damage.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_spawn_damage.lua deleted file mode 100644 index e33a630b556..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/globalevents_secret_library_spawn_damage.lua +++ /dev/null @@ -1,84 +0,0 @@ -local effects = { - { fromPosition = Position(32526, 32536, 12), toPosition = Position(32526, 32539, 12), effect = CONST_ME_HOLYDAMAGE }, - { fromPosition = Position(32533, 32536, 12), toPosition = Position(32533, 32539, 12), effect = CONST_ME_HOLYDAMAGE }, - { fromPosition = Position(32540, 32536, 12), toPosition = Position(32540, 32539, 12), effect = CONST_ME_HOLYDAMAGE }, - { fromPosition = Position(32544, 32542, 12), toPosition = Position(32546, 32542, 12), effect = CONST_ME_HOLYDAMAGE }, - { fromPosition = Position(32540, 32545, 12), toPosition = Position(32540, 32547, 12), effect = CONST_ME_HOLYDAMAGE }, - { fromPosition = Position(32533, 32545, 12), toPosition = Position(32533, 32547, 12), effect = CONST_ME_HOLYDAMAGE }, - { fromPosition = Position(32526, 32545, 12), toPosition = Position(32526, 32547, 12), effect = CONST_ME_HOLYDAMAGE }, - { fromPosition = Position(32522, 32549, 12), toPosition = Position(32524, 32549, 12), effect = CONST_ME_HOLYDAMAGE }, - { fromPosition = Position(32522, 32554, 12), toPosition = Position(32524, 32554, 12), effect = CONST_ME_HOLYDAMAGE }, - { fromPosition = Position(32522, 32559, 12), toPosition = Position(32524, 32559, 12), effect = CONST_ME_HOLYDAMAGE }, - { fromPosition = Position(32528, 32561, 12), toPosition = Position(32528, 32563, 12), effect = CONST_ME_HOLYDAMAGE }, - { fromPosition = Position(32535, 32561, 12), toPosition = Position(32535, 32563, 12), effect = CONST_ME_HOLYDAMAGE }, - { fromPosition = Position(32542, 32561, 12), toPosition = Position(32542, 32563, 12), effect = CONST_ME_HOLYDAMAGE }, - { fromPosition = Position(33369, 31342, 5), toPosition = Position(33371, 31342, 5), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(33369, 31334, 5), toPosition = Position(33371, 31334, 5), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(33363, 31330, 4), toPosition = Position(33365, 31330, 4), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(33363, 31332, 4), toPosition = Position(33365, 31332, 4), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(33369, 31318, 3), toPosition = Position(33370, 31318, 3), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(33369, 31322, 3), toPosition = Position(33370, 31322, 3), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(33369, 31324, 3), toPosition = Position(33370, 31324, 3), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(33369, 31328, 3), toPosition = Position(33370, 31328, 3), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(33369, 31330, 3), toPosition = Position(33370, 31330, 3), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(33369, 31336, 3), toPosition = Position(33370, 31336, 3), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(33369, 31339, 3), toPosition = Position(33370, 31339, 3), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(33364, 31351, 4), toPosition = Position(33364, 31352, 4), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(33366, 31351, 4), toPosition = Position(33366, 31352, 4), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(31716, 31789, 1), toPosition = Position(31716, 31791, 1), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(31709, 31789, 1), toPosition = Position(31709, 31791, 1), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(31710, 31789, 1), toPosition = Position(31710, 31791, 1), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(31703, 31789, 1), toPosition = Position(31703, 31791, 1), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(31704, 31789, 1), toPosition = Position(31704, 31791, 1), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(31697, 31789, 1), toPosition = Position(31697, 31791, 1), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(31698, 31789, 1), toPosition = Position(31698, 31791, 1), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(31691, 31789, 1), toPosition = Position(31691, 31791, 1), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(31692, 31789, 1), toPosition = Position(31692, 31791, 1), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(32514, 32610, 12), toPosition = Position(32514, 32612, 12), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(32515, 32610, 12), toPosition = Position(32515, 32612, 12), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(32520, 32610, 12), toPosition = Position(32520, 32612, 12), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(32521, 32610, 12), toPosition = Position(32521, 32612, 12), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(32526, 32610, 12), toPosition = Position(32526, 32612, 12), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(32532, 32610, 12), toPosition = Position(32532, 32612, 12), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(32533, 32610, 12), toPosition = Position(32533, 32612, 12), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(32538, 32610, 12), toPosition = Position(32538, 32612, 12), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(32539, 32610, 12), toPosition = Position(32539, 32612, 12), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(32544, 32610, 12), toPosition = Position(32544, 32612, 12), effect = CONST_ME_FIREAREA }, - { fromPosition = Position(32545, 32610, 12), toPosition = Position(32545, 32612, 12), effect = CONST_ME_FIREAREA }, -} - -local spawnDamage = GlobalEvent("spawn damage") -function spawnDamage.onThink(interval) - local jolf - for i = 1, #effects do - local settings = effects[i] - fromPosition = settings.fromPosition - toPosition = settings.toPosition - local spectators = Game.getSpectators(settings.fromPosition, false, true, 7, 7, 5, 5) - if #spectators > 0 then - if settings.effect then - for y = fromPosition.y, toPosition.y do - local newPosition = Position(fromPosition.x, y, fromPosition.z) - newPosition:sendMagicEffect(settings.effect) - - jolf = Tile(newPosition):getTopCreature() - if jolf and jolf:isPlayer() then - doTargetCombatHealth(0, jolf, COMBAT_PHYSICALDAMAGE, -500, -1000, settings.effect) - end - end - for x = fromPosition.x, toPosition.x do - local newPosition2 = Position(x, fromPosition.y, fromPosition.z) - newPosition2:sendMagicEffect(settings.effect) - jolf = Tile(newPosition2):getTopCreature() - if jolf and jolf:isPlayer() then - doTargetCombatHealth(0, jolf, COMBAT_PHYSICALDAMAGE, -10, -500, settings.effect) - end - end - end - end - end - return true -end - -spawnDamage:interval(9000) -spawnDamage:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/high_and_dry_isles/actions_telescope.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/high_and_dry_isles/actions_telescope.lua new file mode 100644 index 00000000000..191db8e4cc6 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/high_and_dry_isles/actions_telescope.lua @@ -0,0 +1,13 @@ +local actions_isles_telescope = Action() + +function actions_isles_telescope.onUse(player, item, fromPosition, itemEx, toPosition) + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.BoatStages) == 2 then + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.BoatStages, 3) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The telescope provides a perfect view over the endless ocean - no land in sight") + end + + return true +end + +actions_isles_telescope:aid(4935) +actions_isles_telescope:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/high_and_dry_isles/movements_boat_puzzle.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/high_and_dry_isles/movements_boat_puzzle.lua new file mode 100644 index 00000000000..1e5ac1e9466 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/high_and_dry_isles/movements_boat_puzzle.lua @@ -0,0 +1,43 @@ +local movements_isle_color_puzzle = MoveEvent() + +function movements_isle_color_puzzle.onStepIn(creature, item, position, fromPosition) + if not creature:isPlayer() then + return false + end + + local player = Player(creature:getId()) + local boatStage = player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.BoatStages) + + if item.actionid == 4936 then + if boatStage < 1 then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You see the scattered parts of a wrecked ship. Miraculously the ship telescope survived the wreckm it seems still to be intact.") + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.BoatStages, 1) + elseif boatStage >= 1 and boatStage < 2 then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There are still some loose planks and hawsers. You can't use the raft like this, it will sink for sure.") + end + elseif item.actionid == 4937 then + if boatStage <= 1 then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Hawser) == 1 then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You use the hawser to lash up the loose planks. The raft should be seaworthy now.") + if player:getItemCount(28707) >= 1 then + player:removeItem(28707, 1) + end + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.BoatStages, 2) + else + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You see the scattered parts of a wrecked ship. Miraculously the ship telescope survived the wreck it seems still to be intact.") + end + elseif boatStage == 2 then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Witout any points of orientation you will never find your way back. Try to find a way to improve your navigation.") + elseif boatStage == 3 then + player:teleportTo(Position(32187, 32473, 7)) + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Questline) < 3 then + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Questline, 3) + end + end + end + + return true +end + +movements_isle_color_puzzle:aid(4936, 4937) +movements_isle_color_puzzle:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/high_and_dry_isles/movements_stepIn.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/high_and_dry_isles/movements_stepIn.lua new file mode 100644 index 00000000000..24e6580bf62 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/high_and_dry_isles/movements_stepIn.lua @@ -0,0 +1,100 @@ +local raxias = { + position = Position(33465, 32157, 7), + fromPos = Position(33454, 32151, 8), + toPos = Position(33477, 32171, 8), + storage = Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.RaxiasTimer, + exit = Position(33462, 32159, 7), + toPosition = Position(33466, 32156, 8), + bossName = "Raxias", + bossPos = Position(33466, 32161, 8), +} + +local turtle = { + fromPosition = Position(32460, 32928, 7), + toPosition = Position(32316, 32701, 7), + storageTimer = Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Turtle, +} + +local svargrond = { + fromPosition = Position(32119, 31734, 7), + toPosition = Position(32127, 31665, 7), +} + +local defaultMessage = "You have ten minutes to kill and loot this monster, else you will lose that chance and will be kicked out." + +local function resetRoom(position) + local spec = Game.getSpectators(position, false, false, 5, 5, 5, 5) + for _, c in pairs(spec) do + if c and c:isPlayer() then + return false + end + end + + for _, c in pairs(spec) do + if c then + c:remove() + end + end + + return true +end + +local function startBattle(pid, position, b_name, middle) + local player = Player(pid) + + if player then + player:teleportTo(position) + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + player:say(defaultMessage, TALKTYPE_MONSTER_SAY) + local monster = Game.createMonster(b_name, middle) + end +end + +local movements_isle_stepIn = MoveEvent() + +function movements_isle_stepIn.onStepIn(creature, item, position, fromPosition) + if not creature:isPlayer() then + return false + end + + local player = Player(creature:getId()) + + if position == turtle.fromPosition then + if Game.getStorageValue(turtle.storageTimer) > os.time() then + player:teleportTo(turtle.toPosition) + else + player:say("The turtle is hungry... You must feed it.", TALKTYPE_MONSTER_SAY) + player:teleportTo(fromPosition, true) + end + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + elseif position == svargrond.fromPosition then + player:teleportTo(svargrond.toPosition) + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + elseif position == raxias.position then + if resetRoom(raxias.bossPos) then + if player:getStorageValue(raxias.storage) < os.time() then + startBattle(player:getId(), raxias.toPosition, raxias.bossName, raxias.bossPos) + player:setStorageValue(raxias.storage, os.time() + 20 * 60 * 60) + addEvent(function(cid) + local p = Player(cid) + if p then + if p:getPosition():isInRange(raxias.fromPos, raxias.toPos) then + p:teleportTo(raxias.exit) + end + end + end, 10 * 1000 * 60, player:getId()) + else + player:sendCancelMessage("You are still exhausted from your last battle.") + player:teleportTo(fromPosition, true) + end + else + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You must wait. Someone is challenging " .. raxias.bossName .. " now.") + player:teleportTo(fromPosition, true) + end + end + + return true +end + +movements_isle_stepIn:aid(4935) +movements_isle_stepIn:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_bossesLever.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_bossesLever.lua new file mode 100644 index 00000000000..6f579ea8e07 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_bossesLever.lua @@ -0,0 +1,167 @@ +local mazzinorSummons = { + name = "Wild Knowledge", + eventName = "mazzinorDeath", + middlePosition = Position(32724, 32720, 10), + timing = 25, + positions = { + [1] = Position(32719, 32718, 10), + [2] = Position(32723, 32719, 10), + [3] = Position(32728, 32718, 10), + [4] = Position(32724, 32724, 10), + }, +} + +local ghuloshSummons = { + name = "Bone Jaw", + eventName = "", + middlePosition = Position(32756, 32721, 10), + timing = 25, + positions = { + [1] = Position(32755, 32721, 10), + }, +} + +local gorzindelSummons = { + name = "Mean Minion", + name2 = "Malicious Minion", + eventName = "", + middlePosition = Position(32687, 32719, 10), + timing = 25, + positions = { + [1] = Position(32687, 32717, 10), + }, + positions2 = { + [1] = Position(32687, 32720, 10), + }, + tomesPosition = { + [1] = { name = "stolen knowledge of armor", position = Position(32687, 32707, 10) }, + [2] = { name = "stolen knowledge of summoning", position = Position(32698, 32715, 10) }, + [3] = { name = "stolen knowledge of lifesteal", position = Position(32693, 32729, 10) }, + [4] = { name = "stolen knowledge of spells", position = Position(32681, 32729, 10) }, + [5] = { name = "stolen knowledge of healing", position = Position(32676, 32715, 10) }, + }, +} + +local lokathmorSummons = { + name = "Knowledge Raider", + eventName = "", + middlePosition = Position(32751, 32689, 10), + timing = 25, + positions = { + [1] = Position(32747, 32684, 10), + [2] = Position(32755, 32684, 10), + [3] = Position(32755, 32694, 10), + [4] = Position(32747, 32694, 10), + }, +} + +local bossNames = { "mazzinor", "supercharged mazzinor", "lokathmor", "ghulosh", "ghuloshz' deathgaze", "gorzindel", "stolen tome of portals" } + +local function spawnSummons(k, monsterName, eventName, timing, positionTable, middlePosition, isGorzindel) + local spectators = Game.getSpectators(middlePosition, false, false, 12, 12, 12, 12) + local hasPlayer = false + + for _, c in pairs(spectators) do + if c and c:isPlayer() then + hasPlayer = true + end + end + + if isGorzindel then + local hasTome = false + for _, c in pairs(spectators) do + for i = 1, #gorzindelSummons.tomesPosition do + if c and (c:getName():lower() == gorzindelSummons.tomesPosition[i].name) then + hasTome = true + end + end + end + if not hasTome then + return false + end + end + if hasPlayer then + if k <= 4 then + for i = 1, #positionTable do + local sqm = positionTable[i] + if sqm then + sqm:sendMagicEffect(CONST_ME_TELEPORT) + end + end + k = k + 1 + addEvent(spawnSummons, 2 * 1000, k, monsterName, eventName, timing, positionTable, middlePosition, isGorzindel) + else + for i = 1, #positionTable do + local monster = Game.createMonster(monsterName, positionTable[i]) + if monster then + monster:registerEvent(eventName) + end + end + addEvent(function() + spawnSummons(1, monsterName, eventName, timing, positionTable, middlePosition, isGorzindel) + end, timing * 1000) + end + end +end + +local leverInfo = { + [1] = { bossName = "Mazzinor", storage = Storage.Quest.U11_80.TheSecretLibrary.Library.MazzinorTimer, exit = Position(32616, 32532, 13), position = Position(32720, 32773, 10), type = "x", bossPosition = Position(32724, 32720, 10), teleportTo = Position(32724, 32726, 10), fromPosition = Position(32715, 32712, 10), toPosition = Position(32733, 32729, 10) }, + [2] = { bossName = "Lokathmor", storage = Storage.Quest.U11_80.TheSecretLibrary.Library.LokathmorTimer, exit = Position(32467, 32654, 12), position = Position(32720, 32749, 10), type = "x", bossPosition = Position(32751, 32689, 10), teleportTo = Position(32750, 32694, 10), fromPosition = Position(32741, 32680, 10), toPosition = Position(32759, 32697, 10) }, + [3] = { bossName = "Ghulosh", storage = Storage.Quest.U11_80.TheSecretLibrary.Library.GhuloshTimer, exit = Position(32659, 32713, 13), position = Position(32746, 32773, 10), type = "x", bossPosition = Position(32756, 32721, 10), teleportTo = Position(32755, 32727, 10), fromPosition = Position(32745, 32711, 10), toPosition = Position(32768, 32730, 10) }, + [4] = { bossName = "Gorzindel", storage = Storage.Quest.U11_80.TheSecretLibrary.Library.GorzindelTimer, exit = Position(32660, 32734, 12), position = Position(32746, 32749, 10), type = "x", bossPosition = Position(32685, 32717, 10), teleportTo = Position(32687, 32724, 10), fromPosition = Position(32671, 32703, 10), toPosition = Position(32702, 32734, 10) }, +} + +local actions_library_bossesLever = Action() + +function actions_library_bossesLever.onUse(player, item, fromPosition, itemEx, toPosition) + local playersTable = {} + + for _, lever in pairs(leverInfo) do + if toPosition == lever.position then + if player:doCheckBossRoom(lever.bossName, lever.fromPosition, lever.toPosition) then + if lever.type == "x" then + local startPos = lever.position.x + 1 + for x = startPos, startPos + 4 do + local sqm = Tile(Position(x, lever.position.y, lever.position.z)) + if sqm then + local c = sqm:getTopCreature() + if c and c:isPlayer() then + table.insert(playersTable, c:getId()) + c:teleportTo(lever.teleportTo) + c:setStorageValue(lever.storage, os.time() + 20 * 60 * 60) + end + end + end + end + + local monster = Game.createMonster(lever.bossName, lever.bossPosition) + + if monster then + if lever.bossName:lower() == "mazzinor" then + addEvent(spawnSummons, 4 * 1000, 1, mazzinorSummons.name, mazzinorSummons.eventName, mazzinorSummons.timing, mazzinorSummons.positions, mazzinorSummons.middlePosition, false) + elseif lever.bossName:lower() == "lokathmor" then + addEvent(spawnSummons, 4 * 1000, 1, lokathmorSummons.name, lokathmorSummons.eventName, lokathmorSummons.timing, lokathmorSummons.positions, lokathmorSummons.middlePosition, false) + elseif lever.bossName:lower() == "ghulosh" then + addEvent(spawnSummons, 4 * 1000, 1, ghuloshSummons.name, ghuloshSummons.eventName, ghuloshSummons.timing, ghuloshSummons.positions, ghuloshSummons.middlePosition, false) + local book = Game.createMonster("The Book of Death", Position(32755, 32716, 10)) + Game.setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Library.Ghulosh, 1) + elseif lever.bossName:lower() == "gorzindel" then + addEvent(spawnSummons, 4 * 1000, 1, gorzindelSummons.name, gorzindelSummons.eventName, gorzindelSummons.timing, gorzindelSummons.positions, gorzindelSummons.middlePosition, true) + addEvent(spawnSummons, 4 * 1000, 1, gorzindelSummons.name2, gorzindelSummons.eventName, gorzindelSummons.timing, gorzindelSummons.positions2, gorzindelSummons.middlePosition, true) + local tome = Game.createMonster("Stolen Tome of Portals", Position(32688, 32715, 10)) + for _, k in pairs(gorzindelSummons.tomesPosition) do + local monster = Game.createMonster(k.name, k.position) + local minion = Game.createMonster("Malicious Minion", k.position) + end + end + end + addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, lever.fromPosition, lever.toPosition, lever.exit) + end + end + end + + return true +end + +actions_library_bossesLever:aid(4950) +actions_library_bossesLever:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_parchment.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_parchment.lua new file mode 100644 index 00000000000..1ffb6414c4c --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/actions_parchment.lua @@ -0,0 +1,54 @@ +local deskPosition = Position(32743, 32689, 10) +local center = Position(32751, 32689, 10) + +local function isStuck(mustFree) + local c = Tile(center):getTopCreature() + + if c and c:isMonster() then + if c:getName():lower() == "lokathmor" and c:getSpeed() == 0 then + if mustFree then + c:say("THE DISCHARGE OF THE BOOK BREAKS LOKATHMORS STANCE!", TALKTYPE_MONSTER_SAY) + local cHealth = c:getHealth() + local monster = Game.createMonster("Lokathmor", center, true) + c:remove() + if monster then + monster:addHealth(-(monster:getHealth() - cHealth)) + end + else + return true + end + end + end + + return false +end + +local function freeRoom() + local spectators = Game.getSpectators(center, false, false, 9, 9, 9, 9) + for _, p in pairs(spectators) do + if p and p:isMonster() then + if p:getName():lower() == "force field" then + p:remove() + end + end + end + + isStuck(true) +end + +local actions_library_parchment = Action() + +function onUse(player, item, fromPosition, target, toPosition, isHotkey) + if toPosition == deskPosition then + if isStuck(false) then + player:say("THE DARK KNOWLEDGE PILLS THE BOOK WITH RAW POWER. READY TO BE UNLEASHED!", TALKTYPE_MONSTER_SAY) + freeRoom() + item:remove(1) + end + end + + return true +end + +actions_library_parchment:id(28488) +actions_library_parchment:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/creaturescripts_ghulosh.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/creaturescripts_ghulosh.lua new file mode 100644 index 00000000000..649217a86f5 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/creaturescripts_ghulosh.lua @@ -0,0 +1,67 @@ +local info = { + stages = { + { p = 75, v = 1 }, + { p = 50, v = 2 }, + { p = 25, v = 3 }, + }, + stg = Storage.Quest.U11_80.TheSecretLibrary.Library.Ghulosh, +} + +local function nextStage(storage) + if Game.getStorageValue(storage) < 1 then + Game.setStorageValue(storage, 1) + end + Game.setStorageValue(storage, Game.getStorageValue(storage) + 1) +end + +local creaturescripts_library_ghulosh = CreatureEvent("ghuloshThink") + +function creaturescripts_library_ghulosh.onThink(creature, interval) + local stage = 0 + + for _, k in pairs(info.stages) do + if Game.getStorageValue(info.stg) == k.v then + stage = k.p + end + end + + local position = creature:getPosition() + local cHealth = creature:getHealth() + local percentageHealth = (cHealth / creature:getMaxHealth()) * 100 + + if percentageHealth <= stage then + local monster = Game.createMonster("ghulosh' deathgaze", position, true) + nextStage(info.stg) + creature:remove() + if monster then + monster:addHealth(-(monster:getHealth() - cHealth)) + monster:say("FEEL MY WRATH!!", TALKTYPE_MONSTER_SAY) + end + end +end + +creaturescripts_library_ghulosh:register() + +local function doSpawn(monster, k, position) + if k <= 4 then + position:sendMagicEffect(CONST_ME_TELEPORT) + k = k + 1 + addEvent(doSpawn, 2 * 1000, monster, k, position) + else + local monster = Game.createMonster(monster, position) + end +end + +local creaturescripts_library_ghulosh = CreatureEvent("ghuloshDeath") + +function creaturescripts_library_ghulosh.onDeath(creature, corpse, killer, mostDamageKiller, unjustified, mostDamageUnjustified) + local cPos = creature:getPosition() + + if creature:getName():lower() == "the book of death" then + Game.createMonster("Concentrated Death", cPos) + elseif creature:getName():lower() == "concentrated death" then + addEvent(doSpawn, 4 * 1000, "The Book of Death", 1, Position(32755, 32716, 10)) + end +end + +creaturescripts_library_ghulosh:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/creaturescripts_gorzindel.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/creaturescripts_gorzindel.lua new file mode 100644 index 00000000000..37d11f35a9a --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/creaturescripts_gorzindel.lua @@ -0,0 +1,63 @@ +local knowledges = { + "stolen knowledge of armor", + "stolen knowledge of summoning", + "stolen knowledge of lifesteal", + "stolen knowledge of spells", + "stolen knowledge of healing", +} + +local middlePosition = Position(32687, 32719, 10) + +local creaturescripts_gorzindel = CreatureEvent("gorzindelDeath") + +function creaturescripts_gorzindel.onDeath(creature, corpse, killer, mostDamageKiller, unjustified, mostDamageUnjustified) + local cPos = creature:getPosition() + + if isInArray(knowledges, creature:getName():lower()) then + addEvent(function() + local spectators = Game.getSpectators(middlePosition, false, false, 12, 12, 12, 12) + local hasKnowledges = false + for _, c in pairs(spectators) do + if c and isInArray(knowledges, c:getName():lower()) then + hasKnowledges = true + end + end + if not hasKnowledges then + for _, c in pairs(spectators) do + if c then + if c:getName():lower() == "mean minion" then + c:getPosition():sendMagicEffect(CONST_ME_POFF) + c:remove() + elseif c:getName():lower() == "gorzindel" then + c:unregisterEvent("gorzindelHealth") + end + end + end + end + end, 1 * 1000) + elseif creature:getName():lower() == "stolen tome of portals" then + local portal = Game.createItem(1949, 1, cPos) + if portal then + portal:setActionId(4952) + addEvent(function() + Game.createMonster("stolen tome of portals", cPos, true, true) + local sqm = Tile(cPos):getItemById(1949) + if sqm then + sqm:remove(1) + end + end, 10 * 1000) + end + end +end + +creaturescripts_gorzindel:register() + +local creaturescripts_gorzindel = CreatureEvent("gorzindelHealth") + +function creaturescripts_gorzindel.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin) + primaryDamage = 0 + secondaryDamage = 0 + return primaryDamage, primaryType, secondaryDamage, secondaryType +end + +creaturescripts_gorzindel:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/creaturescripts_lokathmor.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/creaturescripts_lokathmor.lua new file mode 100644 index 00000000000..b01ec8373c5 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/creaturescripts_lokathmor.lua @@ -0,0 +1,12 @@ +local paper = 28488 + +local creaturescripts_library_lokathmor = CreatureEvent("lokathmorDeath") + +function creaturescripts_library_lokathmor.onDeath(creature, corpse, killer, mostDamageKiller, unjustified, mostDamageUnjustified) + local cPos = creature:getPosition() + if creature:getName():lower() == "dark knowledge" then + local item = Game.createItem(paper, 1, cPos) + end +end + +creaturescripts_library_lokathmor:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/creaturescripts_mazzinor.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/creaturescripts_mazzinor.lua new file mode 100644 index 00000000000..1931906f9bb --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/creaturescripts_mazzinor.lua @@ -0,0 +1,33 @@ +local vortexId = 28673 +local actionId = 4951 + +local creaturescripts_library_mazzinor = CreatureEvent("mazzinorDeath") + +function creaturescripts_library_mazzinor.onDeath(creature, corpse, killer, mostDamageKiller, unjustified, mostDamageUnjustified) + local cPos = creature:getPosition() + if creature:getName():lower() == "wild knowledge" then + local vortex = Game.createItem(vortexId, 1, cPos) + if vortex then + vortex:setActionId(actionId) + addEvent(function(cPos) + local item = Tile(cPos):getItemById(vortexId) + if item then + item:remove() + end + end, 1 * 1000 * 60, cPos) + end + end +end + +creaturescripts_library_mazzinor:register() + +local creaturescripts_library_mazzinor = CreatureEvent("mazzinorHealth") + +function creaturescripts_library_mazzinor.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin) + creature:addHealth(primaryDamage or secondaryDamage) + primaryDamage = 0 + secondaryDamage = 0 + return primaryDamage, primaryType, secondaryDamage, secondaryType +end + +creaturescripts_library_mazzinor:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/movements_gorzindel.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/movements_gorzindel.lua new file mode 100644 index 00000000000..8ecf9f94ecd --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/movements_gorzindel.lua @@ -0,0 +1,39 @@ +local tomesPosition = { + [1] = { position = Position(32687, 32707, 10), open = true }, + [2] = { position = Position(32698, 32715, 10), open = true }, + [3] = { position = Position(32693, 32729, 10), open = true }, + [4] = { position = Position(32681, 32729, 10), open = true }, + [5] = { position = Position(32676, 32715, 10), open = true }, +} + +local middlePosition = Position(32687, 32719, 10) + +local movements_library_gorzindel = MoveEvent() + +function movements_library_gorzindel.onStepIn(creature, item, position, fromPosition) + if not creature:isPlayer() then + return false + end + + local player = Player(creature:getId()) + + for _, k in pairs(tomesPosition) do + if k.open then + player:teleportTo(k.position) + k.open = false + addEvent(function(cid) + local p = Player(cid) + if p then + p:teleportTo(middlePosition) + k.open = true + end + end, 10 * 1000, player:getId()) + break + end + end + + return true +end + +movements_library_gorzindel:aid(4952) +movements_library_gorzindel:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/movements_mazzinor.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/movements_mazzinor.lua new file mode 100644 index 00000000000..aadc7528105 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/movements_mazzinor.lua @@ -0,0 +1,21 @@ +local outfit = createConditionObject(CONDITION_OUTFIT) +setConditionParam(outfit, CONDITION_PARAM_TICKS, 30 * 1000) +addOutfitCondition(outfit, { lookType = 1065 }) + +local movements_library_mazzinor = MoveEvent() + +function movements_library_mazzinor.onStepIn(creature, item, position, fromPosition) + if not creature:isPlayer() then + return false + end + + creature:addCondition(outfit) + creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The remains deporalize you temporaly.") + creature:getPosition():sendMagicEffect(CONST_ME_ENERGYHIT) + item:remove(1) + + return true +end + +movements_library_mazzinor:aid(4951) +movements_library_mazzinor:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/movements_timers.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/movements_timers.lua new file mode 100644 index 00000000000..8a6f6f9a134 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/library_area/movements_timers.lua @@ -0,0 +1,37 @@ +local timers = { + [1] = { position = Position(32616, 32529, 13), timer = Storage.Quest.U11_80.TheSecretLibrary.Library.MazzinorTimer, toPosition = Position(32720, 32770, 10) }, + [2] = { position = Position(32464, 32654, 12), timer = Storage.Quest.U11_80.TheSecretLibrary.Library.LokathmorTimer, toPosition = Position(32720, 32746, 10) }, + [3] = { position = Position(32662, 32713, 13), timer = Storage.Quest.U11_80.TheSecretLibrary.Library.GhuloshTimer, toPosition = Position(32746, 32770, 10) }, + [4] = { position = Position(32660, 32736, 12), timer = Storage.Quest.U11_80.TheSecretLibrary.Library.GorzindelTimer, toPosition = Position(32746, 32746, 10) }, + [5] = { position = Position(32750, 32696, 10), toPosition = Position(32466, 32652, 12) }, + [6] = { position = Position(32755, 32729, 10), toPosition = Position(32664, 32711, 13) }, + [7] = { position = Position(32687, 32726, 10), toPosition = Position(32662, 32734, 12) }, + [8] = { position = Position(32724, 32728, 10), toPosition = Position(32618, 32527, 13) }, +} + +local movements_library_timers = MoveEvent() + +function movements_library_timers.onStepIn(creature, item, position, fromPosition) + if not creature:isPlayer() then + return false + end + + local player = Player(creature:getId()) + + for _, k in pairs(timers) do + if position == k.position then + if not k.timer or player:getStorageValue(k.timer) <= os.time() then + player:teleportTo(k.toPosition) + else + player:teleportTo(fromPosition, true) + player:sendCancelMessage("You are still exhausted from your last battle.") + end + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + end + end + + return true +end + +movements_library_timers:aid(4950) +movements_library_timers:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/liquid_death/actions_brokulLever.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/liquid_death/actions_brokulLever.lua new file mode 100644 index 00000000000..2cab34c6e48 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/liquid_death/actions_brokulLever.lua @@ -0,0 +1,113 @@ +local transform = { + [2772] = 2773, + [2773] = 2772, +} + +local leverInfo = { + [1] = { + bossName = "Brokul", + bossPosition = Position(33483, 31437, 15), + leverPosition = Position(33522, 31464, 15), + pushPosition = Position(33522, 31465, 15), + leverFromPos = Position(33520, 31465, 15), + leverToPos = Position(33524, 31465, 15), + storageTimer = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.BrokulTimer, + teleportTo = Position(33484, 31446, 15), + globalTimer = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.BrokulTimerGlobal, + roomFromPosition = Position(33472, 31427, 15), + roomToPosition = Position(33496, 31450, 15), + exitPosition = Position(33528, 31464, 14), + }, +} + +local function clearBossRoom(fromPos, toPos) + local spectators = Game.getSpectators(fromPos, false, false, 0, 0, 0, 0, toPos) + for _, spec in pairs(spectators) do + if not spec:isPlayer() then + spec:remove() + end + end +end + +local function isBossInRoom(fromPos, toPos, bossName) + local hasBoss = false + local hasPlayers = false + local spectators = Game.getSpectators(fromPos, false, false, 0, 0, 0, 0, toPos) + + for _, spec in pairs(spectators) do + if spec:isPlayer() then + hasPlayers = true + elseif spec:isMonster() and spec:getName():lower() == bossName:lower() then + hasBoss = true + end + end + + return hasBoss, hasPlayers +end + +local actions_liquid_brokulLever = Action() + +function actions_liquid_brokulLever.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if not player then + return true + end + + local playersTable = {} + local iPos = item:getPosition() + local pPos = player:getPosition() + + if item.itemid == 2772 then + for i = 1, #leverInfo do + if iPos == leverInfo[i].leverPosition then + local leverTable = leverInfo[i] + if pPos == leverTable.pushPosition then + local hasBoss, hasPlayers = isBossInRoom(leverTable.roomFromPosition, leverTable.roomToPosition, leverTable.bossName) + + if hasPlayers then + player:sendCancelMessage("The room is already occupied by other players.") + return true + elseif hasBoss then + clearBossRoom(leverTable.roomFromPosition, leverTable.roomToPosition) + end + + local playerCount = 0 + for i = leverTable.leverFromPos.x, leverTable.leverToPos.x do + local newPos = Position(i, leverTable.leverFromPos.y, leverTable.leverFromPos.z) + local creature = Tile(newPos):getTopCreature() + if creature and creature:isPlayer() then + if creature:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Questline) >= 6 then + playerCount = playerCount + 1 + table.insert(playersTable, creature:getId()) + else + creature:sendCancelMessage("You are not qualified to face the boss.") + end + end + end + + if playerCount < 5 then + player:sendCancelMessage("You need 5 qualified players for this challenge.") + return true + end + + for _, playerId in ipairs(playersTable) do + local creature = Creature(playerId) + if creature then + creature:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.BrokulTimer, os.time() + 20 * 60 * 60) + creature:teleportTo(leverTable.teleportTo, true) + creature:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + end + end + + local monster = Game.createMonster(leverTable.bossName, leverTable.bossPosition) + addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, leverTable.roomFromPosition, leverTable.roomToPosition, leverTable.exitPosition) + end + end + end + end + + item:transform(transform[item.itemid]) + return true +end + +actions_liquid_brokulLever:aid(4901) +actions_liquid_brokulLever:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/liquid_death/actions_usableItens.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/liquid_death/actions_usableItens.lua new file mode 100644 index 00000000000..168e841886a --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/liquid_death/actions_usableItens.lua @@ -0,0 +1,89 @@ +local config = { + statues = { + [1] = { position = Position(33598, 31398, 14), storage = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Statue1 }, + [2] = { position = Position(33602, 31439, 13), storage = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Statue2 }, + [3] = { position = Position(33587, 31461, 14), storage = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Statue3 }, + [4] = { position = Position(33588, 31461, 14), storage = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Statue4 }, + [5] = { position = Position(33549, 31459, 14), storage = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Statue5 }, + [6] = { position = Position(33577, 31475, 15), storage = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Statue6 }, + [7] = { position = Position(33612, 31465, 15), storage = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Statue7 }, + [8] = { position = Position(33565, 31423, 13), storage = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Statue8 }, + [9] = { position = Position(33574, 31441, 15), storage = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Statue9 }, + }, + bookInfo = { + id = 14072, + position = Position(32881, 32435, 8), + storage = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Questline, + value = 2, + message = "The descriptions in this book look like plans detailing the launch of a large-scale assault.", + }, + statueEffect = CONST_ME_THUNDER, + statueMessage = "The Njey will appreciate your help.", + statueMessage_ = "You recently fixed that idol.", + statueId = { 15895, 15896 }, + fixedId = { 15894, 15897 }, + countStatues = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.StatueCount, + maxValue = 9, +} + +local function doRevert(position, id, newId) + local item = Tile(position):getItemById(newId) + if item then + item:transform(id) + end +end + +local function fixStatue(position, id) + local item = Tile(position):getItemById(id) + local newId = 0 + + if id == config.statueId[1] then + newId = config.fixedId[1] + else + newId = config.fixedId[2] + end + + if item then + item:transform(newId) + position:sendMagicEffect(config.statueEffect) + end + + addEvent(doRevert, 10 * 1000, position, id, newId) +end + +local actions_liquid_usableItens = Action() + +function actions_liquid_usableItens.onUse(player, item, position, fromPosition, pos, target, toPosition) + local b = config.bookInfo + + if item:getId() == b.id and item:getPosition() == b.position then + if player:getStorageValue(b.storage) == b.value then + player:setStorageValue(b.storage, player:getStorageValue(b.storage) + 1) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, b.message) + end + else + local statueId = config.statueId + if item.itemid == statueId[1] or item.itemid == statueId[2] then + for _, k in pairs(config.statues) do + if position == k.position then + if player:getStorageValue(k.storage) < 1 then + player:setStorageValue(k.storage, 1) + player:setStorageValue(config.countStatues, math.max(player:getStorageValue(config.countStatues), 0) + 1) + if player:getStorageValue(config.countStatues) == config.maxValue then + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Questline, player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Questline) + 1) + end + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, config.statueMessage) + fixStatue(position, item.itemid) + else + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, config.statueMessage_) + end + end + end + end + end + + return true +end + +actions_liquid_usableItens:aid(4900) +actions_liquid_usableItens:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/liquid_death/movements_bossWay.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/liquid_death/movements_bossWay.lua new file mode 100644 index 00000000000..becc6a05cb7 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/liquid_death/movements_bossWay.lua @@ -0,0 +1,41 @@ +local config = { + accesses = { + [1] = { fromPos = Position(33525, 31464, 14), toPos = Position(33525, 31464, 15), storage = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Questline, value = 6, message = "Without the help of other Njey you will probably not make enough of a difference in this battle.", timer = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.BrokulTimer }, + }, + defaultMessage = "You are not ready to pass yet.", + notime = "You need to wait 20 hours to face this boss again.", +} + +local movements_liquid_bossWay = MoveEvent() + +function movements_liquid_bossWay.onStepIn(creature, item, position, fromPosition) + if not creature:isPlayer() then + return false + end + + local player = Player(creature:getId()) + + if player then + local accesses = config.accesses + for i = 1, #accesses do + if accesses[i].fromPos == position then + if player:getStorageValue(accesses[i].storage) < accesses[i].value then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, accesses[i].message) + player:teleportTo(fromPosition, true) + else + if player:getStorageValue(accesses[i].timer) > os.time() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, config.notime) + player:teleportTo(fromPosition, true) + else + player:teleportTo(accesses[i].toPos, true) + end + end + end + end + end + + return true +end + +movements_liquid_bossWay:aid(4901) +movements_liquid_bossWay:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/liquid_death/movements_teleportTo.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/liquid_death/movements_teleportTo.lua new file mode 100644 index 00000000000..f313bf3cdd9 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/liquid_death/movements_teleportTo.lua @@ -0,0 +1,53 @@ +local config = { + teleports = { + [1] = { fromPos = Position(32871, 32510, 7), toPos = Position(32881, 32471, 9), storage = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Questline, value = 1, effect = CONST_ME_WATERSPLASH, achievementName = "Spectulation" }, + [2] = { fromPos = Position(32881, 32473, 9), toPos = Position(32871, 32513, 7), effect = CONST_ME_WATERSPLASH }, + [3] = { fromPos = Position(33584, 31388, 13), toPos = Position(33584, 31391, 13), storage = Storage.Quest.U11_80.TheSecretLibrary.LiquidDeath.Questline, value = 4, effect = CONST_ME_TELEPORT, message = "You squeeze through an ancient small passage. There are small symbols carved deep into the coral you cannot read." }, + [4] = { fromPos = Position(33560, 31395, 13), toPos = Position(33561, 31391, 13), effect = CONST_ME_TELEPORT }, + }, + defaultMessage = "You are not ready to pass yet.", +} + +local movements_liquid_teleportTo = MoveEvent() + +function movements_liquid_teleportTo.onStepIn(creature, item, position, fromPosition) + if not creature:isPlayer() then + return false + end + + local player = Player(creature:getId()) + + if player then + for i = 1, #config.teleports do + local tab = config.teleports + if position == tab[i].fromPos then + if tab[i].storage then + if player:getStorageValue(tab[i].storage) >= tab[i].value then + player:teleportTo(tab[i].toPos) + if tab[i].message then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, tab[i].message) + end + if tab[i].achievementName and not player:hasAchievement(tab[i].achievementName) then + player:addAchievement(tab[i].achievementName) + end + if player:getStorageValue(tab[i].storage) == tab[i].value then + player:setStorageValue(tab[i].storage, player:getStorageValue(tab[i].storage) + 1) + end + else + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, config.defaultMessage) + player:teleportTo(fromPosition, true) + return true + end + else + player:teleportTo(tab[i].toPos) + end + player:getPosition():sendMagicEffect(tab[i].effect) + end + end + end + + return true +end + +movements_liquid_teleportTo:aid(4900) +movements_liquid_teleportTo:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/movements_boat.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/movements_boat.lua deleted file mode 100644 index d076b5e22f2..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/movements_boat.lua +++ /dev/null @@ -1,17 +0,0 @@ -local boat = MoveEvent() - -function boat.onStepIn(creature, item, toPosition, fromPosition) - local player = creature:getPlayer() - if not player then - return true - end - - if player:getStorageValue(Storage.TheSecretLibrary.HighDry) == 1 then - player:setStorageValue(Storage.TheSecretLibrary.HighDry, 2) - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "There are still some loose planks and hawsers. You can't use the raft like this, it will sink for sure.") - end - return true -end - -boat:aid(26701) -boat:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/movements_golem_teleport.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/movements_golem_teleport.lua deleted file mode 100644 index c8f53be5453..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/movements_golem_teleport.lua +++ /dev/null @@ -1,17 +0,0 @@ -local golemTeleport = MoveEvent() - -function golemTeleport.onStepIn(creature, item, toPosition, fromPosition) - local player = creature:getPlayer() - if not player then - return true - end - - if player:getStorageValue(Storage.TheSecretLibrary.Mota) == 10 then - player:setStorageValue(Storage.TheSecretLibrary.Mota, 11) - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - end - return true -end - -golemTeleport:aid(26688) -golemTeleport:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/movements_pink_teleport.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/movements_pink_teleport.lua deleted file mode 100644 index 772926ede5e..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/movements_pink_teleport.lua +++ /dev/null @@ -1,17 +0,0 @@ -local pinkTeleport = MoveEvent() - -function pinkTeleport.onStepIn(creature, item, toPosition, fromPosition) - local player = creature:getPlayer() - if not player then - return true - end - - if player:getStorageValue(Storage.TheSecretLibrary.Peacock) == 2 then - player:teleportTo(Position(32880, 32828, 11)) - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - end - return true -end - -pinkTeleport:aid(26698) -pinkTeleport:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/movements_teleport.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/movements_teleport.lua deleted file mode 100644 index e788c64e0a4..00000000000 --- a/data-otservbr-global/scripts/quests/the_secret_library_quest/movements_teleport.lua +++ /dev/null @@ -1,35 +0,0 @@ -local destination = { - [64007] = Position(33345, 31347, 7), --Falcon - [64008] = Position(33357, 31308, 4), --Falcon - [64009] = Position(33382, 31292, 7), --Falcon - [64010] = Position(33327, 31351, 7), --Falcon - [64011] = Position(33201, 31765, 1), --Falcon - [64012] = Position(33327, 31351, 7), --Falcon - [64013] = Position(32958, 32324, 8), --Deep desert - [64014] = Position(33110, 32386, 7), --Deep desert -} - -local teleport = MoveEvent() - -function teleport.onStepIn(creature, item, position, fromPosition) - local player = creature:getPlayer() - if not player then - return true - end - - local teleport = destination[item.actionid] - if teleport then - player:teleportTo(teleport) - fromPosition:sendMagicEffect(CONST_ME_TELEPORT) - teleport:sendMagicEffect(CONST_ME_TELEPORT) - end - return true -end - -teleport:type("stepin") - -for index, value in pairs(destination) do - teleport:aid(index) -end - -teleport:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_bonefiddle.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_bonefiddle.lua new file mode 100644 index 00000000000..1a4ff13266b --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_bonefiddle.lua @@ -0,0 +1,46 @@ +local actions_asura_bonefiddle = Action() + +function actions_asura_bonefiddle.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Asuras.Questline) < 2 then + if item.itemid == 28491 then + if target.itemid == 28489 then + item:remove(1) + target:remove(1) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You attach the ebony wood to the skull. This should meet the requirements of a fingerboard.") + player:addItem(28492) + end + elseif item.itemid == 28492 then + if target.itemid == 28490 then + item:remove(1) + target:remove(1) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You tack the hair to the ebony fingerboard. The strands should be adquate to serve as strings.") + player:addItem(28493) + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Asuras.Questline, 2) + end + end + end + if item.itemid == 28493 then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Asuras.Questline) == 2 then + if player:getPosition():isInRange(Position(32807, 32762, 10), Position(32809, 32768, 10)) then + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Asuras.Questline, 3) + end + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "*There was once a maiden fair, with dark eyes and silken hair. Far away from home she died. No grave, no wake, no mourning.*") + player:getPosition():sendMagicEffect(CONST_ME_SOUND_PURPLE) + elseif player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Asuras.Questline) == 4 then + if player:getPosition():isInRange(Position(32807, 32762, 10), Position(32809, 32768, 10)) then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are playing the Peacock Ballad and the portal opens.") + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Asuras.Questline, 5) + player:getPosition():sendMagicEffect(CONST_ME_SOUND_WHITE) + return true + end + elseif player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Asuras.Questline) >= 5 then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are playing the bone fiddle.") + player:getPosition():sendMagicEffect(CONST_ME_SOUND_WHITE) + end + end + + return true +end + +actions_asura_bonefiddle:id(28491, 28492, 28493) +actions_asura_bonefiddle:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_doors.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_doors.lua new file mode 100644 index 00000000000..6b4ee88eb63 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_doors.lua @@ -0,0 +1,28 @@ +local doors = { + [1] = { doorPosition = Position(32962, 32674, 2), storage = Storage.Quest.U11_80.TheSecretLibrary.Asuras.FlammingOrchid, value = 1, level = 250 }, + [2] = { doorPosition = Position(32959, 32679, 2), storage = Storage.Quest.U11_80.TheSecretLibrary.Asuras.Questline, value = 1 }, +} + +local actions_asura_doors = Action() + +function actions_asura_doors.onUse(player, item, fromPosition, target, toPosition, isHotkey) + for _, p in pairs(doors) do + if (item:getPosition() == p.doorPosition) and not (Tile(item:getPosition()):getTopCreature()) then + if player:getStorageValue(p.storage) >= p.value then + if not p.level or (p.level and player:getLevel() >= p.level) then + player:teleportTo(toPosition, true) + item:transform(item.itemid + 1) + else + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You do not have enough level.") + end + else + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The door seems to be sealed against unwanted intruders.") + end + end + end + + return true +end + +actions_asura_doors:aid(4911) +actions_asura_doors:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_fragrance.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_fragrance.lua new file mode 100644 index 00000000000..e8630e36887 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_fragrance.lua @@ -0,0 +1,12 @@ +local actions_asura_fragrance = Action() + +function actions_asura_fragrance.onUse(player, item, fromPosition, target, toPosition, isHotkey) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Hmmmm, what an infatuating fragrance!") + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Asuras.Fragrance, os.time() + 10 * 60) + item:remove(1) + + return true +end + +actions_asura_fragrance:id(28495) +actions_asura_fragrance:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_keys.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_keys.lua new file mode 100644 index 00000000000..a259ca202ea --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_keys.lua @@ -0,0 +1,35 @@ +local doors = { + [1] = { key = 28476, position = Position(32813, 32813, 9) }, + [2] = { key = 28477, position = Position(32864, 32810, 9) }, +} + +local locked = 23873 +local opened = 23877 + +local function revert(position) + local lockedDoor = Tile(position):getItemById(opened) + if lockedDoor then + lockedDoor:transform(locked) + end +end + +local actions_asura_keys = Action() + +function actions_asura_keys.onUse(player, item, fromPosition, target, toPosition, isHotkey) + for _, k in pairs(doors) do + if item.itemid == k.key then + if toPosition == k.position and target.itemid == locked then + target:transform(opened) + addEvent(revert, 10 * 1000, target:getPosition()) + end + end + end + + return true +end + +for _, door in pairs(doors) do + actions_asura_keys:id(door.key) +end + +actions_asura_keys:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_mirror.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_mirror.lua new file mode 100644 index 00000000000..448152746f2 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/actions_mirror.lua @@ -0,0 +1,19 @@ +local goPos = Position(32814, 32754, 9) + +local actions_asura_mirror = Action() + +function actions_asura_mirror.onUse(player, item, fromPosition, itemEx, toPosition) + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Asuras.FlammingOrchid) >= 1 and player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Asuras.Questline) >= 1 then + if player:getLevel() >= 250 then + player:teleportTo(goPos) + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + else + player:sendCancelMessage("You do not have enough level.") + end + else + player:sendCancelMessage("You do not have permission.") + end +end + +actions_asura_mirror:aid(4910) +actions_asura_mirror:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/creaturescripts_asuras_mechanic.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/creaturescripts_asuras_mechanic.lua new file mode 100644 index 00000000000..d569a8806f2 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/creaturescripts_asuras_mechanic.lua @@ -0,0 +1,50 @@ +local redItems = { + 3566, + 3379, + 3388, + 8039, + 8053, + 8064, + 22534, + 3381, + 7991, + 3380, + 10439, + 3564, +} + +local creaturescripts_asuras_mechanic = CreatureEvent("AsurasMechanic") + +function creaturescripts_asuras_mechanic.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin) + if not attacker or not creature then + return primaryDamage, primaryType, secondaryDamage, secondaryType + end + + if attacker:isPlayer() then + if creature:getName():lower() == "the diamond blossom" then + local slot = attacker:getSlotItem(CONST_SLOT_ARMOR) + if slot then + for i = 1, #redItems do + if slot.itemid == redItems[i] then + return primaryDamage, primaryType, secondaryDamage, secondaryType + end + end + end + elseif creature:getName():lower() == "the blazing rose" then + local slot = attacker:getSlotItem(CONST_SLOT_RIGHT) + if slot and slot.itemid == 28494 then + return primaryDamage, primaryType, secondaryDamage, secondaryType + end + elseif creature:getName():lower() == "the lily of night" then + if attacker:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Asuras.Fragrance) > os.time() then + return primaryDamage, primaryType, secondaryDamage, secondaryType + end + end + end + + primaryDamage = 0 + secondaryDamage = 0 + return primaryDamage, primaryType, secondaryDamage, secondaryType +end + +creaturescripts_asuras_mechanic:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/movements_elemental_portals.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/movements_elemental_portals.lua new file mode 100644 index 00000000000..8776ee64ba3 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_lament_asuras/movements_elemental_portals.lua @@ -0,0 +1,158 @@ +local entrances = { + [1] = { position = Position(32860, 32798, 11), storage = false, toPosition = Position(32887, 32772, 9) }, + [2] = { + position = Position(32858, 32766, 10), + toPosition = Position(32882, 32791, 11), + fromPos = Position(32877, 32790, 11), + toPos = Position(32887, 32800, 11), + exit = Position(32858, 32767, 10), + storage = Storage.Quest.U11_80.TheSecretLibrary.Asuras.DiamondTimer, + bossName = "the diamond blossom", + bossPos = Position(32882, 32795, 11), + }, + [3] = { + position = Position(32818, 32780, 11), + toPosition = Position(32857, 32740, 11), + fromPos = Position(32852, 32739, 11), + toPos = Position(32862, 32749, 11), + exit = Position(32818, 32781, 11), + storage = Storage.Quest.U11_80.TheSecretLibrary.Asuras.DarkTimer, + bossName = "the lily of night", + bossPos = Position(32857, 32744, 11), + }, + [4] = { + position = Position(32854, 32737, 10), + toPosition = Position(32860, 32770, 11), + fromPos = Position(32855, 32769, 11), + toPos = Position(32865, 32779, 11), + exit = Position(32854, 32738, 10), + storage = Storage.Quest.U11_80.TheSecretLibrary.Asuras.BlazingTimer, + bossName = "the blazing rose", + bossPos = Position(32860, 32774, 11), + }, +} +local exits = { + [1] = { position = Position(32857, 32739, 11), toPosition = Position(32818, 32781, 11) }, + [2] = { position = Position(32860, 32769, 11), toPosition = Position(32854, 32738, 10) }, + [3] = { position = Position(32882, 32790, 11), toPosition = Position(32858, 32767, 10) }, + [4] = { position = Position(32886, 32772, 9), toPosition = Position(32860, 32799, 11) }, + [5] = { position = Position(32881, 32829, 11), toPosition = Position(32809, 32765, 10) }, +} +local defaultMessage = "You have ten minutes to kill and loot this monster, else you will lose that chance and will be kicked out." +local purplePosition = Position(32808, 32765, 10) +local quest = Storage.Quest.U11_80.TheSecretLibrary.Asuras.Questline +local toPosition_l = Position(32881, 32828, 11) +local hiddenMap1 = Position(32881, 32820, 11) +local hiddenMap2 = Position(32882, 32820, 11) + +local function isPlayerInRoom(fromPos, toPos) + for x = fromPos.x, toPos.x do + for y = fromPos.y, toPos.y do + for z = fromPos.z, toPos.z do + local tile = Tile(Position(x, y, z)) + if tile then + local creatures = tile:getCreatures() + for _, creature in pairs(creatures) do + if creature:isPlayer() then + return true + end + end + end + end + end + end + return false +end + +local function clearBossRoom(fromPos, toPos, exitPos) + local spectators = Game.getSpectators(fromPos, false, false, 0, 0, 0, 0, toPos) + for _, spec in pairs(spectators) do + if spec:isMonster() then + spec:remove() + end + end +end + +local function startBattle(pid, position, b_name, middle) + local player = Player(pid) + if player then + player:teleportTo(position, true) + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + player:say(defaultMessage, TALKTYPE_MONSTER_SAY) + local monster = Game.createMonster(b_name, middle) + end +end + +local function expelPlayerFromRoom(cid, fromPos, toPos, exitPos) + local player = Player(cid) + if player then + if player:getPosition():isInRange(fromPos, toPos) then + player:teleportTo(exitPos) + exitPos:sendMagicEffect(CONST_ME_TELEPORT) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You took too long, the battle has ended.") + end + end +end + +local movements_asura_elemental_portals = MoveEvent() + +function movements_asura_elemental_portals.onStepIn(creature, item, position, fromPosition) + local player = Player(creature:getId()) + + if not creature:isPlayer() then + return false + end + + if item.actionid == 4915 then + if position == purplePosition then + if player:getStorageValue(quest) >= 5 then + player:teleportTo(toPosition_l) + else + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can not use this portal yet.") + player:teleportTo(fromPosition, true) + end + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + return true + end + for _, k in pairs(entrances) do + if position == k.position then + if isPlayerInRoom(k.fromPos, k.toPos) then + return true + end + clearBossRoom(k.fromPos, k.toPos, k.exit) + if k.storage then + if player:getStorageValue(k.storage) < os.time() then + startBattle(player:getId(), k.toPosition, k.bossName, k.bossPos) + addEvent(expelPlayerFromRoom, 6000000, player:getId(), k.fromPos, k.toPos, k.exit) + player:setStorageValue(k.storage, os.time() + (20 * 3600)) + else + player:sendCancelMessage("You are still exhausted from your last battle.") + player:teleportTo(fromPosition, true) + end + else + player:teleportTo(k.toPosition) + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + end + end + end + elseif item.actionid == 4914 then + if position == hiddenMap1 or hiddenMap2 then + if player:getStorageValue(quest) == 5 then + player:addItem(28908, 1) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have discovered an old writing desk that contains an ancient map.") + player:setStorageValue(quest, 6) + end + end + for _, k in pairs(exits) do + if position == k.position then + player:teleportTo(k.toPosition) + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + end + end + end + + return true +end + +movements_asura_elemental_portals:aid(4914, 4915) +movements_asura_elemental_portals:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/the_order_of_the_falcon/actions_doors.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_order_of_the_falcon/actions_doors.lua new file mode 100644 index 00000000000..34a56f1d74e --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_order_of_the_falcon/actions_doors.lua @@ -0,0 +1,75 @@ +local doors = { + [1] = { doorPosition = Position(33376, 31335, 3), value = 1 }, + [2] = { doorPosition = Position(33371, 31349, 4), value = 2 }, + [3] = { doorPosition = Position(33376, 31349, 4), value = 2 }, + [4] = { doorPosition = Position(33375, 31346, 5), value = 2 }, + [5] = { doorPosition = Position(33363, 31346, 7), value = 4 }, + [6] = { doorPosition = Position(33366, 31343, 7), value = 4 }, +} + +local boats = { + [1] = { + boatPosition = Position(33373, 31309, 7), + value = 3, + toPosition = Position(33382, 31292, 7), + message = "A small island emerges out of the mist as you row towards a tiny light inside a dark, forehoding chapel.", + }, + [2] = { + boatPosition = Position(33382, 31294, 7), + value = 3, + toPosition = Position(33374, 31310, 7), + message = "Your heart lightens as you return from the gloomy isle.", + }, + [3] = { + boatPosition = Position(33344, 31348, 7), + value = 3, + toPosition = Position(33326, 31352, 7), + }, + [4] = { + boatPosition = Position(33328, 31352, 7), + value = 3, + toPosition = Position(33346, 31348, 7), + }, +} + +local actions_falcon_doors = Action() + +function actions_falcon_doors.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if item:getActionId() == 4920 then + for _, p in pairs(doors) do + local door = p.doorPosition + local value = p.value + if (item:getPosition() == door) and not (Tile(item:getPosition()):getTopCreature()) then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.KillingBosses) >= value then + player:teleportTo(toPosition, true) + item:transform(item.itemid + 1) + else + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The door seems to be sealed against unwanted intruders.") + end + end + end + elseif item:getActionId() == 4921 then + for _, p in pairs(boats) do + local boat = p.boatPosition + local value = p.value + local toPos = p.toPosition + local message = p.message + if item:getPosition() == boat then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.KillingBosses) >= value then + player:teleportTo(toPos, true) + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + if message then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, message) + end + else + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can not use this boat yet.") + end + end + end + end + + return true +end + +actions_falcon_doors:aid(4920, 4921) +actions_falcon_doors:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/the_order_of_the_falcon/actions_entrance.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_order_of_the_falcon/actions_entrance.lua new file mode 100644 index 00000000000..d7601f9ba68 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_order_of_the_falcon/actions_entrance.lua @@ -0,0 +1,28 @@ +local actions_entrance = Action() + +function actions_entrance.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if (target == nil) or not target:isItem() then + return false + end + + local currentTime = os.date("*t") + local currentMinute = currentTime.min + + local isNightTime = (currentMinute >= 45 or currentMinute < 15) + + if isNightTime then + if target:getPosition() == Position(33201, 31763, 1) then + player:teleportTo(Position(33356, 31309, 4), true) + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Once more you mix the chalk with a drop of your blood and a bit of water and renew the symbol on the floor...") + item:transform(2873, 0) + end + else + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use this entrance during the night.") + end + + return true +end + +actions_entrance:id(28468) +actions_entrance:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/the_order_of_the_falcon/actions_oberonLever.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_order_of_the_falcon/actions_oberonLever.lua new file mode 100644 index 00000000000..c6e6841f714 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_order_of_the_falcon/actions_oberonLever.lua @@ -0,0 +1,39 @@ +local actions_falcon_oberon_lever = Action() + +function actions_falcon_oberon_lever.onUse(player, item, fromPosition, itemEx, toPosition) + local bossName = "Grand Master Oberon" + local playersTable = {} + local fromPosition_ = Position(33356, 31311, 9) + local toPosition_ = Position(33376, 31328, 9) + local exitPosition = Position(33297, 31285, 9) + + if item:getId() == 2772 then + if doCheckBossRoom(player:getId(), bossName, fromPosition_, toPosition_) then + for i = 33362, 33366, 1 do + local newpos = Position(i, 31344, 9) + local nplayer = Tile(newpos):getTopCreature() + if nplayer and nplayer:isPlayer() then + nplayer:teleportTo(Position(33364, 31322, 9), true) + nplayer:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + table.insert(playersTable, nplayer:getId()) + nplayer:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.OberonTimer, os.time() + 20 * 60 * 60) + end + end + local oberon = Game.createMonster("Grand Master Oberon", Position(33365, 31318, 9)) + if oberon then + oberon:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.OberonHeal, 0) + end + Game.setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.OberonSay, -1) + Game.createNpc("Oberon's Spite", Position(33361, 31320, 9)) + Game.createNpc("Oberon's Ire", Position(33367, 31320, 9)) + Game.createNpc("Oberon's Bile", Position(33361, 31316, 9)) + Game.createNpc("Oberon's Hate", Position(33367, 31316, 9)) + addEvent(kickPlayersAfterTime, 30 * 60 * 1000, playersTable, fromPosition_, toPosition_, exitPosition) + end + end + + return true +end + +actions_falcon_oberon_lever:aid(4922) +actions_falcon_oberon_lever:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/the_order_of_the_falcon/movements_bossEntrance.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_order_of_the_falcon/movements_bossEntrance.lua new file mode 100644 index 00000000000..0f000b94e7c --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_order_of_the_falcon/movements_bossEntrance.lua @@ -0,0 +1,53 @@ +local config = { + entrance = Position(33310, 31325, 8), + exit = Position(33329, 31333, 9), +} + +local movements_falcon_bossEntrance = MoveEvent() + +function movements_falcon_bossEntrance.onStepIn(creature, item, position, fromPosition) + if not creature:isPlayer() then + return false + end + + local entrance = config.entrance + local exit = config.exit + + if item:getActionId() == 4920 then + if creature then + if item:getPosition() == entrance then + if creature:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.KillingBosses) >= 5 then + creature:teleportTo(Position(exit.x, exit.y - 2, exit.z), true) + else + creature:teleportTo(Position(entrance.x - 2, entrance.y, entrance.z), true) + end + elseif item:getPosition() == exit then + creature:teleportTo(Position(entrance.x - 2, entrance.y, entrance.z), true) + end + end + elseif item:getActionId() == 4921 then + local blockedPositions = { + Position(33328, 31352, 7), + Position(33373, 31309, 7), + Position(33382, 31294, 7), + Position(33344, 31348, 7), + } + if creature then + if isInArray(blockedPositions, position) then + return true + else + if creature:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.OberonTimer) <= os.time() then + creature:teleportTo(Position(33363, 31341, 9), true) + else + creature:teleportTo(fromPosition, true) + creature:sendCancelMessage("You are still exhausted from your last battle.") + end + end + end + end + + return true +end + +movements_falcon_bossEntrance:aid(4920, 4921) +movements_falcon_bossEntrance:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/actions_chests.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/actions_chests.lua new file mode 100644 index 00000000000..b11d6209acb --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/actions_chests.lua @@ -0,0 +1,31 @@ +local chests = { + [1] = { position = Position(32970, 32314, 9), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.FirstChest, reward = 28538, questlog = true }, + [2] = { position = Position(32980, 32308, 9), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.SecondChest, reward = 28536, questlog = true }, + [3] = { position = Position(32955, 32282, 10), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.ThirdChest, reward = 28537, questlog = false }, + [4] = { position = Position(32983, 32289, 10), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.FourthChest, reward = 28535, questlog = false }, + [5] = { position = Position(32944, 32309, 8), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.FifthChest, reward = 28818, questlog = true }, +} + +local actions_desert_chests = Action() + +function actions_desert_chests.onUse(player, item, fromPosition, target, toPosition, isHotkey) + for _, k in pairs(chests) do + if toPosition == k.position then + if player:getStorageValue(k.storage) ~= 1 then + if k.questlog then + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline, player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline) + 1) + end + player:addItem(k.reward, 1) + player:setStorageValue(k.storage, 1) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found a " .. ItemType(k.reward):getName():lower() .. ".") + else + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "It is empty.") + end + end + end + + return true +end + +actions_desert_chests:aid(4931) +actions_desert_chests:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/actions_doors.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/actions_doors.lua new file mode 100644 index 00000000000..be148a3c40e --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/actions_doors.lua @@ -0,0 +1,30 @@ +local doors = { + [1] = { doorPosition = Position(32963, 32319, 9), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.PuzzleSqm, value = 39 }, + [2] = { doorPosition = Position(32955, 32304, 9), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.PuzzleSqm, value = 40 }, + [3] = { doorPosition = Position(32984, 32314, 9), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.FirstChest, value = 1 }, + [4] = { doorPosition = Position(32968, 32324, 9), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.SecondChest, value = 1 }, + [5] = { doorPosition = Position(32978, 32290, 10), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.EatenFood, value = 4 }, + [6] = { doorPosition = Position(32963, 32297, 8), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline, value = 7 }, + [7] = { doorPosition = Position(32963, 32299, 8), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline, value = 7 }, + [8] = { doorPosition = Position(32963, 32301, 8), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline, value = 7 }, + [9] = { doorPosition = Position(32963, 32303, 8), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline, value = 7 }, +} + +local actions_desert_doors = Action() + +function actions_desert_doors.onUse(player, item, fromPosition, target, toPosition, isHotkey) + for _, p in pairs(doors) do + if (item:getPosition() == p.doorPosition) and not (Tile(item:getPosition()):getTopCreature()) and isInArray({ 8361, 8355, 20450 }, item.itemid) then + if player:getStorageValue(p.storage) >= p.value then + player:teleportTo(toPosition, true) + item:transform(item.itemid + 1) + else + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The door seems to be sealed against unwanted intruders.") + end + end + end + return true +end + +actions_desert_doors:aid(4930) +actions_desert_doors:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/actions_totems.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/actions_totems.lua new file mode 100644 index 00000000000..86fd5d2edca --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/actions_totems.lua @@ -0,0 +1,54 @@ +local totems = { + { toPosition = Position(32945, 32292, 8), targetId = 28531, toId = 28532, itemId = 28535, storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.FirstTotem }, + { toPosition = Position(32947, 32292, 8), targetId = 28527, toId = 28528, itemId = 28537, storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.SecondTotem }, + { toPosition = Position(32949, 32292, 8), targetId = 28533, toId = 28534, itemId = 28538, storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.ThirdTotem }, + { toPosition = Position(32951, 32292, 8), targetId = 28529, toId = 28530, itemId = 28536, storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.FourthTotem }, +} + +local function isQuestComplete(cid) + local player = Player(cid) + if player then + for _, s in pairs(totems) do + if player:getStorageValue(s.storage) ~= 1 then + return false + end + end + end + return true +end + +local function revert(old, new, position) + local totem = Tile(position):getItemById(new) + if totem then + totem:transform(old) + end +end + +local actions_desert_totems = Action() + +function actions_desert_totems.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline) == 6 then + for _, k in pairs(totems) do + if toPosition == k.toPosition and item.itemid == k.itemId and target.itemid == k.targetId then + if player:getStorageValue(k.storage) < 1 then + toPosition:sendMagicEffect(CONST_ME_HITAREA) + target:transform(k.toId) + item:remove(1) + player:setStorageValue(k.storage, 1) + addEvent(revert, 15 * 1000, k.targetId, k.toId, toPosition) + end + end + if isQuestComplete(player:getId()) then + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline, 7) + player:say("Access granted!", TALKTYPE_MONSTER_SAY) + end + end + end + return true +end + +for _, totem in pairs(totems) do + actions_desert_totems:id(totem.itemId) +end + +actions_desert_totems:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/movements_color_puzzle.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/movements_color_puzzle.lua new file mode 100644 index 00000000000..d7b635a9857 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/movements_color_puzzle.lua @@ -0,0 +1,41 @@ +local color = { + [1] = { itemid = 4858, position = Position(32945, 32288, 10), value = 2, storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.RedColor }, + [2] = { itemid = 5581, position = Position(32948, 32288, 10), value = 1, storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.GreenColor }, + [3] = { itemid = 8695, position = Position(32951, 32288, 10), value = 3, storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.BlueColor }, +} + +local movements_desert_color_puzzle = MoveEvent() + +function movements_desert_color_puzzle.onStepIn(creature, item, position, fromPosition) + if not creature:isPlayer() then + return false + end + local player = Player(creature:getId()) + if player then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Darashia.ColorPuzzle) < 1 then + for _, k in pairs(color) do + if item.itemid == k.itemid and position == k.position then + if player:getStorageValue(k.storage) < k.value then + if player:getStorageValue(k.storage) < 0 then + player:setStorageValue(k.storage, 0) + end + player:setStorageValue(k.storage, player:getStorageValue(k.storage) + 1) + else + for i = 1, #color do + player:setStorageValue(color[i].storage, 0) + end + end + end + end + if player:getStorageValue(color[1].storage) == color[1].value and player:getStorageValue(color[2].storage) == color[2].value and player:getStorageValue(color[3].storage) == color[3].value then + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Darashia.ColorPuzzle, 1) + player:say("Access granted!", TALKTYPE_MONSTER_SAY) + end + end + end + + return true +end + +movements_desert_color_puzzle:aid(4933) +movements_desert_color_puzzle:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/movements_sacrifice_foods.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/movements_sacrifice_foods.lua new file mode 100644 index 00000000000..df06751adcd --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/movements_sacrifice_foods.lua @@ -0,0 +1,237 @@ +local foods = { + 3606, + 3250, + 3577, + 21145, + 21144, + 21143, + 3578, + 3579, + 23535, + 23545, + 3580, + 3581, + 3582, + 3583, + 3584, + 3585, + 3586, + 3587, + 3588, + 3589, + 3590, + 3591, + 3592, + 904, + 3593, + 3594, + 3595, + 3596, + 3597, + 3598, + 3599, + 3600, + 3601, + 3602, + 3607, + 3723, + 3724, + 3725, + 3726, + 3727, + 3728, + 3729, + 3730, + 3731, + 3732, + 5096, + 20310, + 5678, + 6125, + 6277, + 6278, + 6392, + 6393, + 6500, + 6541, + 6542, + 6543, + 6544, + 6545, + 6569, + 6574, + 7158, + 7159, + 229, + 7373, + 7374, + 7375, + 7376, + 7377, + 836, + 841, + 901, + 169, + 8010, + 8011, + 8012, + 8013, + 8014, + 8015, + 8016, + 8017, + 8019, + 8177, + 8197, + 9537, + 10329, + 10453, + 10219, + 11459, + 11460, + 11461, + 11462, + 11681, + 11682, + 11683, + 12310, + 13992, + 14084, + 14085, + 14681, + 15795, + 16103, + 17457, + 17820, + 17821, + 21146, + 22187, + 22185, + 24382, + 24383, + 24396, + 24948, + 25692, + 30198, + 30202, + 31560, + 32069, + 37530, + 37531, + 37532, + 37533, +} + +local pillars = { + { position = Position(32963, 32280, 10), itemPosition = Position(32961, 32280, 10) }, + { position = Position(32963, 32282, 10), itemPosition = Position(32961, 32282, 10) }, + { position = Position(32963, 32284, 10), itemPosition = Position(32961, 32284, 10) }, + { position = Position(32963, 32286, 10), itemPosition = Position(32961, 32286, 10) }, +} + +local storageValue = Storage.Quest.U11_80.TheSecretLibrary.Darashia.EatenFood +local transformTime = 5 * 60 * 1000 + +local perimeter1Min = Position(32964, 32278, 10) +local perimeter1Max = Position(32966, 32290, 10) + +local perimeter2Min = Position(32961, 32288, 10) +local perimeter2Max = Position(32963, 32290, 10) + +local function isFood(itemid) + for _, foodid in ipairs(foods) do + if foodid == itemid then + return true + end + end + return false +end + +local function findPlayerInPerimeter() + -- Verificar ambos os perímetros + for x = perimeter1Min.x, perimeter1Max.x do + for y = perimeter1Min.y, perimeter1Max.y do + local tile = Tile(Position(x, y, perimeter1Min.z)) + if tile then + local player = tile:getTopCreature() + if player and player:isPlayer() then + return player + end + end + end + end + for x = perimeter2Min.x, perimeter2Max.x do + for y = perimeter2Min.y, perimeter2Max.y do + local tile = Tile(Position(x, y, perimeter2Min.z)) + if tile then + local player = tile:getTopCreature() + if player and player:isPlayer() then + return player + end + end + end + end + return nil +end + +local foodSacrifice = MoveEvent() + +function foodSacrifice.onAddItem(moveitem, tileitem, position) + if not isFood(moveitem.itemid) then + return true + end + + for _, pillar in ipairs(pillars) do + if position == pillar.position and tileitem:getActionId() == 4932 then + local itemToTransform = Tile(pillar.itemPosition):getItemById(27987) + if itemToTransform then + -- Transformar item 27987 em 27989 + itemToTransform:transform(27989) + + -- Remover o item de comida e adicionar o item 24490 em seu lugar + local foodPosition = moveitem:getPosition() + moveitem:remove() + Game.createItem(24490, 1, foodPosition) + + addEvent(function() + local revertedTile = Tile(pillar.itemPosition) + if revertedTile then + local itemToRevert = revertedTile:getItemById(27989) + if itemToRevert then + itemToRevert:transform(27987) + end + end + -- Reverter o item 24490 de volta (remover) + local revertFoodItem = Tile(foodPosition):getItemById(24490) + if revertFoodItem then + revertFoodItem:remove() + end + end, transformTime) + + local player = findPlayerInPerimeter() + if player then + local currentProgress = player:getStorageValue(storageValue) + if currentProgress < 0 then + currentProgress = 0 + end + if currentProgress < 4 then + player:setStorageValue(storageValue, currentProgress + 1) + end + if player:getStorageValue(storageValue) == 4 then + if player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Questlog) < 6 then + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline, 6) + end + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have completed the food offering ritual!") + end + end + + return true + end + end + end + return true +end + +foodSacrifice:type("additem") +foodSacrifice:aid(4932) +foodSacrifice:register() diff --git a/data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/movements_teleportTo.lua b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/movements_teleportTo.lua new file mode 100644 index 00000000000..905dcd0e743 --- /dev/null +++ b/data-otservbr-global/scripts/quests/the_secret_library_quest/the_parth_of_defiances_desert/movements_teleportTo.lua @@ -0,0 +1,187 @@ +local lastTeleport = Position(32963, 32312, 8) +local scorpionTimer = Storage.Quest.U11_80.TheSecretLibrary.Darashia.ScorpionTimer +local scorpionPosition = Position(32951, 32309, 8) +local fromPos = Position(32943, 32303, 8) +local toPos = Position(32960, 32315, 8) +local exit = Position(32963, 32307, 8) +local bossName = "furious scorpion" + +local teleports = { + [1] = { position = Position(33110, 32385, 7), destination = Position(32958, 32320, 8), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline, value = 1 }, + [2] = { position = Position(32958, 32322, 8), destination = Position(33110, 32387, 7) }, + [3] = { position = Position(32955, 32288, 10), destination = Position(32955, 32284, 10), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.ColorPuzzle, value = 1, challenge = true }, + [4] = { position = Position(32942, 32283, 10), destination = Position(32942, 32288, 10) }, + [5] = { position = Position(32945, 32313, 8), destination = Position(33101, 32381, 7) }, +} + +local tileAccess = { + [1] = { position = Position(32974, 32296, 9), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline, value = 2 }, + [2] = { position = Position(32967, 32319, 9), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline, value = 3 }, + [3] = { position = Position(32978, 32290, 10), storage = Storage.Quest.U11_80.TheSecretLibrary.Darashia.Questline, value = 6 }, +} + +local puzzle = { + { position = Position(32965, 32310, 9), value = 1 }, + { position = Position(32965, 32309, 9), value = 2 }, + { position = Position(32964, 32309, 9), value = 3 }, + { position = Position(32963, 32309, 9), value = 4 }, + { position = Position(32962, 32309, 9), value = 5 }, + { position = Position(32961, 32309, 9), value = 6 }, + { position = Position(32960, 32309, 9), value = 7 }, + { position = Position(32959, 32309, 9), value = 8 }, + { position = Position(32959, 32310, 9), value = 9 }, + { position = Position(32959, 32311, 9), value = 10 }, + { position = Position(32958, 32311, 9), value = 11 }, + { position = Position(32957, 32311, 9), value = 12 }, + { position = Position(32956, 32311, 9), value = 13 }, + { position = Position(32956, 32310, 9), value = 14 }, + { position = Position(32955, 32310, 9), value = 15 }, + { position = Position(32954, 32310, 9), value = 16 }, + { position = Position(32953, 32310, 9), value = 17 }, + { position = Position(32953, 32311, 9), value = 18 }, + { position = Position(32953, 32312, 9), value = 19 }, + { position = Position(32953, 32313, 9), value = 20 }, + { position = Position(32953, 32314, 9), value = 21 }, + { position = Position(32954, 32314, 9), value = 22 }, + { position = Position(32955, 32314, 9), value = 23 }, + { position = Position(32956, 32314, 9), value = 24 }, + { position = Position(32956, 32315, 9), value = 25 }, + { position = Position(32956, 32316, 9), value = 26 }, + { position = Position(32956, 32317, 9), value = 27 }, + { position = Position(32957, 32317, 9), value = 28 }, + { position = Position(32957, 32318, 9), value = 29 }, + { position = Position(32957, 32319, 9), value = 30 }, + { position = Position(32958, 32319, 9), value = 31 }, + { position = Position(32959, 32319, 9), value = 32 }, + { position = Position(32960, 32319, 9), value = 33 }, + { position = Position(32960, 32318, 9), value = 34 }, + { position = Position(32960, 32317, 9), value = 35 }, + { position = Position(32961, 32317, 9), value = 36 }, + { position = Position(32962, 32317, 9), value = 37 }, + { position = Position(32962, 32318, 9), value = 38 }, + { position = Position(32962, 32319, 9), value = 39 }, +} + +local function startBattle(pid, position, b_name, middle) + local player = Player(pid) + if player then + player:teleportTo(position) + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + player:say("You have to master this very last challenge within 5 minutes!", TALKTYPE_MONSTER_SAY) + local monster = Game.createMonster(b_name, middle) + end +end + +local function resetRoom(position, name) + local spec = Game.getSpectators(position, false, false, 5, 5, 5, 5) + + for _, c in pairs(spec) do + if c and c:isPlayer() then + return false + end + end + + for _, c in pairs(spec) do + if c and c:getName():lower() == name then + c:remove() + end + end + + return true +end + +local movements_desert_teleportTo = MoveEvent() + +function movements_desert_teleportTo.onStepIn(creature, item, position, fromPosition) + if not creature:isPlayer() then + return false + end + + local player = Player(creature:getId()) + + if player then + if item.actionid == 4930 then + if position == lastTeleport then + if resetRoom(scorpionPosition, bossName) then + if player:getStorageValue(scorpionTimer) < os.time() then + startBattle(player:getId(), Position(32958, 32309, 8), bossName, scorpionPosition) + addEvent(function(cid) + local p = Player(cid) + if p then + if p:getPosition():isInRange(fromPos, toPos) then + p:teleportTo(exit) + end + end + end, 5 * 1000 * 60, player:getId()) + addEvent(function(cid) + local p = Player(cid) + if p then + p:setStorageValue(scorpionTimer, os.time() + 20 * 60 * 60) + end + end, 1000, player:getId()) + else + player:sendCancelMessage("You are still exhausted from your last battle.") + player:teleportTo(fromPosition, true) + end + else + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You must wait. Someone is challenging Furious Scorpion now.") + player:teleportTo(fromPosition, true) + end + else + for _, k in pairs(teleports) do + if position == k.position then + if k.storage then + if player:getStorageValue(k.storage) >= k.value then + player:teleportTo(k.destination) + else + if k.challenge then + player:say("You have to master the challenge first!", TALKTYPE_MONSTER_SAY) + end + player:teleportTo(fromPosition, true) + end + else + player:teleportTo(k.destination) + end + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + end + end + end + elseif item.actionid == 4931 then + local tileItem = Tile(position):getItemById(item.itemid) + if tileItem and (tileItem:getId() == 231 or tileItem:getId() == 28318 or tileItem:getId() == 28319 or tileItem:getId() == 28320 or tileItem:getId() == 28322 or tileItem:getId() == 28323) then + for _, k in pairs(puzzle) do + if position == k.position then + local currentStep = player:getStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Darashia.PuzzleSqm) + if currentStep == -1 then + currentStep = 0 + end + if currentStep == k.value - 1 then + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Darashia.PuzzleSqm, currentStep + 1) + player:getPosition():sendMagicEffect(CONST_ME_SOUND_WHITE) + + if currentStep + 1 == #puzzle then + player:say("Access granted!", TALKTYPE_MONSTER_SAY) + end + else + player:setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.Darashia.PuzzleSqm, 0) + player:getPosition():sendMagicEffect(CONST_ME_SOUND_RED) + end + end + end + end + elseif item.actionid == 4932 then + for _, k in pairs(tileAccess) do + if position == k.position then + if player:getStorageValue(k.storage) < k.value then + player:setStorageValue(k.storage, player:getStorageValue(k.storage) + 1) + end + end + end + end + end + + return true +end + +movements_desert_teleportTo:aid(4930, 4931, 4932) +movements_desert_teleportTo:register() diff --git a/data-otservbr-global/scripts/quests/threatened_dreams/action-fairies_release.lua b/data-otservbr-global/scripts/quests/threatened_dreams/action_fairies_release.lua similarity index 100% rename from data-otservbr-global/scripts/quests/threatened_dreams/action-fairies_release.lua rename to data-otservbr-global/scripts/quests/threatened_dreams/action_fairies_release.lua diff --git a/data-otservbr-global/scripts/quests/threatened_dreams/action-moon_mirror.lua b/data-otservbr-global/scripts/quests/threatened_dreams/action_moon_mirror.lua similarity index 100% rename from data-otservbr-global/scripts/quests/threatened_dreams/action-moon_mirror.lua rename to data-otservbr-global/scripts/quests/threatened_dreams/action_moon_mirror.lua diff --git a/data-otservbr-global/scripts/quests/threatened_dreams/action-poacher_book.lua b/data-otservbr-global/scripts/quests/threatened_dreams/action_poacher_book.lua similarity index 100% rename from data-otservbr-global/scripts/quests/threatened_dreams/action-poacher_book.lua rename to data-otservbr-global/scripts/quests/threatened_dreams/action_poacher_book.lua diff --git a/data-otservbr-global/scripts/quests/threatened_dreams/action-poacher_notes.lua b/data-otservbr-global/scripts/quests/threatened_dreams/action_poacher_notes.lua similarity index 100% rename from data-otservbr-global/scripts/quests/threatened_dreams/action-poacher_notes.lua rename to data-otservbr-global/scripts/quests/threatened_dreams/action_poacher_notes.lua diff --git a/data-otservbr-global/scripts/quests/threatened_dreams/action-starlight_vial.lua b/data-otservbr-global/scripts/quests/threatened_dreams/action_starlight_vial.lua similarity index 100% rename from data-otservbr-global/scripts/quests/threatened_dreams/action-starlight_vial.lua rename to data-otservbr-global/scripts/quests/threatened_dreams/action_starlight_vial.lua diff --git a/data-otservbr-global/scripts/quests/threatened_dreams/action-sun_catcher.lua b/data-otservbr-global/scripts/quests/threatened_dreams/action_sun_catcher.lua similarity index 100% rename from data-otservbr-global/scripts/quests/threatened_dreams/action-sun_catcher.lua rename to data-otservbr-global/scripts/quests/threatened_dreams/action_sun_catcher.lua diff --git a/data-otservbr-global/scripts/quests/threatened_dreams/action-whelp_fur.lua b/data-otservbr-global/scripts/quests/threatened_dreams/action_whelp_fur.lua similarity index 100% rename from data-otservbr-global/scripts/quests/threatened_dreams/action-whelp_fur.lua rename to data-otservbr-global/scripts/quests/threatened_dreams/action_whelp_fur.lua diff --git a/data-otservbr-global/scripts/quests/threatened_dreams/event-raven_herb_bush.lua b/data-otservbr-global/scripts/quests/threatened_dreams/event_raven_herb_bush.lua similarity index 100% rename from data-otservbr-global/scripts/quests/threatened_dreams/event-raven_herb_bush.lua rename to data-otservbr-global/scripts/quests/threatened_dreams/event_raven_herb_bush.lua diff --git a/data-otservbr-global/scripts/quests/threatened_dreams/movement-kroazur_room.lua b/data-otservbr-global/scripts/quests/threatened_dreams/movement_kroazur_room.lua similarity index 99% rename from data-otservbr-global/scripts/quests/threatened_dreams/movement-kroazur_room.lua rename to data-otservbr-global/scripts/quests/threatened_dreams/movement_kroazur_room.lua index 46b6a58bd5e..3ca23a4ce84 100644 --- a/data-otservbr-global/scripts/quests/threatened_dreams/movement-kroazur_room.lua +++ b/data-otservbr-global/scripts/quests/threatened_dreams/movement_kroazur_room.lua @@ -1,4 +1,5 @@ local ThreatenedDreams = Storage.Quest.U11_40.ThreatenedDreams + local config = { [25029] = { bossName = "Kroazur", -- boss name diff --git a/data-otservbr-global/scripts/quests/threatened_dreams/movement-poacher_notes.lua b/data-otservbr-global/scripts/quests/threatened_dreams/movement_poacher_notes.lua similarity index 100% rename from data-otservbr-global/scripts/quests/threatened_dreams/movement-poacher_notes.lua rename to data-otservbr-global/scripts/quests/threatened_dreams/movement_poacher_notes.lua diff --git a/data-otservbr-global/scripts/quests/threatened_dreams/movement-swan_feathers.lua b/data-otservbr-global/scripts/quests/threatened_dreams/movement_swan_feathers.lua similarity index 100% rename from data-otservbr-global/scripts/quests/threatened_dreams/movement-swan_feathers.lua rename to data-otservbr-global/scripts/quests/threatened_dreams/movement_swan_feathers.lua diff --git a/data-otservbr-global/scripts/spells/monster/exploding_cask.lua b/data-otservbr-global/scripts/spells/monster/exploding_cask.lua index 5448d0733ff..4af10ce98a9 100644 --- a/data-otservbr-global/scripts/spells/monster/exploding_cask.lua +++ b/data-otservbr-global/scripts/spells/monster/exploding_cask.lua @@ -5,7 +5,7 @@ combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_FIREAREA) local combatCast = Combat() -local barrelId = 23485 +local barrelId = 23486 local bombArea = { { 0, 1, 1, 1, 0 }, { 1, 1, 1, 1, 1 }, diff --git a/data-otservbr-global/scripts/world_changes/iron_servant_transformation.lua b/data-otservbr-global/scripts/world_changes/iron_servant_transformation.lua index 7da5f5d1b99..e62cd23c876 100644 --- a/data-otservbr-global/scripts/world_changes/iron_servant_transformation.lua +++ b/data-otservbr-global/scripts/world_changes/iron_servant_transformation.lua @@ -6,7 +6,7 @@ ironServantTransformation.monsterOnSpawn = function(monster, position) end local chance = math.random(100) - if Game.getStorageValue(GlobalStorage.ForgottenKnowledge.MechanismDiamond) >= 1 and Game.getStorageValue(GlobalStorage.ForgottenKnowledge.MechanismGolden) >= 1 then + if Game.getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.MechanismDiamond) >= 1 and Game.getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.MechanismGolden) >= 1 then if chance > 30 then local monsterType = math.random(2) == 1 and "diamond servant replica" or "golden servant replica" Game.createMonster(monsterType, monster:getPosition(), false, true) @@ -15,13 +15,13 @@ ironServantTransformation.monsterOnSpawn = function(monster, position) return end - if Game.getStorageValue(GlobalStorage.ForgottenKnowledge.MechanismDiamond) >= 1 and chance > 30 then + if Game.getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.MechanismDiamond) >= 1 and chance > 30 then Game.createMonster("diamond servant replica", monster:getPosition(), false, true) monster:remove() return end - if Game.getStorageValue(GlobalStorage.ForgottenKnowledge.MechanismGolden) >= 1 and chance > 30 then + if Game.getStorageValue(Storage.Quest.U11_02.ForgottenKnowledge.MechanismGolden) >= 1 and chance > 30 then Game.createMonster("golden servant replica", monster:getPosition(), false, true) monster:remove() end diff --git a/data-otservbr-global/startup/tables/chest.lua b/data-otservbr-global/startup/tables/chest.lua index d5b28c633ed..c1f15358825 100644 --- a/data-otservbr-global/startup/tables/chest.lua +++ b/data-otservbr-global/startup/tables/chest.lua @@ -181,14 +181,13 @@ ChestUnique = { reward = { { 2970, 1 } }, storage = Storage.Quest.Key.ID3899, }, + -- The Secret Library Quest [5012] = { isKey = true, itemId = 23740, itemPos = { x = 33377, y = 31321, z = 1 }, reward = { { 2969, 1 } }, storage = Storage.Quest.Key.ID0909, - timerStorage = Storage.TheSecretLibrary.FalconBastionChestsTimer.Key0909, - time = 24, -- hour }, -- Bear Room Quest (rookgaard) key 4601 [5013] = { @@ -1080,12 +1079,13 @@ ChestUnique = { weight = 15.00, storage = Storage.Quest.U8_54.TomesOfKnowledge.TopTower, }, + -- The Secret Library Quest [6105] = { itemId = 23741, itemPos = { x = 33352, y = 31318, z = 7 }, randomReward = { { 9081, 1 }, { 28821, 1 }, { 28823, 1 }, { 9058, 1 }, { 6299, 1 }, { 3052, 1 }, { 3035, 10 } }, reward = { { nil, nil } }, - timerStorage = Storage.Quest.U11_80.TheSecretLibrary.FalconBastionChestsTimer.Coast, + timerStorage = Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.ChestsTimer.Coast, time = 24, -- hour }, [6106] = { @@ -1093,7 +1093,7 @@ ChestUnique = { itemPos = { x = 33384, y = 31285, z = 7 }, randomReward = { { 9081, 1 }, { 28821, 1 }, { 28823, 1 }, { 9058, 1 }, { 6299, 1 }, { 3052, 1 }, { 3035, 10 } }, reward = { { nil, nil } }, - timerStorage = Storage.Quest.U11_80.TheSecretLibrary.FalconBastionChestsTimer.Island, + timerStorage = Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.ChestsTimer.Island, time = 24, -- hour }, [6107] = { @@ -1101,7 +1101,7 @@ ChestUnique = { itemPos = { x = 33366, y = 31323, z = 5 }, randomReward = { { 9081, 1 }, { 28821, 1 }, { 28823, 1 }, { 9058, 1 }, { 6299, 1 }, { 3052, 1 }, { 3035, 10 } }, reward = { { nil, nil } }, - timerStorage = Storage.Quest.U11_80.TheSecretLibrary.FalconBastionChestsTimer.ThroneHall, + timerStorage = Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.ChestsTimer.ThroneHall, time = 24, -- hour }, [6108] = { @@ -1109,7 +1109,7 @@ ChestUnique = { itemPos = { x = 33374, y = 31340, z = 4 }, randomReward = { { 9081, 1 }, { 28821, 1 }, { 28823, 1 }, { 9058, 1 }, { 6299, 1 }, { 3052, 1 }, { 3035, 10 } }, reward = { { nil, nil } }, - timerStorage = Storage.Quest.U11_80.TheSecretLibrary.FalconBastionChestsTimer.Shortcut, + timerStorage = Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.ChestsTimer.Shortcut, time = 24, -- hour }, [6109] = { @@ -1117,7 +1117,7 @@ ChestUnique = { itemPos = { x = 33324, y = 31268, z = 8 }, randomReward = { { 9081, 1 }, { 28821, 1 }, { 28823, 1 }, { 9058, 1 }, { 6299, 1 }, { 3052, 1 }, { 3035, 10 } }, reward = { { nil, nil } }, - timerStorage = Storage.Quest.U11_80.TheSecretLibrary.FalconBastionChestsTimer.LowerBastion, + timerStorage = Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.ChestsTimer.LowerBastion, time = 24, -- hour }, [6110] = { @@ -1125,7 +1125,7 @@ ChestUnique = { itemPos = { x = 33308, y = 31304, z = 9 }, randomReward = { { 9081, 1 }, { 28821, 1 }, { 28823, 1 }, { 9058, 1 }, { 6299, 1 }, { 3052, 1 }, { 3035, 10 } }, reward = { { nil, nil } }, - timerStorage = Storage.Quest.U11_80.TheSecretLibrary.FalconBastionChestsTimer.UndergroundBastion, + timerStorage = Storage.Quest.U11_80.TheSecretLibrary.FalconBastion.ChestsTimer.UndergroundBastion, time = 24, -- hour }, -- Blood Herb Quest @@ -2621,6 +2621,160 @@ ChestUnique = { weight = 1.00, storage = Storage.Quest.U10_90.FerumbrasAscension.VampireTeeth, }, + -- Forgotten Knowledge Quest + [6310] = { + useKV = true, + itemId = 23736, + itemPos = { x = 32825, y = 31664, z = 9 }, + reward = { { 23734, 1 } }, + weight = 6.00, + questName = "GhostsilverLanternQuest", + }, + [6311] = { + useKV = true, + itemId = 21858, + itemPos = { x = 32883, y = 31686, z = 10 }, + reward = { { 23732, 1 } }, + weight = 4.00, + questName = "PaintingOfAGirlQuest", + }, + [6312] = { + useKV = true, + itemId = 9253, + itemPos = { x = 33026, y = 31662, z = 14 }, + reward = { { 24964, 1 } }, + weight = 3.00, + questName = "ImbuingCrystalQuest", + }, + -- Cults of Tibia Quest + [6313] = { + useKV = true, + itemId = 2472, + itemPos = { x = 32739, y = 31426, z = 8 }, + reward = { { 10420, 5 } }, + weight = 8.00, + questName = "PetrifiedScreamQuest", + }, + -- The Secret Library Quest + [6314] = { + itemId = 2472, + itemPos = { x = 33231, y = 32017, z = 8 }, + reward = { { 27874, 1 } }, + weight = 1.00, + storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.SampleBlood, + }, + [6315] = { + itemId = 2472, + itemPos = { x = 33212, y = 32081, z = 9 }, + reward = { { 27847, 1 } }, + weight = 2.00, + storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.BonyRod, + }, + [6316] = { + itemId = 2472, + itemPos = { x = 33342, y = 32120, z = 10 }, + reward = { { 25746, 1 } }, + weight = 2.00, + storage = Storage.Quest.U11_80.TheSecretLibrary.MoTA.BrokenCompass, + }, + [6317] = { + itemId = 28517, + itemPos = { x = 32826, y = 32772, z = 10 }, + reward = { { 28518, 1 } }, + weight = 1.00, + storage = Storage.Quest.U11_80.TheSecretLibrary.Asuras.SkeletonNotes, + }, + [6318] = { + itemId = 28522, + itemPos = { x = 32828, y = 32772, z = 10 }, + reward = { { 28490, 1 } }, + weight = 1.00, + storage = Storage.Quest.U11_80.TheSecretLibrary.Asuras.StrandHair, + }, + [6319] = { + itemId = 1992, + itemPos = { x = 32874, y = 32760, z = 10 }, + reward = { { 28476, 1 } }, + weight = 1.00, + storage = Storage.Quest.U11_80.TheSecretLibrary.Asuras.LotusKey, + }, + [6320] = { + itemId = 27490, + itemPos = { x = 32836, y = 32820, z = 10 }, + reward = { { 28477, 1 } }, + weight = 1.00, + storage = Storage.Quest.U11_80.TheSecretLibrary.Asuras.EyeKey, + }, + [6321] = { + itemId = 6560, + itemPos = { x = 32872, y = 32817, z = 10 }, + reward = { { 28515, 1 } }, + weight = 1.00, + storage = Storage.Quest.U11_80.TheSecretLibrary.Asuras.ScribbledNotes, + }, + [6322] = { + itemId = 4077, + itemPos = { x = 32877, y = 32795, z = 11 }, + reward = { { 28491, 1 } }, + weight = 9.00, + storage = Storage.Quest.U11_80.TheSecretLibrary.Asuras.EbonyPiece, + }, + [6323] = { + itemId = 23741, + itemPos = { x = 32833, y = 32759, z = 11 }, + reward = { { 28710, 1 } }, + weight = 1.00, + storage = Storage.Quest.U11_80.TheSecretLibrary.Asuras.PeacockBallad, + }, + [6324] = { + itemId = 9794, + itemPos = { x = 32890, y = 32768, z = 9 }, + reward = { { 28494, 1 } }, + weight = 3.00, + storage = Storage.Quest.U11_80.TheSecretLibrary.Asuras.SilverChimes, + }, + [6325] = { + itemId = 23741, + itemPos = { x = 32852, y = 32744, z = 11 }, + reward = { { 28489, 1 } }, + weight = 20.00, + storage = Storage.Quest.U11_80.TheSecretLibrary.Asuras.BlackSkull, + }, + [6326] = { + itemId = 28828, + itemPos = { x = 32013, y = 32447, z = 8 }, + reward = { { 28650, 1 } }, + weight = 2.00, + storage = Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Parchment, + }, + [6327] = { + itemId = 2523, + itemPos = { x = 32096, y = 31757, z = 8 }, + reward = { { 675, 2 } }, + weight = 1.00, + storage = Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Sapphire, + }, + [6328] = { + itemId = 4242, + itemPos = { x = 32460, y = 32934, z = 8 }, + reward = { { 3483, 1 } }, + weight = 9.00, + storage = Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Fishing, + }, + [6329] = { + itemId = 6560, + itemPos = { x = 32460, y = 32940, z = 7 }, + reward = { { 3457, 1 } }, + weight = 35.00, + storage = Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Shovel, + }, + [6330] = { + itemId = 5951, + itemPos = { x = 32025, y = 32469, z = 7 }, + reward = { { 28707, 1 } }, + weight = 6.00, + storage = Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Hawser, + }, -- Reward of others scrips files (varied rewards) -- The First dragon Quest -- Treasure chests (data\scripts\actions\quests\first_dragon\treasure_chests.lua) diff --git a/data-otservbr-global/startup/tables/door_quest.lua b/data-otservbr-global/startup/tables/door_quest.lua index 3e5888b7399..374f3c9a61f 100644 --- a/data-otservbr-global/startup/tables/door_quest.lua +++ b/data-otservbr-global/startup/tables/door_quest.lua @@ -70,26 +70,6 @@ QuestDoorAction = { { x = 33569, y = 31951, z = 14 }, }, }, - -- Secret library quest door - [Storage.TheSecretLibrary.Mota] = { - itemId = false, - itemPos = { - { x = 33208, y = 32071, z = 8 }, - { x = 33246, y = 32122, z = 8 }, - }, - }, - [Storage.TheSecretLibrary.MotaDoor] = { - itemId = false, - itemPos = { { x = 33208, y = 32074, z = 8 } }, - }, - [Storage.TheSecretLibrary.BasinDoor] = { - itemId = false, - itemPos = { { x = 33341, y = 32117, z = 10 } }, - }, - [Storage.TheSecretLibrary.SkullDoor] = { - itemId = false, - itemPos = { { x = 33344, y = 32120, z = 10 } }, - }, -- Koshei the deathless quest door [Storage.Quest.U8_1.KosheiTheDeathless.RewardDoor] = { itemId = false, @@ -203,57 +183,59 @@ QuestDoorAction = { itemPos = { { x = 32067, y = 31896, z = 3 } }, }, -- Cults of tibia door - [Storage.CultsOfTibia.Minotaurs.EntranceAccessDoor] = { + [Storage.Quest.U11_40.CultsOfTibia.Minotaurs.BossAccessDoor] = { itemId = false, - itemPos = { { x = 31950, y = 32501, z = 8 } }, + itemPos = { { x = 31957, y = 32468, z = 9 } }, }, - [Storage.CultsOfTibia.Minotaurs.AccessDoor] = { + [Storage.Quest.U11_40.CultsOfTibia.Minotaurs.AccessDoor] = { itemId = false, - itemPos = { { x = 31957, y = 32468, z = 9 } }, + itemPos = { { x = 31950, y = 32501, z = 8 } }, }, - [Storage.CultsOfTibia.MotA.AccessDoorInvestigation] = { + [Storage.Quest.U11_40.CultsOfTibia.MotA.AccessDoorInvestigation] = { itemId = false, itemPos = { { x = 33273, y = 32172, z = 8 } }, }, - [Storage.CultsOfTibia.MotA.AccessDoorGareth] = { + [Storage.Quest.U11_40.CultsOfTibia.MotA.AccessDoorGareth] = { itemId = false, itemPos = { { x = 33220, y = 32147, z = 9 } }, }, - [Storage.CultsOfTibia.MotA.AccessDoorDenominator] = { + [Storage.Quest.U11_40.CultsOfTibia.MotA.AccessDoorDenominator] = { itemId = false, itemPos = { { x = 33220, y = 32149, z = 9 } }, }, - [Storage.CultsOfTibia.Barkless.TrialAccessDoor] = { + [Storage.Quest.U11_40.CultsOfTibia.Barkless.TrialAccessDoor] = { itemId = false, itemPos = { { x = 32688, y = 31543, z = 9 } }, }, - [Storage.CultsOfTibia.Barkless.TarAccessDoor] = { + [Storage.Quest.U11_40.CultsOfTibia.Barkless.TarAccessDoor] = { itemId = false, - itemPos = { - { x = 32747, y = 31462, z = 8 }, - { x = 32678, y = 31506, z = 8 }, - }, + itemPos = { { x = 32747, y = 31462, z = 8 } }, }, - [Storage.CultsOfTibia.Barkless.AccessDoor] = { + [Storage.Quest.U11_40.CultsOfTibia.Barkless.SulphurAccessDoor] = { + itemId = false, + itemPos = { { x = 32678, y = 31506, z = 8 } }, + }, + [Storage.Quest.U11_40.CultsOfTibia.Barkless.AccessDoor] = { itemId = false, itemPos = { - { x = 32742, y = 31408, z = 8 }, { x = 32686, y = 31430, z = 8 }, + { x = 32746, y = 31423, z = 8 }, + { x = 32754, y = 31442, z = 8 }, }, }, - [Storage.CultsOfTibia.Barkless.BossAccessDoor] = { + [Storage.Quest.U11_40.CultsOfTibia.Barkless.BossAccessDoor] = { itemId = false, itemPos = { { x = 32672, y = 31543, z = 9 } }, }, - [Storage.CultsOfTibia.Life.AccessDoor] = { + [Storage.Quest.U11_40.CultsOfTibia.Life.AccessDoor] = { itemId = false, itemPos = { { x = 33295, y = 32271, z = 12 } }, }, - [Storage.CultsOfTibia.Misguided.AccessDoor] = { + [Storage.Quest.U11_40.CultsOfTibia.Misguided.AccessDoor] = { itemId = false, itemPos = { { x = 32508, y = 32370, z = 9 } }, }, - [Storage.CultsOfTibia.FinalBoss.AccessDoor] = { + [Storage.Quest.U11_40.CultsOfTibia.FinalBoss.AccessDoor] = { itemId = false, itemPos = { { x = 33452, y = 32241, z = 7 } }, }, @@ -794,33 +776,10 @@ QuestDoorAction = { { x = 32012, y = 31565, z = 7 }, }, }, - [Storage.TheSecretLibrary.MiniBosses.PreceptorLazare] = { - itemId = 6260, - itemPos = { { x = 33376, y = 31335, z = 3 } }, - }, - [Storage.TheSecretLibrary.LowerBastionAccess] = { - itemId = 6260, - itemPos = { - { x = 33371, y = 31349, z = 4 }, - { x = 33375, y = 31346, z = 5 }, - }, - }, - [Storage.TheSecretLibrary.UndergroundBastionAccess] = { - itemId = false, - itemPos = { - { x = 33366, y = 31343, z = 7 }, - { x = 33363, y = 31346, z = 7 }, - { x = 32191, y = 31823, z = 8 }, - }, - }, [Storage.Quest.U12_20.GraveDanger.Bosses.KingZelosDoor] = { itemId = false, itemPos = { { x = 32173, y = 31922, z = 8 } }, }, - [Storage.Quest.U11_80.TheSecretLibrary.ScourgeOfOblivionDoor] = { - itemId = false, - itemPos = { { x = 32480, y = 32591, z = 15 } }, - }, [Storage.Quest.U12_70.AdventuresOfGalthen.AccessDoor] = { itemId = false, itemPos = { { x = 32466, y = 32494, z = 8 } }, diff --git a/data-otservbr-global/startup/tables/item.lua b/data-otservbr-global/startup/tables/item.lua index ed01d30aeed..5868efcd31b 100644 --- a/data-otservbr-global/startup/tables/item.lua +++ b/data-otservbr-global/startup/tables/item.lua @@ -556,6 +556,369 @@ ItemAction = { { x = 32994, y = 32442, z = 11 }, }, }, + -- The Secret Library Quest + [4900] = { + itemId = false, + itemPos = { + { x = 32871, y = 32510, z = 7 }, + { x = 32881, y = 32473, z = 9 }, + { x = 32881, y = 32435, z = 8 }, + { x = 33584, y = 31388, z = 13 }, + { x = 33560, y = 31395, z = 13 }, + { x = 33598, y = 31398, z = 14 }, + { x = 33602, y = 31439, z = 13 }, + { x = 33587, y = 31461, z = 14 }, + { x = 33588, y = 31461, z = 14 }, + { x = 33549, y = 31459, z = 14 }, + { x = 33577, y = 31475, z = 15 }, + { x = 33612, y = 31465, z = 15 }, + { x = 33565, y = 31423, z = 13 }, + { x = 33574, y = 31441, z = 15 }, + }, + }, + [4905] = { + itemId = false, + itemPos = { + { x = 33246, y = 32122, z = 8 }, + { x = 33208, y = 32071, z = 8 }, + { x = 33208, y = 32074, z = 8 }, + { x = 33341, y = 32117, z = 10 }, + { x = 33344, y = 32120, z = 10 }, + }, + }, + [4906] = { + itemId = false, + itemPos = { + { x = 33204, y = 32069, z = 8 }, + }, + }, + [4907] = { + itemId = false, + itemPos = { + { x = 33354, y = 32135, z = 10 }, + { x = 33354, y = 32136, z = 10 }, + { x = 33353, y = 32135, z = 10 }, + { x = 33353, y = 32136, z = 10 }, + }, + }, + [4910] = { + itemId = 28906, + itemPos = { + { x = 32953, y = 32669, z = 7 }, + }, + }, + [4911] = { + itemId = false, + itemPos = { + { x = 32962, y = 32674, z = 2 }, + { x = 32959, y = 32679, z = 2 }, + }, + }, + [4914] = { + itemId = false, + itemPos = { + { x = 32886, y = 32772, z = 9 }, + { x = 32882, y = 32790, z = 11 }, + { x = 32857, y = 32739, z = 11 }, + { x = 32860, y = 32769, z = 11 }, + { x = 32881, y = 32829, z = 11 }, + { x = 32881, y = 32820, z = 11 }, + { x = 32882, y = 32820, z = 11 }, + }, + }, + [4915] = { + itemId = false, + itemPos = { + { x = 32860, y = 32798, z = 11 }, + { x = 32858, y = 32766, z = 10 }, + { x = 32818, y = 32780, z = 11 }, + { x = 32854, y = 32737, z = 10 }, + { x = 32808, y = 32765, z = 10 }, + }, + }, + [4920] = { + itemId = false, + itemPos = { + { x = 33376, y = 31335, z = 3 }, + { x = 33371, y = 31349, z = 4 }, + { x = 33376, y = 31349, z = 4 }, + { x = 33375, y = 31346, z = 5 }, + { x = 33363, y = 31346, z = 7 }, + { x = 33366, y = 31343, z = 7 }, + { x = 33310, y = 31325, z = 8 }, + { x = 33329, y = 31333, z = 9 }, + }, + }, + [4921] = { + itemId = false, + itemPos = { + { x = 33373, y = 31309, z = 7 }, + { x = 33382, y = 31294, z = 7 }, + { x = 33344, y = 31348, z = 7 }, + { x = 33328, y = 31352, z = 7 }, + { x = 33298, y = 31287, z = 9 }, + { x = 33297, y = 31287, z = 9 }, + { x = 33296, y = 31287, z = 9 }, + { x = 33296, y = 31288, z = 9 }, + { x = 33298, y = 31288, z = 9 }, + { x = 33298, y = 31289, z = 9 }, + { x = 33297, y = 31289, z = 9 }, + }, + }, + [4930] = { + itemId = false, + itemPos = { + { x = 33110, y = 32385, z = 7 }, + { x = 32958, y = 32322, z = 8 }, + { x = 32955, y = 32288, z = 10 }, + { x = 32942, y = 32283, z = 10 }, + { x = 32945, y = 32313, z = 8 }, + { x = 32963, y = 32319, z = 9 }, + { x = 32955, y = 32304, z = 9 }, + { x = 32984, y = 32314, z = 9 }, + { x = 32968, y = 32324, z = 9 }, + { x = 32978, y = 32290, z = 10 }, + { x = 32963, y = 32297, z = 8 }, + { x = 32963, y = 32299, z = 8 }, + { x = 32963, y = 32301, z = 8 }, + { x = 32963, y = 32303, z = 8 }, + { x = 32963, y = 32312, z = 8 }, + }, + }, + [4931] = { + itemId = false, + itemPos = { + { x = 32965, y = 32310, z = 9 }, + { x = 32965, y = 32309, z = 9 }, + { x = 32964, y = 32309, z = 9 }, + { x = 32963, y = 32309, z = 9 }, + { x = 32962, y = 32309, z = 9 }, + { x = 32961, y = 32309, z = 9 }, + { x = 32960, y = 32309, z = 9 }, + { x = 32959, y = 32309, z = 9 }, + { x = 32959, y = 32310, z = 9 }, + { x = 32959, y = 32311, z = 9 }, + { x = 32958, y = 32311, z = 9 }, + { x = 32957, y = 32311, z = 9 }, + { x = 32956, y = 32311, z = 9 }, + { x = 32956, y = 32310, z = 9 }, + { x = 32955, y = 32310, z = 9 }, + { x = 32954, y = 32310, z = 9 }, + { x = 32953, y = 32310, z = 9 }, + { x = 32953, y = 32311, z = 9 }, + { x = 32953, y = 32312, z = 9 }, + { x = 32953, y = 32313, z = 9 }, + { x = 32953, y = 32314, z = 9 }, + { x = 32954, y = 32314, z = 9 }, + { x = 32955, y = 32314, z = 9 }, + { x = 32956, y = 32314, z = 9 }, + { x = 32956, y = 32315, z = 9 }, + { x = 32956, y = 32316, z = 9 }, + { x = 32956, y = 32317, z = 9 }, + { x = 32957, y = 32317, z = 9 }, + { x = 32957, y = 32318, z = 9 }, + { x = 32957, y = 32319, z = 9 }, + { x = 32958, y = 32319, z = 9 }, + { x = 32959, y = 32319, z = 9 }, + { x = 32960, y = 32319, z = 9 }, + { x = 32960, y = 32318, z = 9 }, + { x = 32960, y = 32317, z = 9 }, + { x = 32961, y = 32317, z = 9 }, + { x = 32962, y = 32317, z = 9 }, + { x = 32962, y = 32318, z = 9 }, + { x = 32962, y = 32319, z = 9 }, + { x = 32970, y = 32314, z = 9 }, + { x = 32980, y = 32308, z = 9 }, + { x = 32955, y = 32282, z = 10 }, + { x = 32983, y = 32289, z = 10 }, + { x = 32944, y = 32309, z = 8 }, + }, + }, + [4932] = { + itemId = false, + itemPos = { + { x = 32974, y = 32296, z = 9 }, + { x = 32967, y = 32319, z = 9 }, + { x = 32963, y = 32280, z = 10 }, + { x = 32963, y = 32282, z = 10 }, + { x = 32963, y = 32284, z = 10 }, + { x = 32963, y = 32286, z = 10 }, + }, + }, + [4933] = { + itemId = false, + itemPos = { + { x = 32945, y = 32288, z = 10 }, + { x = 32948, y = 32288, z = 10 }, + { x = 32951, y = 32288, z = 10 }, + }, + }, + [4935] = { + itemId = false, + itemPos = { + { x = 33464, y = 32157, z = 7 }, + { x = 32019, y = 32470, z = 7 }, + { x = 32460, y = 32928, z = 7 }, + { x = 32119, y = 31734, z = 7 }, + }, + }, + [4936] = { + itemId = false, + itemPos = { + { x = 32026, y = 32468, z = 7 }, + { x = 32025, y = 32468, z = 7 }, + { x = 32024, y = 32468, z = 7 }, + { x = 32018, y = 32467, z = 7 }, + { x = 32017, y = 32467, z = 7 }, + { x = 32023, y = 32468, z = 7 }, + { x = 32022, y = 32468, z = 7 }, + { x = 32021, y = 32468, z = 7 }, + { x = 32019, y = 32468, z = 7 }, + { x = 32020, y = 32468, z = 7 }, + { x = 32018, y = 32468, z = 7 }, + }, + }, + [4937] = { + itemId = false, + itemPos = { + { x = 32034, y = 32456, z = 7 }, + { x = 32034, y = 32457, z = 7 }, + }, + }, + [4950] = { + itemId = false, + itemPos = { + { x = 32616, y = 32529, z = 13 }, + { x = 32464, y = 32654, z = 12 }, + { x = 32662, y = 32713, z = 13 }, + { x = 32660, y = 32736, z = 12 }, + { x = 32720, y = 32773, z = 10 }, + { x = 32720, y = 32749, z = 10 }, + { x = 32746, y = 32773, z = 10 }, + { x = 32746, y = 32749, z = 10 }, + { x = 32750, y = 32696, z = 10 }, + { x = 32755, y = 32729, z = 10 }, + { x = 32687, y = 32726, z = 10 }, + { x = 32724, y = 32728, z = 10 }, + }, + }, + [4952] = { + itemId = false, + itemPos = { + { x = 32687, y = 32707, z = 10 }, + { x = 32698, y = 32715, z = 10 }, + { x = 32693, y = 32729, z = 10 }, + { x = 32681, y = 32729, z = 10 }, + { x = 32676, y = 32715, z = 10 }, + }, + }, + -- Cults of Tibia Quest + [5522] = { + itemId = 25687, + itemPos = { + { x = 33279, y = 32169, z = 8 }, + }, + }, + [5523] = { + itemId = 2471, + itemPos = { + { x = 33300, y = 32277, z = 12 }, + }, + }, + [5524] = { + itemId = 2930, + itemPos = { + { x = 32400, y = 31793, z = 8 }, + }, + }, + [5530] = { + itemId = false, + itemPos = { + { x = 32693, y = 31479, z = 8 }, + { x = 32680, y = 31485, z = 8 }, + }, + }, + [5531] = { + itemId = false, + itemPos = { + { x = 32748, y = 31489, z = 8 }, + { x = 32750, y = 31508, z = 8 }, + }, + }, + [5532] = { + itemId = false, + itemPos = { + { x = 32710, y = 31440, z = 8 }, + { x = 32709, y = 31440, z = 8 }, + { x = 32708, y = 31440, z = 8 }, + { x = 32698, y = 31405, z = 8 }, + }, + }, + [5533] = { + itemId = false, + itemPos = { + { x = 32683, y = 31412, z = 8 }, + { x = 32684, y = 31412, z = 8 }, + }, + }, + [5535] = { + itemId = false, + itemPos = { + { x = 32742, y = 31410, z = 8 }, + { x = 32685, y = 31430, z = 8 }, + { x = 32746, y = 31462, z = 8 }, + { x = 32731, y = 31531, z = 9 }, + { x = 32745, y = 31523, z = 9 }, + { x = 32761, y = 31518, z = 9 }, + { x = 32739, y = 31507, z = 9 }, + { x = 32739, y = 31489, z = 9 }, + { x = 32683, y = 31537, z = 9 }, + { x = 32720, y = 31545, z = 8 }, + }, + }, + [5536] = { + itemId = false, + itemPos = { + { x = 32740, y = 31494, z = 9 }, + { x = 32741, y = 31494, z = 9 }, + }, + }, + [5540] = { + itemId = 25761, + itemPos = { + { x = 33135, y = 31859, z = 10 }, + { x = 33136, y = 31859, z = 10 }, + { x = 33128, y = 31885, z = 11 }, + { x = 33129, y = 31885, z = 11 }, + { x = 33175, y = 31923, z = 12 }, + { x = 33176, y = 31923, z = 12 }, + { x = 33177, y = 31923, z = 12 }, + }, + }, + [5581] = { + itemId = 25726, + itemPos = { + { x = 32350, y = 31657, z = 8 }, + { x = 32351, y = 31657, z = 8 }, + { x = 32352, y = 31657, z = 8 }, + { x = 32353, y = 31657, z = 8 }, + { x = 32327, y = 31692, z = 9 }, + { x = 32328, y = 31692, z = 9 }, + { x = 32329, y = 31692, z = 9 }, + { x = 32528, y = 32468, z = 10 }, + { x = 32529, y = 32468, z = 10 }, + { x = 32530, y = 32468, z = 10 }, + { x = 32531, y = 32468, z = 10 }, + { x = 32532, y = 32468, z = 10 }, + { x = 32533, y = 32468, z = 10 }, + { x = 32534, y = 32468, z = 10 }, + { x = 32535, y = 32468, z = 10 }, + { x = 32536, y = 32468, z = 10 }, + { x = 32537, y = 32468, z = 10 }, + { x = 32538, y = 32468, z = 10 }, + { x = 32539, y = 32468, z = 10 }, + }, + }, -- In Service of Yalahar Quest [7812] = { itemId = 7804, @@ -885,6 +1248,19 @@ ItemAction = { { x = 33324, y = 31372, z = 14 }, }, }, + -- Forgotten Knowledge Quest + [24875] = { + itemId = 23730, + itemPos = { x = 32860, y = 31644, z = 10 }, + }, + [24876] = { + itemId = 1503, + itemPos = { x = 32891, y = 31619, z = 10 }, + }, + [24877] = { + itemId = 1503, + itemPos = { x = 32924, y = 31636, z = 14 }, + }, -- The Cursed Crystal [25018] = { itemId = false, @@ -1591,6 +1967,152 @@ ItemAction = { { x = 32242, y = 32611, z = 11 }, }, }, + -- Dangerous Depths Quest + [57231] = { + itemId = 7062, + itemPos = { + { x = 33773, y = 32249, z = 14 }, + { x = 33774, y = 32249, z = 14 }, + }, + }, + [57240] = { + itemId = false, + itemPos = { + { x = 33979, y = 32209, z = 14 }, + { x = 33979, y = 32208, z = 14 }, + { x = 33933, y = 32163, z = 14 }, + { x = 33925, y = 32217, z = 14 }, + { x = 33979, y = 32207, z = 14 }, + { x = 33979, y = 32206, z = 14 }, + { x = 33925, y = 32215, z = 14 }, + { x = 33925, y = 32216, z = 14 }, + { x = 33924, y = 32217, z = 14 }, + { x = 33924, y = 32218, z = 14 }, + { x = 33932, y = 32162, z = 14 }, + { x = 33933, y = 32162, z = 14 }, + }, + }, + [57241] = { + itemId = false, + itemPos = { + { x = 33990, y = 32195, z = 14 }, + { x = 33983, y = 32207, z = 14 }, + { x = 33983, y = 32206, z = 14 }, + { x = 33983, y = 32208, z = 14 }, + { x = 33992, y = 32195, z = 14 }, + { x = 33983, y = 32209, z = 14 }, + { x = 33991, y = 32195, z = 14 }, + }, + }, + [57242] = { + itemId = false, + itemPos = { + { x = 33863, y = 32177, z = 14 }, + { x = 33863, y = 32178, z = 14 }, + { x = 33863, y = 32179, z = 14 }, + { x = 33863, y = 32181, z = 14 }, + { x = 33863, y = 32182, z = 14 }, + { x = 33863, y = 32180, z = 14 }, + { x = 33863, y = 32175, z = 14 }, + { x = 33863, y = 32176, z = 14 }, + }, + }, + [57300] = { + itemId = false, + itemPos = { + { x = 34033, y = 32000, z = 14 }, + { x = 33982, y = 31982, z = 14 }, + { x = 34002, y = 31986, z = 14 }, + }, + }, + [57301] = { + itemId = false, + itemPos = { + { x = 33993, y = 32264, z = 14 }, + { x = 33947, y = 32258, z = 14 }, + { x = 34027, y = 32245, z = 14 }, + }, + }, + [57302] = { + itemId = false, + itemPos = { + { x = 33927, y = 32338, z = 14 }, + { x = 33898, y = 32338, z = 14 }, + { x = 33958, y = 32338, z = 14 }, + }, + }, + [57350] = { + itemId = 27505, + itemPos = { x = 33214, y = 32280, z = 15 }, + }, + [57351] = { + itemId = 27503, + itemPos = { x = 33358, y = 32267, z = 15 }, + }, + [57352] = { + itemId = 27503, + itemPos = { x = 33275, y = 32310, z = 15 }, + }, + [57353] = { + itemId = 27505, + itemPos = { x = 33302, y = 32353, z = 15 }, + }, + [57354] = { + itemId = 27507, + itemPos = { x = 33330, y = 32290, z = 15 }, + }, + [57355] = { + itemId = 27507, + itemPos = { x = 33224, y = 32361, z = 15 }, + }, + [57356] = { + itemId = 27503, + itemPos = { x = 33212, y = 32147, z = 15 }, + }, + [57357] = { + itemId = 27505, + itemPos = { x = 33300, y = 32134, z = 15 }, + }, + [57358] = { + itemId = 27503, + itemPos = { x = 33277, y = 32172, z = 15 }, + }, + [57359] = { + itemId = 27505, + itemPos = { x = 33246, y = 32162, z = 15 }, + }, + [57360] = { + itemId = 27507, + itemPos = { x = 33222, y = 32182, z = 15 }, + }, + [57361] = { + itemId = 27507, + itemPos = { x = 33338, y = 32164, z = 15 }, + }, + [57362] = { + itemId = 27503, + itemPos = { x = 33428, y = 32181, z = 15 }, + }, + [57363] = { + itemId = 27505, + itemPos = { x = 33539, y = 32213, z = 15 }, + }, + [57364] = { + itemId = 27503, + itemPos = { x = 33501, y = 32255, z = 15 }, + }, + [57365] = { + itemId = 27505, + itemPos = { x = 33493, y = 32216, z = 15 }, + }, + [57366] = { + itemId = 27507, + itemPos = { x = 33490, y = 32180, z = 15 }, + }, + [57367] = { + itemId = 27507, + itemPos = { x = 33454, y = 32215, z = 15 }, + }, -- The Ice Islands Quest, Nibelor 1: Breaking the Ice [60000] = { itemId = 7185, @@ -1623,6 +2145,11 @@ ItemUnique = { itemId = 2576, itemPos = { x = 32455, y = 31166, z = 12 }, }, + -- The First Dragon + [1066] = { + itemId = 3657, + itemPos = { x = 33608, y = 31022, z = 14 }, + }, -- The Pits of Inferno Quest [2000] = { itemId = 599, @@ -2649,4 +3176,17 @@ ItemUnique = { itemId = 1949, itemPos = { x = 32826, y = 32347, z = 9 }, }, + -- Dangerous Depths Quest + [57234] = { + itemId = 2472, + itemPos = { x = 33841, y = 32124, z = 14 }, + }, + [57235] = { + itemId = 21301, + itemPos = { x = 33755, y = 32163, z = 14 }, + }, + [57236] = { + itemId = 9253, + itemPos = { x = 33755, y = 32161, z = 14 }, + }, } diff --git a/data-otservbr-global/startup/tables/lever.lua b/data-otservbr-global/startup/tables/lever.lua index 86798188a0a..e7db97ab859 100644 --- a/data-otservbr-global/startup/tables/lever.lua +++ b/data-otservbr-global/startup/tables/lever.lua @@ -15,6 +15,45 @@ LeverAction = { { x = 32972, y = 32412, z = 11 }, }, }, + -- The Secret Library Quest + [4901] = { + itemId = 2772, + itemPos = { + { x = 33522, y = 31464, z = 15 }, + }, + }, + [4906] = { + itemId = false, + itemPos = { + { x = 33251, y = 32039, z = 8 }, + { x = 33218, y = 32096, z = 10 }, + }, + }, + [4922] = { + itemId = 2772, + itemPos = { + { x = 33364, y = 31343, z = 9 }, + }, + }, + -- Cults of Tibia Quest + [5500] = { + itemId = 8911, + itemPos = { + { x = 33095, y = 31942, z = 15 }, + }, + }, + [5501] = { + itemId = 8911, + itemPos = { + { x = 33138, y = 31952, z = 15 }, + }, + }, + [5520] = { + itemId = 9110, + itemPos = { + { x = 33299, y = 32144, z = 10 }, + }, + }, -- The Pits of Inferno Quest [7799] = { itemId = 431, @@ -102,6 +141,19 @@ LeverAction = { { x = 32576, y = 31862, z = 14 }, }, }, + -- Forgotten Knowledge Quest + [26663] = { + itemId = 9125, + itemPos = { + { x = 32792, y = 32820, z = 13 }, + }, + }, + [26664] = { + itemId = 9125, + itemPos = { + { x = 32840, y = 32820, z = 13 }, + }, + }, -- Dawnport bridge lever -- Path: data\scripts\actions\dawnport\lever.lua [30001] = { @@ -230,9 +282,12 @@ LeverAction = { -- The Pits of Inferno Quest [50160] = { itemId = 2772, - itemPos = { - { x = 32853, y = 32318, z = 9 }, - }, + itemPos = { x = 32853, y = 32318, z = 9 }, + }, + -- The Pits of Inferno Quest + [57234] = { + itemId = 2772, + itemPos = { x = 33841, y = 32080, z = 14 }, }, } diff --git a/data-otservbr-global/startup/tables/teleport.lua b/data-otservbr-global/startup/tables/teleport.lua index 367dc51ba4e..94a7d57e822 100644 --- a/data-otservbr-global/startup/tables/teleport.lua +++ b/data-otservbr-global/startup/tables/teleport.lua @@ -9,6 +9,38 @@ TeleportAction = { { x = 32210, y = 32292, z = 6 }, }, }, + -- The Secret Library Quest + [4905] = { + itemId = 25054, + itemPos = { + { x = 33246, y = 32107, z = 8 }, + { x = 33246, y = 32098, z = 8 }, + }, + }, + [4906] = { + itemId = 1949, + itemPos = { + { x = 33288, y = 32106, z = 9 }, + { x = 33288, y = 32107, z = 9 }, + { x = 33287, y = 32106, z = 9 }, + { x = 33287, y = 32107, z = 9 }, + { x = 33286, y = 32106, z = 9 }, + { x = 33286, y = 32107, z = 9 }, + }, + }, + -- Cults of Tibia Quest + [5517] = { + itemId = 1949, + itemPos = { + { x = 33459, y = 32267, z = 10 }, + }, + }, + [5518] = { + itemId = 1949, + itemPos = { + { x = 33449, y = 32241, z = 7 }, + }, + }, -- Barbarian Test - Temple Citizen Svargrond [30032] = { itemId = 1949, @@ -24,9 +56,39 @@ TeleportAction = { { x = 32177, y = 31869, z = 15 }, }, }, + -- Dangerous Depths Quest + [57230] = { + itemId = 1949, + itemPos = { + { x = 33827, y = 32172, z = 14 }, + { x = 33829, y = 32128, z = 14 }, + { x = 33777, y = 32192, z = 14 }, + }, + }, } TeleportUnique = { + -- Forgotten Knowledge Quest + [1067] = { + itemId = 1949, + itemPos = { x = 32915, y = 31637, z = 14 }, + }, + [1068] = { + itemId = 1949, + itemPos = { x = 32676, y = 32888, z = 14 }, + }, + [1070] = { + itemId = 1949, + itemPos = { x = 32316, y = 31093, z = 14 }, + }, + [1071] = { + itemId = 1949, + itemPos = { x = 32849, y = 32689, z = 15 }, + }, + [1072] = { + itemId = 1949, + itemPos = { x = 32033, y = 32859, z = 14 }, + }, -- Issavi town teleport [9515] = { itemId = 1949, @@ -34,6 +96,11 @@ TeleportUnique = { }, -- The first dragon quest -- Path: data\scripts\movements\quests\first_dragon\entrance_teleport.lua + -- Boss First Dragon + [24889] = { + itemId = 1949, + itemPos = { x = 33597, y = 30996, z = 14 }, + }, -- Tazhadur entrance [35001] = { itemId = 8649, diff --git a/data-otservbr-global/startup/tables/teleport_item.lua b/data-otservbr-global/startup/tables/teleport_item.lua index c9595228a49..c09dd9e22fa 100644 --- a/data-otservbr-global/startup/tables/teleport_item.lua +++ b/data-otservbr-global/startup/tables/teleport_item.lua @@ -49,19 +49,97 @@ TeleportItemAction = { { x = 32172, y = 32439, z = 7 }, }, }, - -- Teleports Thais + -- Forgotten Knowledge Quest - Teleports Thais [24873] = { - itemId = false, + itemId = 25047, itemPos = { { x = 32325, y = 32087, z = 7 }, + }, + }, + [24873] = { + itemId = 25051, + itemPos = { { x = 32328, y = 32087, z = 7 }, + }, + }, + [24873] = { + itemId = 25049, + itemPos = { { x = 32331, y = 32087, z = 7 }, + }, + }, + [24873] = { + itemId = 25053, + itemPos = { { x = 32334, y = 32087, z = 7 }, + }, + }, + [24873] = { + itemId = 25057, + itemPos = { { x = 32337, y = 32087, z = 7 }, + }, + }, + [24873] = { + itemId = 25055, + itemPos = { { x = 32340, y = 32087, z = 7 }, + }, + }, + [24873] = { + itemId = 10840, + itemPos = { { x = 32332, y = 32094, z = 7 }, }, }, + [24873] = { + itemId = 25048, + itemPos = { + { x = 32805, y = 31657, z = 8 }, + }, + }, + [24873] = { + itemId = 25052, + itemPos = { + { x = 32786, y = 32818, z = 13 }, + }, + }, + [24873] = { + itemId = 25050, + itemPos = { + { x = 32637, y = 32255, z = 7 }, + }, + }, + [24873] = { + itemId = 25054, + itemPos = { + { x = 33341, y = 31167, z = 7 }, + }, + }, + [24873] = { + itemId = 25058, + itemPos = { + { x = 32205, y = 31036, z = 10 }, + }, + }, + [24873] = { + itemId = 25056, + itemPos = { + { x = 32780, y = 32684, z = 14 }, + }, + }, + [24873] = { + itemId = 10842, + itemPos = { + { x = 32906, y = 32846, z = 13 }, + }, + }, + [26668] = { + itemId = 1949, + itemPos = { + { x = 33396, y = 31129, z = 9 }, + }, + }, } TeleportItemUnique = { diff --git a/data-otservbr-global/startup/tables/tile.lua b/data-otservbr-global/startup/tables/tile.lua index 546bc66ef65..59d9d3be69f 100644 --- a/data-otservbr-global/startup/tables/tile.lua +++ b/data-otservbr-global/startup/tables/tile.lua @@ -54,6 +54,7 @@ TileAction = { { x = 32696, y = 31720, z = 7 }, { x = 32697, y = 31720, z = 7 }, { x = 32698, y = 31720, z = 7 }, + { x = 32691, y = 31724, z = 7 }, }, }, --Dawnport diff --git a/data-otservbr-global/world/otservbr-monster.xml b/data-otservbr-global/world/otservbr-monster.xml index 80c5f5d4ff3..424e4210b80 100644 --- a/data-otservbr-global/world/otservbr-monster.xml +++ b/data-otservbr-global/world/otservbr-monster.xml @@ -1780,6 +1780,9 @@ + + + @@ -2116,6 +2119,9 @@ + + + @@ -3314,12 +3320,15 @@ - - + + + + + @@ -9595,6 +9604,7 @@ + @@ -14604,6 +14614,9 @@ + + + @@ -51892,8 +51905,9 @@ - - + + + diff --git a/data-otservbr-global/world/otservbr-npc.xml b/data-otservbr-global/world/otservbr-npc.xml index 5a475b8999d..5c53601aa9c 100644 --- a/data-otservbr-global/world/otservbr-npc.xml +++ b/data-otservbr-global/world/otservbr-npc.xml @@ -2818,6 +2818,9 @@ + + + diff --git a/data/events/scripts/player.lua b/data/events/scripts/player.lua index eee28b632d6..2efc7d92f75 100644 --- a/data/events/scripts/player.lua +++ b/data/events/scripts/player.lua @@ -371,9 +371,17 @@ end function Player:onItemMoved(item, count, fromPosition, toPosition, fromCylinder, toCylinder) if IsRunningGlobalDatapack() then + -- The Secret Library Quest + if toPosition == Position(32460, 32928, 7) and item.itemid == 3578 then + toPosition:sendMagicEffect(CONST_ME_HEARTS) + self:say("You feed the turtle, now you may pass.", TALKTYPE_MONSTER_SAY) + Game.setStorageValue(Storage.Quest.U11_80.TheSecretLibrary.SmallIslands.Turtle, os.time() + 10 * 60) + item:remove(1) + end + -- Cults of Tibia begin - local frompos = Position(33023, 31904, 14) -- Checagem - local topos = Position(33052, 31932, 15) -- Checagem + local frompos = Position(33023, 31904, 14) + local topos = Position(33052, 31932, 15) local removeItem = false if self:getPosition():isInRange(frompos, topos) and item:getId() == 23729 then local tile = Tile(toPosition) diff --git a/data/items/items.xml b/data/items/items.xml index 79f122afb9a..d4e2e6bf5cf 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -51874,6 +51874,10 @@ hands of its owner. Granted by TibiaRoyal.com"/> + + + + @@ -53150,6 +53154,7 @@ hands of its owner. Granted by TibiaRoyal.com"/> + @@ -53200,7 +53205,10 @@ hands of its owner. Granted by TibiaRoyal.com"/> - + + + + @@ -53994,7 +54002,7 @@ hands of its owner. Granted by TibiaRoyal.com"/> - + diff --git a/data/libs/functions/boss_lever.lua b/data/libs/functions/boss_lever.lua index b1141619ce2..da3b81d3704 100644 --- a/data/libs/functions/boss_lever.lua +++ b/data/libs/functions/boss_lever.lua @@ -146,10 +146,22 @@ end function BossLever:onUse(player) local monsterName = MonsterType(self.name):getName() local isParticipant = false - for _, v in ipairs(self.playerPositions) do - if Position(v.pos) == player:getPosition() then + local players = {} + + for i = 1, #self.playerPositions do + local pos = self.playerPositions[i].pos + local creature = Tile(pos):getTopCreature() + + if not creature or not creature:isPlayer() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need " .. #self.playerPositions .. " players to challenge " .. self.name .. ".") + return true + end + + if pos == player:getPosition() then isParticipant = true end + + table.insert(players, creature) end if not isParticipant then return false diff --git a/data/modules/scripts/cults_of_tibia/death.lua b/data/modules/scripts/cults_of_tibia/death.lua index a9a855a9471..c27a9926171 100644 --- a/data/modules/scripts/cults_of_tibia/death.lua +++ b/data/modules/scripts/cults_of_tibia/death.lua @@ -1,8 +1,8 @@ function onRecvbyte(player, msg, byte) if IsRunningGlobalDatapack() and player then - local storageDeathFake = player:getStorageValue(Storage.CultsOfTibia.Barkless.Death) + local storageDeathFake = player:getStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Death) if storageDeathFake == 1 then - player:setStorageValue(Storage.CultsOfTibia.Barkless.Death, 0) + player:setStorageValue(Storage.Quest.U11_40.CultsOfTibia.Barkless.Death, 0) player:addHealth(player:getMaxHealth()) player:addMana(player:getMaxMana()) player:setHiddenHealth(false) From 00b75095cc250873b16533234fddffe2ff9b6a8a Mon Sep 17 00:00:00 2001 From: Pedro Cruz Date: Wed, 30 Oct 2024 22:34:53 -0300 Subject: [PATCH 3/3] feat: crusher and amber crusher actions (#3033) Adds the Crusher and Amber Crusher item actions. --- .../lib/others/fragment_gems.lua | 16 +++++++ data-otservbr-global/lib/others/load.lua | 1 + .../scripts/actions/tools/amber_crusher.lua | 31 ++++++++++++++ .../scripts/actions/tools/crusher.lua | 42 +++++++++++++++++++ 4 files changed, 90 insertions(+) create mode 100644 data-otservbr-global/lib/others/fragment_gems.lua create mode 100644 data-otservbr-global/scripts/actions/tools/amber_crusher.lua create mode 100644 data-otservbr-global/scripts/actions/tools/crusher.lua diff --git a/data-otservbr-global/lib/others/fragment_gems.lua b/data-otservbr-global/lib/others/fragment_gems.lua new file mode 100644 index 00000000000..cb82b9e951e --- /dev/null +++ b/data-otservbr-global/lib/others/fragment_gems.lua @@ -0,0 +1,16 @@ +MAX_GEM_BREAK = 10 + +FRAGMENT_GEMS = { + small = { ids = { 44602, 44605, 44608, 44611 }, fragment = 46625, range = { 1, 4 } }, + medium = { ids = { 44603, 44606, 44609, 44612 }, fragment = 46625, range = { 2, 8 } }, + great = { ids = { 44604, 44607, 44610, 44613 }, fragment = 46626, range = { 1, 4 } }, +} + +function getGemData(id) + for _, gem in pairs(FRAGMENT_GEMS) do + if table.contains(gem.ids, id) then + return gem.fragment, gem.range + end + end + return nil +end diff --git a/data-otservbr-global/lib/others/load.lua b/data-otservbr-global/lib/others/load.lua index 031c8fb2026..3d7da57cc77 100644 --- a/data-otservbr-global/lib/others/load.lua +++ b/data-otservbr-global/lib/others/load.lua @@ -1 +1,2 @@ dofile(DATA_DIRECTORY .. "/lib/others/dawnport.lua") +dofile(DATA_DIRECTORY .. "/lib/others/fragment_gems.lua") diff --git a/data-otservbr-global/scripts/actions/tools/amber_crusher.lua b/data-otservbr-global/scripts/actions/tools/amber_crusher.lua new file mode 100644 index 00000000000..d9c3edc4ce8 --- /dev/null +++ b/data-otservbr-global/scripts/actions/tools/amber_crusher.lua @@ -0,0 +1,31 @@ +local amberCrusher = Action() + +function amberCrusher.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if not target or not target:isItem() or target:getId() == item:getId() or player:getItemCount(target:getId()) <= 0 then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use the crusher on a valid gem in your inventory.") + return false + end + + local fragmentType, fragmentRange = getGemData(target:getId()) + if not fragmentType then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "This item can't be broken into fragments.") + return false + end + + if target:getCount() >= MAX_GEM_BREAK then + target:remove(MAX_GEM_BREAK) + for i = 1, MAX_GEM_BREAK, 1 do + player:addItem(fragmentType, math.random(fragmentRange[1], fragmentRange[2])) + end + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have broken the gems into fragments.") + else + target:remove(1) + player:addItem(fragmentType, math.random(fragmentRange[1], fragmentRange[2])) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have broken the gem into fragments.") + end + + return true +end + +amberCrusher:id(46628) +amberCrusher:register() diff --git a/data-otservbr-global/scripts/actions/tools/crusher.lua b/data-otservbr-global/scripts/actions/tools/crusher.lua new file mode 100644 index 00000000000..8daa9f2771b --- /dev/null +++ b/data-otservbr-global/scripts/actions/tools/crusher.lua @@ -0,0 +1,42 @@ +local crusher = Action() + +function crusher.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if not target or not target:isItem() or target:getId() == item:getId() or player:getItemCount(target:getId()) <= 0 then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use the crusher on a valid gem in your inventory.") + return false + end + + local fragmentType, fragmentRange = getGemData(target:getId()) + if not fragmentType then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "This item can't be broken into fragments.") + return false + end + + local crusherCharges = item:getAttribute(ITEM_ATTRIBUTE_CHARGES) + if not crusherCharges or crusherCharges <= 0 then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your crusher has no more charges.") + return false + end + + target:remove(1) + + player:addItem(fragmentType, math.random(fragmentRange[1], fragmentRange[2])) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have broken the gem into fragments.") + + crusherCharges = crusherCharges - 1 + if crusherCharges > 0 then + local container = item:getParent() + item:setAttribute(ITEM_ATTRIBUTE_CHARGES, crusherCharges) + if container:isContainer() then + player:sendUpdateContainer(container) + end + else + item:remove() + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your crusher has been consumed.") + end + + return true +end + +crusher:id(46627) +crusher:register()