From cdd68ad8d2a015dbc13a57e4f099243840d33074 Mon Sep 17 00:00:00 2001 From: Beats Date: Wed, 30 Oct 2024 19:13:33 -0300 Subject: [PATCH] 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