From d755a2936921da75264e657f94e019e09d853f82 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Fri, 14 Nov 2025 15:25:12 +0000 Subject: [PATCH 1/8] feat: Made it possible for random bots to be persistent. --- conf/playerbots.conf.dist | 11 ++ src/Bot/Factory/PlayerbotFactory.cpp | 3 + src/Bot/RandomPlayerbotMgr.cpp | 136 +++++++++++++---------- src/Bot/RandomPlayerbotMgr.h | 2 + src/PlayerbotAIConfig.cpp | 3 + src/PlayerbotAIConfig.h | 3 + src/Script/Playerbots.cpp | 92 ++++++++++++++- src/registry/PlayerGuildRegistry.cpp | 67 +++++++++++ src/registry/PlayerGuildRegistry.h | 51 +++++++++ src/repository/PlayerGuildRepository.cpp | 76 +++++++++++++ src/repository/PlayerGuildRepository.h | 32 ++++++ src/utility/UInt32VectorToString.h | 23 ++++ 12 files changed, 438 insertions(+), 61 deletions(-) create mode 100644 src/registry/PlayerGuildRegistry.cpp create mode 100644 src/registry/PlayerGuildRegistry.h create mode 100644 src/repository/PlayerGuildRepository.cpp create mode 100644 src/repository/PlayerGuildRepository.h create mode 100644 src/utility/UInt32VectorToString.h diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 22390933eba..6545b824096 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -736,6 +736,17 @@ AiPlayerbot.EnableRandomBotTrading = 1 # Configure message prefixes which will be excluded in analysis in trade action to open trade window AiPlayerbot.TradeActionExcludedPrefixes = "RPLL_H_,DBMv4,{звезда} Questie,{rt1} Questie" +# This option will prevent ALL random bots from being randomized. +# It does not prevent periodic teleport. +# Without it, random bots will be periodically randomized unless they are in a guild +# with at least one character who is not a random bot. +AiPlayerbot.DisableRandomBotPeriodicRandomization = 1 + +# This option will prevent ALL random bots from being periodically teleported. +# THIS SHOULD NOT BE ENABLED AT ALL TIMES. It exists mostly for debugging purposes. +# Disabling periodic teleport will make the bots almost unable to reach new areas. +AiPlayerbot.DisableRandomBotPeriodicTeleportation = 0 + # # # diff --git a/src/Bot/Factory/PlayerbotFactory.cpp b/src/Bot/Factory/PlayerbotFactory.cpp index ca2b074ce21..32c3086f7ac 100644 --- a/src/Bot/Factory/PlayerbotFactory.cpp +++ b/src/Bot/Factory/PlayerbotFactory.cpp @@ -486,6 +486,9 @@ void PlayerbotFactory::Randomize(bool incremental) bot->SetPower(POWER_MANA, bot->GetMaxPower(POWER_MANA)); bot->SaveToDB(false, false); LOG_DEBUG("playerbots", "Initialization Done."); + + LOG_DEBUG("playerbots", "Bot #{} {}:{} <{}>: randomized", bot->GetGUID().GetRawValue(), + bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName().c_str()); if (pmo) pmo->finish(); } diff --git a/src/Bot/RandomPlayerbotMgr.cpp b/src/Bot/RandomPlayerbotMgr.cpp index 729090b5f06..2166eaa035b 100644 --- a/src/Bot/RandomPlayerbotMgr.cpp +++ b/src/Bot/RandomPlayerbotMgr.cpp @@ -45,6 +45,7 @@ #include "TravelMgr.h" #include "Unit.h" #include "World.h" +#include "PlayerGuildRegistry.h" #include "Cell.h" #include "GridNotifiers.h" // Required for Cell because of poor AC implementation @@ -1486,10 +1487,64 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot) return false; } -bool RandomPlayerbotMgr::ProcessBot(Player* bot) +bool RandomPlayerbotMgr::ProcessBotRandomization(Player* bot) +{ + if (sPlayerbotAIConfig->disableRandomBotPeriodicRandomization) + return false; + + uint64_t botId = bot->GetGUID().GetRawValue(); + uint32_t botGuildId = bot->GetGuildId(); + + if (sPlayerGuildRegistry.Contains(botGuildId)) + { + LOG_DEBUG("playerbots", "Bot #{} {}:{} <{}>: Randomization skipped because it is part of a guild with at least one non random bot.", bot->GetGUID().GetRawValue(), IsAlliance(bot->getRace()) ? "A" : "H", + bot->GetLevel(), bot->GetName().c_str()); + + return false; + } + + uint32_t timeUntilRandomize = GetEventValue(botId, "randomize"); + + if (timeUntilRandomize > 0) + return false; + + Randomize(bot); + + uint32 time = urand(sPlayerbotAIConfig->minRandomBotRandomizeTime, sPlayerbotAIConfig->maxRandomBotRandomizeTime); + + ScheduleRandomize(botId, time); + + return true; +} + +bool RandomPlayerbotMgr::ProcessBotTeleportation(Player* bot) { + if (sPlayerbotAIConfig->disableRandomBotPeriodicTeleportation) + return false; + + uint64_t botId = bot->GetGUID().GetRawValue(); + uint32_t timeUntilTeleport = GetEventValue(botId, "teleport"); + + if (timeUntilTeleport > 0) + return false; + + LOG_DEBUG("playerbots", "Bot #{} <{}>: teleport for level and refresh", botId, bot->GetName()); + Refresh(bot); + RandomTeleportForLevel(bot); + + uint32_t time = urand(sPlayerbotAIConfig->minRandomBotTeleportInterval, + sPlayerbotAIConfig->maxRandomBotTeleportInterval); + + ScheduleTeleport(botId, time); + + return true; +} + +bool RandomPlayerbotMgr::ProcessBot(Player* bot) +{ PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); + if (!botAI) return false; @@ -1526,6 +1581,7 @@ bool RandomPlayerbotMgr::ProcessBot(Player* bot) // leave group if leader is rndbot Group* group = bot->GetGroup(); + if (group && !group->isLFGGroup() && IsRandomBot(group->GetLeader())) { botAI->LeaveOrDisbandGroup(); @@ -1534,6 +1590,7 @@ bool RandomPlayerbotMgr::ProcessBot(Player* bot) // only randomize and teleport idle bots bool idleBot = false; + if (TravelTarget* target = botAI->GetAiObjectContext()->GetValue("travel target")->Get()) { if (target->getTravelState() == TravelState::TRAVEL_STATE_IDLE) @@ -1548,59 +1605,15 @@ bool RandomPlayerbotMgr::ProcessBot(Player* bot) if (idleBot) { - // randomize - uint32 randomize = GetEventValue(botId, "randomize"); - if (!randomize) - { - // bool randomiser = true; - // if (player->GetGuildId()) - // { - // if (Guild* guild = sGuildMgr->GetGuildById(player->GetGuildId())) - // { - // if (guild->GetLeaderGUID() == player->GetGUID()) - // { - // for (std::vector::iterator i = players.begin(); i != players.end(); ++i) - // GuildTaskMgr::instance().Update(*i, player); - // } - - // uint32 accountId = sCharacterCache->GetCharacterAccountIdByGuid(guild->GetLeaderGUID()); - // if (!sPlayerbotAIConfig.IsInRandomAccountList(accountId)) - // { - // uint8 rank = player->GetRank(); - // randomiser = rank < 4 ? false : true; - // } - // } - // } - // if (randomiser) - // { - Randomize(bot); - LOG_DEBUG("playerbots", "Bot #{} {}:{} <{}>: randomized", botId, - bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName()); - uint32 randomTime = - urand(sPlayerbotAIConfig.minRandomBotRandomizeTime, sPlayerbotAIConfig.maxRandomBotRandomizeTime); - ScheduleRandomize(botId, randomTime); - return true; - } + bool hasBeenRandomized = ProcessBotRandomization(bot); - // uint32 changeStrategy = GetEventValue(bot, "change_strategy"); - // if (!changeStrategy) - // { - // LOG_INFO("playerbots", "Changing strategy for bot #{} <{}>", bot, player->GetName().c_str()); - // ChangeStrategy(player); - // return true; - // } + if (hasBeenRandomized) + return true; - uint32 teleport = GetEventValue(botId, "teleport"); - if (!teleport) - { - LOG_DEBUG("playerbots", "Bot #{} <{}>: teleport for level and refresh", botId, bot->GetName()); - Refresh(bot); - RandomTeleportForLevel(bot); - uint32 time = urand(sPlayerbotAIConfig.minRandomBotTeleportInterval, - sPlayerbotAIConfig.maxRandomBotTeleportInterval); - ScheduleTeleport(botId, time); - return true; - } + bool hasBeenTeleported = ProcessBotTeleportation(bot); + + if (hasBeenTeleported) + return true; } return false; @@ -1610,7 +1623,6 @@ void RandomPlayerbotMgr::Revive(Player* player) { uint32 bot = player->GetGUID().GetCounter(); - // LOG_INFO("playerbots", "Bot {} revived", player->GetName().c_str()); SetEventValue(bot, "dead", 0, 0); SetEventValue(bot, "revive", 0, 0); @@ -2304,24 +2316,28 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot) void RandomPlayerbotMgr::Randomize(Player* bot) { + uint8 level = bot->GetLevel(); + if (bot->InBattleground()) return; if (bot->GetLevel() < 3 || (bot->GetLevel() < 56 && bot->getClass() == CLASS_DEATH_KNIGHT)) { RandomizeFirst(bot); + + return; } - else if (bot->GetLevel() < sPlayerbotAIConfig.randomBotMaxLevel || !sPlayerbotAIConfig.downgradeMaxLevelBot) + + if (bot->GetLevel() < sPlayerbotAIConfig.randomBotMaxLevel || !sPlayerbotAIConfig.downgradeMaxLevelBot) { - uint8 level = bot->GetLevel(); PlayerbotFactory factory(bot, level); + factory.Randomize(true); - // IncreaseLevel(bot); - } - else - { - RandomizeFirst(bot); + + return; } + + RandomizeFirst(bot); } void RandomPlayerbotMgr::IncreaseLevel(Player* bot) diff --git a/src/Bot/RandomPlayerbotMgr.h b/src/Bot/RandomPlayerbotMgr.h index 94c0a015134..74179e681da 100644 --- a/src/Bot/RandomPlayerbotMgr.h +++ b/src/Bot/RandomPlayerbotMgr.h @@ -250,6 +250,8 @@ class RandomPlayerbotMgr : public PlayerbotHolder time_t printStatsTimer; uint32 AddRandomBots(); bool ProcessBot(uint32 bot); + bool ProcessBotRandomization(Player* bot); + bool ProcessBotTeleportation(Player* bot); void ScheduleRandomize(uint32 bot, uint32 time); void RandomTeleport(Player* bot); void RandomTeleport(Player* bot, std::vector& locs, bool hearth = false); diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 3cb4dec1dbc..7ae3e012a50 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -678,6 +678,9 @@ bool PlayerbotAIConfig::Initialize() selfBotLevel = sConfigMgr->GetOption("AiPlayerbot.SelfBotLevel", 1); + disableRandomBotPeriodicRandomization = sConfigMgr->GetOption("AiPlayerbot.DisableRandomBotPeriodicRandomization", false); + disableRandomBotPeriodicTeleportation = sConfigMgr->GetOption("AiPlayerbot.DisableRandomBotPeriodicTeleportation", false); + RandomPlayerbotFactory::CreateRandomBots(); if (World::IsStopped()) { diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 2f066abc193..f64b29c4984 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -309,6 +309,9 @@ class PlayerbotAIConfig std::vector botCheats; uint32 botCheatMask = 0; + bool disableRandomBotPeriodicRandomization; + bool disableRandomBotPeriodicTeleportation; + struct worldBuff { uint32 spellId; diff --git a/src/Script/Playerbots.cpp b/src/Script/Playerbots.cpp index 6326eabade5..51b6c3f8b43 100644 --- a/src/Script/Playerbots.cpp +++ b/src/Script/Playerbots.cpp @@ -16,6 +16,8 @@ */ #include "Playerbots.h" +#include +#include #include "BattlefieldScript.h" #include "Channel.h" @@ -24,6 +26,7 @@ #include "DatabaseLoader.h" #include "GuildTaskMgr.h" #include "PlayerScript.h" +#include "GuildScript.h" #include "PlayerbotAIConfig.h" #include "PlayerbotGuildMgr.h" #include "PlayerbotSpellRepository.h" @@ -33,6 +36,11 @@ #include "PlayerbotCommandScript.h" #include "cmath" #include "BattleGroundTactics.h" +#include "PlayerGuildRegistry.h" +#include "Log.h" +#include "PlayerGuildRegistry.h" +#include "PlayerGuildRepository.h" +#include "Guild.h" class PlayerbotsDatabaseScript : public DatabaseScript { @@ -352,7 +360,13 @@ class PlayerbotsWorldScript : public WorldScript PlayerbotSpellRepository::Instance().Initialize(); LOG_INFO("server.loading", "Playerbots World Thread Processor initialized"); - } + + uint32_t beforeGuildRegistryInit = getMSTime(); + + sPlayerGuildRegistry.Initialize(); + + LOG_INFO("server.loading", ">> Initialized PlayerGuildRegistry in {} ms", GetMSTimeDiffToNow(beforeGuildRegistryInit)); + } void OnUpdate(uint32 diff) override { @@ -514,6 +528,81 @@ class PlayerbotsBattlefieldScript : public BattlefieldScript }; void AddPlayerbotsSecureLoginScripts(); +class PlayerbotsGuildScript : public GuildScript +{ + public: + + PlayerbotsGuildScript() : GuildScript("PlayerbotsGuildScript") {} + + void OnAddMember(Guild* guild, Player* player, [[maybe_unused]] uint8_t& plRank) + { + if (sRandomPlayerbotMgr->IsRandomBot(player)) + return; + + uint32_t guildId = guild->GetId(); + + if (sPlayerGuildRegistry.Contains(guildId)) + return; + + sPlayerGuildRegistry.Add(guildId); + + LOG_DEBUG("playerbots", "Added guild with id {} to PlayerGuildRegistry because it now contains a non random bot.", guildId); + } + + /** + * @brief Handles the player guilds registry maintenance when a member is removed from a guild. + * + * This hook is executed BEFORE the member is removed within the core. This method handles this properly and already handles + * the eventual change where the hook is being ran AFTER the removal making it future-proof. + */ + void OnGuildRemoveMember(Guild* guild, Player* player) + { + if (sRandomPlayerbotMgr->IsRandomBot(player)) + return; + + uint32_t guildId = guild->GetId(); + + if (!sPlayerGuildRegistry.Contains(guildId)) + return; + + // This does a non prepared database query. Since this event should be quite rare, it does not need to be overly optimised for now. + std::unordered_set memberIds = sPlayerGuildRepository.GetGuildMembersIds(guildId); + + uint64_t removedCharacterId = player->GetGUID().GetRawValue(); + + bool noNonRandomBotMember = memberIds.empty(); + bool removedPlayerWasLastNonBotMember = memberIds.size() == 1 && memberIds.find(removedCharacterId) != memberIds.end(); + + if (noNonRandomBotMember || removedPlayerWasLastNonBotMember) + { + sPlayerGuildRegistry.Remove(guildId); + } + } + + void OnGuildDisband(Guild* guild) + { + uint32_t guildId = guild->GetId(); + + if (!sPlayerGuildRegistry.Contains(guildId)) + return; + + sPlayerGuildRegistry.Remove(guildId); + } + + void OnGuildCreate(Guild* guild, [[maybe_unused]] Player* leader, [[maybe_unused]] const std::string& name) + { + uint32_t guildId = guild->GetId(); + + std::unordered_set memberIds = sPlayerGuildRepository.GetGuildMembersIds(guildId); + + bool nonRandomBotMember = !memberIds.empty(); + + if (nonRandomBotMember) + { + sPlayerGuildRegistry.Add(guildId); + } + } +}; void AddSC_TempestKeepBotScripts(); @@ -527,6 +616,7 @@ void AddPlayerbotsScripts() new PlayerbotsWorldScript(); new PlayerbotsScript(); new PlayerBotsBGScript(); + new PlayerbotsGuildScript(); AddPlayerbotsSecureLoginScripts(); AddPlayerbotsCommandscripts(); PlayerBotsGuildValidationScript(); diff --git a/src/registry/PlayerGuildRegistry.cpp b/src/registry/PlayerGuildRegistry.cpp new file mode 100644 index 00000000000..8613a4c71db --- /dev/null +++ b/src/registry/PlayerGuildRegistry.cpp @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include "PlayerGuildRegistry.h" +#include "PlayerGuildRepository.h" +#include "PlayerbotAIConfig.h" +#include "UInt32VectorToString.h" +#include "DatabaseEnv.h" +#include "QueryResult.h" +#include "Log.h" + +void PlayerGuildRegistry::Initialize() +{ + std::unordered_set playerGuildsIds = sPlayerGuildRepository.GetPlayerGuildsIds(); + + ids_.clear(); + ids_.merge(playerGuildsIds); + + LOG_INFO("server.loading", "[PlayerGuildRegistry] Initialized PlayerGuildRegistry with {} guilds containing non random bot account characters.", Size()); +} + +// Add an ID if not already present +void PlayerGuildRegistry::Add(uint32_t id) +{ + std::lock_guard lock(mutex_); + + ids_.insert(id); +} + +// Remove an ID if present +void PlayerGuildRegistry::Remove(uint32_t id) +{ + std::lock_guard lock(mutex_); + + ids_.erase(id); +} + +// Check if ID is present +bool PlayerGuildRegistry::Contains(uint32_t id) const +{ + std::lock_guard lock(mutex_); + + return ids_.find(id) != ids_.end(); +} + +// Retrieve a copy (thread safe) +std::vector PlayerGuildRegistry::Snapshot() const +{ + std::lock_guard lock(mutex_); + + return std::vector(ids_.begin(), ids_.end()); +} + +std::size_t PlayerGuildRegistry::Size() const +{ + std::lock_guard lock(mutex_); + + return ids_.size(); +} + +bool PlayerGuildRegistry::Empty() const +{ + std::lock_guard lock(mutex_); + + return ids_.empty(); +} diff --git a/src/registry/PlayerGuildRegistry.h b/src/registry/PlayerGuildRegistry.h new file mode 100644 index 00000000000..b03f4e6b9d3 --- /dev/null +++ b/src/registry/PlayerGuildRegistry.h @@ -0,0 +1,51 @@ +#ifndef PLAYER_GUILD_REGISTRY_H +#define PLAYER_GUILD_REGISTRY_H + +#include +#include +#include +#include + +class PlayerGuildRegistry { +public: + static PlayerGuildRegistry& GetInstance() + { + static PlayerGuildRegistry instance; + return instance; + } + + // Delete copy constructor and assignment operator to enforce singleton pattern + PlayerGuildRegistry(const PlayerGuildRegistry&) = delete; + PlayerGuildRegistry& operator=(const PlayerGuildRegistry&) = delete; + + void Initialize(); + + // Add an ID if not already present + void Add(uint32_t id); + + // Remove an ID if present + void Remove(uint32_t id); + + // Check if ID is present + bool Contains(uint32_t id) const; + + // Retrieve a copy (thread safe) + std::vector Snapshot() const; + + // Get the number of registered guilds + std::size_t Size() const; + + // Check if registry is empty + bool Empty() const; + +private: + PlayerGuildRegistry() = default; + ~PlayerGuildRegistry() = default; + + mutable std::mutex mutex_; + std::unordered_set ids_; +}; + +#define sPlayerGuildRegistry PlayerGuildRegistry::GetInstance() + +#endif diff --git a/src/repository/PlayerGuildRepository.cpp b/src/repository/PlayerGuildRepository.cpp new file mode 100644 index 00000000000..90d9f0ff686 --- /dev/null +++ b/src/repository/PlayerGuildRepository.cpp @@ -0,0 +1,76 @@ +#include +#include +#include "PlayerGuildRepository.h" +#include "PlayerbotAIConfig.h" +#include "UInt32VectorToString.h" +#include "DatabaseEnv.h" +#include "QueryResult.h" +#include "Log.h" + +std::unordered_set PlayerGuildRepository::GetPlayerGuildsIds() +{ + std::string randomBotsAccountsIdsString = UInt32VectorToString(sPlayerbotAIConfig->randomBotAccounts); + + std::unordered_set guildIds; + + QueryResult result = CharacterDatabase.Query( + "SELECT DISTINCT(guild.guildid) " + "FROM guild " + "INNER JOIN guild_member ON guild_member.guildid = guild.guildid " + "INNER JOIN characters ON guild_member.guid = characters.guid " + "WHERE characters.account NOT IN ({})", + randomBotsAccountsIdsString + ); + + if (!result) + { + LOG_INFO("playerbots", "[PlayerGuildRepository] No guild found containing real players. PlayerGuildRepository is empty."); + + return guildIds; + } + + do + { + Field* fields = result->Fetch(); + uint32_t id = fields[0].Get(); + + LOG_INFO("playerbots", "[PlayerGuildRepository] Adding guild with id '{}' to registry because it contains at least one non random bot account character.", id); + + guildIds.insert(id); + } while (result->NextRow()); + + return guildIds; +} + +std::unordered_set PlayerGuildRepository::GetGuildMembersIds(uint32_t guildId) +{ + std::string randomBotsAccountsIdsString = UInt32VectorToString(sPlayerbotAIConfig->randomBotAccounts); + + std::unordered_set membersIds; + + QueryResult result = CharacterDatabase.Query( + "SELECT characters.guid " + "INNER JOIN guild_member ON guild_member.guid = characters.guid " + "WHERE guild_member.guildid = {} " + "AND characters.account NOT IN ({})", + guildId, + randomBotsAccountsIdsString + ); + + if (!result) + { + LOG_DEBUG("playerbots", "[PlayerGuildRepository] No non random bot member for guild with id {}", guildId); + + return membersIds; + } + + do + { + Field* fields = result->Fetch(); + uint64_t id = fields[0].Get(); + + membersIds.insert(id); + } while (result->NextRow()); + + return membersIds; +} diff --git a/src/repository/PlayerGuildRepository.h b/src/repository/PlayerGuildRepository.h new file mode 100644 index 00000000000..d8c3a25e6ea --- /dev/null +++ b/src/repository/PlayerGuildRepository.h @@ -0,0 +1,32 @@ +#ifndef PLAYER_GUILD_REPOSITORY_H +#define PLAYER_GUILD_REPOSITORY_H + +#include +#include +#include +#include + +class PlayerGuildRepository { +public: + static PlayerGuildRepository& GetInstance() + { + static PlayerGuildRepository instance; + return instance; + } + + // Delete copy constructor and assignment operator to enforce singleton pattern + PlayerGuildRepository(const PlayerGuildRepository&) = delete; + PlayerGuildRepository& operator=(const PlayerGuildRepository&) = delete; + + std::unordered_set GetPlayerGuildsIds(); + + std::unordered_set GetGuildMembersIds(uint32_t guildId); + +private: + PlayerGuildRepository() = default; + ~PlayerGuildRepository() = default; +}; + +#define sPlayerGuildRepository PlayerGuildRepository::GetInstance() + +#endif diff --git a/src/utility/UInt32VectorToString.h b/src/utility/UInt32VectorToString.h new file mode 100644 index 00000000000..e517a8d1cfc --- /dev/null +++ b/src/utility/UInt32VectorToString.h @@ -0,0 +1,23 @@ +#ifndef UINT32_VECTOR_TO_STRING_H +#define UINT32_VECTOR_TO_STRING_H + +#include +#include +#include +#include + +inline std::string UInt32VectorToString(const std::vector& vec) +{ + std::ostringstream oss; + + for (size_t i = 0; i < vec.size(); ++i) + { + if (i > 0) + oss << ','; + oss << vec[i]; + } + + return oss.str(); +} + +#endif From 67a844c9a3b549e5af48c01c3dfc891f3f594c51 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sun, 4 Jan 2026 15:29:38 +0000 Subject: [PATCH 2/8] wip --- .gitignore | 3 + src/Ai/Base/Actions/SayAction.cpp | 1 + src/Ai/Base/ChatActionContext.h | 7 + src/Ai/Base/ChatTriggerContext.h | 2 + .../Strategy/ChatCommandHandlerStrategy.cpp | 10 + src/Ai/Base/Strategy/NonCombatStrategy.cpp | 1 + src/Bot/Engine/Engine.cpp | 7 + src/Bot/Factory/PlayerbotFactory.cpp | 316 +----- src/Bot/RandomPlayerbotMgr.cpp | 12 +- src/PlayerbotTitleMgr.cpp | 152 +++ src/Script/Playerbots.cpp | 4 +- .../abstract/AbstractItemInspector.h | 261 +++++ .../abstract/definition/enum/ItemActionEnum.h | 9 + .../definition/struct/ItemActionStruct.h | 12 + .../item/inspector/armor/ArmorItemInspector.h | 129 +++ .../consumable/AbstractConsumableInspector.h | 74 ++ .../ConsumableConsumableInspector.h | 960 ++++++++++++++++++ .../elixir/ConsumableElixirInspector.h | 83 ++ .../potion/ConsumablePotionInspector.h | 97 ++ .../inspector/container/ContainerInspector.h | 122 +++ src/domain/item/inspector/gem/GemInspector.h | 57 ++ .../inspector/generic/GenericItemInspector.h | 56 + .../inspector/global/GlobalItemInspector.h | 20 + .../item/inspector/glyph/GlyphInspector.h | 57 ++ src/domain/item/inspector/key/KeyInspector.h | 57 ++ .../MiscellaneousItemInspector.h | 56 + .../item/inspector/money/MoneyInspector.h | 65 ++ .../permanent/PermanentItemInspector.h | 43 + .../projectile/ProjectItemInspector.h | 67 ++ .../item/inspector/quest/QuestItemInspector.h | 70 ++ .../inspector/quiver/QuiverItemInspector.h | 56 + .../inspector/reagent/ReagentItemInspector.h | 56 + .../inspector/recipe/RecipeItemInspector.h | 56 + .../trade-good/TradeGoodItemInspector.h | 56 + .../inspector/weapon/WeaponItemInspector.h | 126 +++ .../facade/abstract/AbstractPlayerFacade.h | 36 + .../facade/equipment/PlayerEquipmentFacade.h | 48 + .../enum/PlayerEquipmentFacadeResultEnum.h | 6 + .../facade/inventory/PlayerInventoryFacade.h | 45 + .../enum/PlayerInventoryFacadeResultEnum.h | 6 + .../inspector/global/GlobalPlayerInspector.h | 243 +++++ src/repository/PlayerGuildRepository.cpp | 6 +- src/repository/PlayerGuildRepository.h | 2 - .../inventory/abstract/AbstractItemVisitor.h | 214 ++++ .../inventory/armor/ArmorItemVisitor.h | 110 ++ .../consumable/ConsumableItemVisitor.h | 150 +++ .../ConsumableConsumableItemVisitor.h | 914 +++++++++++++++++ .../elixir/ConsumableElixirItemVisitor.h | 223 ++++ .../potion/ConsumablePotionItemVisitor.h | 163 +++ .../container/ContainerItemVisitor.h | 98 ++ src/service/inventory/gem/GemItemVisitor.h | 59 ++ .../inventory/generic/GenericItemVisitor.h | 62 ++ .../inventory/glyph/GlyphItemVisitor.h | 53 + .../inventory/inventory/InventoryService.h | 104 ++ src/service/inventory/key/KeyItemVisitor.h | 52 + .../miscellaneous/MiscellaneousItemVisitor.h | 53 + .../inventory/money/MoneyItemVisitor.h | 76 ++ .../permanent/PermanentItemVisitor.h | 55 + .../projectile/ProjectileItemVisitor.h | 63 ++ .../inventory/quest/QuestItemVisitor.h | 70 ++ .../inventory/quiver/QuiverItemVisitor.h | 60 ++ .../inventory/reagent/ReagentItemVisitor.h | 54 + .../inventory/recipe/RecipeItemVisitor.h | 53 + .../trade-good/TradeGoodItemVisitor.h | 53 + .../inventory/weapon/WeaponItemVisitor.h | 96 ++ .../AbstractItemValueCalculationService.h | 198 ++++ ...enericWarriorItemValueCalculationService.h | 75 ++ .../inventory/ManageInventoryAction.cpp | 564 ++++++++++ .../player/inventory/ManageInventoryAction.h | 101 ++ 69 files changed, 7033 insertions(+), 292 deletions(-) create mode 100644 src/PlayerbotTitleMgr.cpp create mode 100644 src/domain/item/inspector/abstract/AbstractItemInspector.h create mode 100644 src/domain/item/inspector/abstract/definition/enum/ItemActionEnum.h create mode 100644 src/domain/item/inspector/abstract/definition/struct/ItemActionStruct.h create mode 100644 src/domain/item/inspector/armor/ArmorItemInspector.h create mode 100644 src/domain/item/inspector/consumable/AbstractConsumableInspector.h create mode 100644 src/domain/item/inspector/consumable/consumable/ConsumableConsumableInspector.h create mode 100644 src/domain/item/inspector/consumable/elixir/ConsumableElixirInspector.h create mode 100644 src/domain/item/inspector/consumable/potion/ConsumablePotionInspector.h create mode 100644 src/domain/item/inspector/container/ContainerInspector.h create mode 100644 src/domain/item/inspector/gem/GemInspector.h create mode 100644 src/domain/item/inspector/generic/GenericItemInspector.h create mode 100644 src/domain/item/inspector/global/GlobalItemInspector.h create mode 100644 src/domain/item/inspector/glyph/GlyphInspector.h create mode 100644 src/domain/item/inspector/key/KeyInspector.h create mode 100644 src/domain/item/inspector/miscellaneous/MiscellaneousItemInspector.h create mode 100644 src/domain/item/inspector/money/MoneyInspector.h create mode 100644 src/domain/item/inspector/permanent/PermanentItemInspector.h create mode 100644 src/domain/item/inspector/projectile/ProjectItemInspector.h create mode 100644 src/domain/item/inspector/quest/QuestItemInspector.h create mode 100644 src/domain/item/inspector/quiver/QuiverItemInspector.h create mode 100644 src/domain/item/inspector/reagent/ReagentItemInspector.h create mode 100644 src/domain/item/inspector/recipe/RecipeItemInspector.h create mode 100644 src/domain/item/inspector/trade-good/TradeGoodItemInspector.h create mode 100644 src/domain/item/inspector/weapon/WeaponItemInspector.h create mode 100644 src/domain/player/facade/abstract/AbstractPlayerFacade.h create mode 100644 src/domain/player/facade/equipment/PlayerEquipmentFacade.h create mode 100644 src/domain/player/facade/equipment/definition/enum/PlayerEquipmentFacadeResultEnum.h create mode 100644 src/domain/player/facade/inventory/PlayerInventoryFacade.h create mode 100644 src/domain/player/facade/inventory/definition/enum/PlayerInventoryFacadeResultEnum.h create mode 100644 src/service/inventory/abstract/AbstractItemVisitor.h create mode 100644 src/service/inventory/armor/ArmorItemVisitor.h create mode 100644 src/service/inventory/consumable/ConsumableItemVisitor.h create mode 100644 src/service/inventory/consumable/consumable/ConsumableConsumableItemVisitor.h create mode 100644 src/service/inventory/consumable/elixir/ConsumableElixirItemVisitor.h create mode 100644 src/service/inventory/consumable/potion/ConsumablePotionItemVisitor.h create mode 100644 src/service/inventory/container/ContainerItemVisitor.h create mode 100644 src/service/inventory/gem/GemItemVisitor.h create mode 100644 src/service/inventory/generic/GenericItemVisitor.h create mode 100644 src/service/inventory/glyph/GlyphItemVisitor.h create mode 100644 src/service/inventory/inventory/InventoryService.h create mode 100644 src/service/inventory/key/KeyItemVisitor.h create mode 100644 src/service/inventory/miscellaneous/MiscellaneousItemVisitor.h create mode 100644 src/service/inventory/money/MoneyItemVisitor.h create mode 100644 src/service/inventory/permanent/PermanentItemVisitor.h create mode 100644 src/service/inventory/projectile/ProjectileItemVisitor.h create mode 100644 src/service/inventory/quest/QuestItemVisitor.h create mode 100644 src/service/inventory/quiver/QuiverItemVisitor.h create mode 100644 src/service/inventory/reagent/ReagentItemVisitor.h create mode 100644 src/service/inventory/recipe/RecipeItemVisitor.h create mode 100644 src/service/inventory/trade-good/TradeGoodItemVisitor.h create mode 100644 src/service/inventory/weapon/WeaponItemVisitor.h create mode 100644 src/service/statistic/abstract/AbstractItemValueCalculationService.h create mode 100644 src/service/statistic/warrior/GenericWarriorItemValueCalculationService.h create mode 100644 src/strategy/actions/player/inventory/ManageInventoryAction.cpp create mode 100644 src/strategy/actions/player/inventory/ManageInventoryAction.h diff --git a/.gitignore b/.gitignore index 06b6c10a8f2..d8613487bb6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ !.gitignore +.cache/ +build/ + # #Generic # diff --git a/src/Ai/Base/Actions/SayAction.cpp b/src/Ai/Base/Actions/SayAction.cpp index 8dadca531a8..7c9fd74e08a 100644 --- a/src/Ai/Base/Actions/SayAction.cpp +++ b/src/Ai/Base/Actions/SayAction.cpp @@ -44,6 +44,7 @@ static const std::unordered_set noReplyMsgs = { "guard", "do accept invitation", "stats", + "manage_inventory", "react ?", "reset strats", "home", diff --git a/src/Ai/Base/ChatActionContext.h b/src/Ai/Base/ChatActionContext.h index fc00d434213..88a5aa7d42b 100644 --- a/src/Ai/Base/ChatActionContext.h +++ b/src/Ai/Base/ChatActionContext.h @@ -38,6 +38,7 @@ #include "LootStrategyAction.h" #include "LootRollAction.h" #include "MailAction.h" +#include "ManageInventoryAction.h" #include "NamedObjectContext.h" #include "NewRpgAction.h" #include "PassLeadershipToMasterAction.h" @@ -96,6 +97,9 @@ class ChatActionContext : public NamedObjectContext creators["unlock traded item"] = &ChatActionContext::unlock_traded_item; creators["range"] = &ChatActionContext::range; creators["stats"] = &ChatActionContext::stats; + + creators["manage inventory"] = &ChatActionContext::manage_inventory; + creators["quests"] = &ChatActionContext::quests; creators["leave"] = &ChatActionContext::leave; creators["reputation"] = &ChatActionContext::reputation; @@ -280,6 +284,9 @@ class ChatActionContext : public NamedObjectContext static Action* clean_quest_log(PlayerbotAI* botAI) { return new CleanQuestLogAction(botAI); } static Action* share(PlayerbotAI* botAI) { return new ShareQuestAction(botAI); } static Action* stats(PlayerbotAI* botAI) { return new StatsAction(botAI); } + + static Action* manage_inventory(PlayerbotAI* botAI) { return new ManageInventoryAction(botAI); } + static Action* quests(PlayerbotAI* botAI) { return new ListQuestsAction(botAI); } static Action* leave(PlayerbotAI* botAI) { return new LeaveGroupAction(botAI); } static Action* reputation(PlayerbotAI* botAI) { return new TellReputationAction(botAI); } diff --git a/src/Ai/Base/ChatTriggerContext.h b/src/Ai/Base/ChatTriggerContext.h index 6bdfcdc4134..99ced0f6cbb 100644 --- a/src/Ai/Base/ChatTriggerContext.h +++ b/src/Ai/Base/ChatTriggerContext.h @@ -146,6 +146,7 @@ class ChatTriggerContext : public NamedObjectContext creators["pet attack"] = &ChatTriggerContext::pet_attack; creators["roll"] = &ChatTriggerContext::roll_action; creators["wait for attack time"] = &ChatTriggerContext::wait_for_attack_time; + creators["manage inventory"] = &ChatTriggerContext::manage_inventory; } private: @@ -271,6 +272,7 @@ class ChatTriggerContext : public NamedObjectContext static Trigger* pet_attack(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pet attack"); } static Trigger* roll_action(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "roll"); } static Trigger* wait_for_attack_time(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "wait for attack time"); } + static Trigger* manage_inventory(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "manage inventory"); } }; #endif diff --git a/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp b/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp index c1aaf5ad891..eb12f622c9a 100644 --- a/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp +++ b/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp @@ -45,6 +45,7 @@ #include "PetsAction.h" #include "PetAttackAction.h" #include "LootRollAction.h" +#include "ManageInventoryAction.h" class ChatCommandActionNodeFactoryInternal : public NamedObjectFactory { @@ -469,6 +470,14 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector& trigger } ) ); + triggers.push_back( + new TriggerNode( + "manage inventory", + { + CreateNextAction(relevance) + } + ) + ); } ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI) @@ -478,6 +487,7 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas this->supported.push_back({ "tell pvp stats", CreateNextAction(relevance).factory }); this->supported.push_back({ "quests", CreateNextAction(relevance).factory }); this->supported.push_back({ "stats", CreateNextAction(relevance).factory }); + this->supported.push_back({ "manage inventory", CreateNextAction(relevance).factory }); this->supported.push_back({ "leave", CreateNextAction(relevance).factory }); this->supported.push_back({ "reputation", CreateNextAction(relevance).factory }); this->supported.push_back({ "log", CreateNextAction(relevance).factory }); diff --git a/src/Ai/Base/Strategy/NonCombatStrategy.cpp b/src/Ai/Base/Strategy/NonCombatStrategy.cpp index 4b6a0e630e9..977343cc26d 100644 --- a/src/Ai/Base/Strategy/NonCombatStrategy.cpp +++ b/src/Ai/Base/Strategy/NonCombatStrategy.cpp @@ -23,6 +23,7 @@ void NonCombatStrategy::InitTriggers(std::vector& triggers) } ) ); + // triggers.push_back(new TriggerNode("seldom", { NextAction("manage inventory", 1.0f) })); triggers.push_back( new TriggerNode( "timer", diff --git a/src/Bot/Engine/Engine.cpp b/src/Bot/Engine/Engine.cpp index 2759485a6b8..a58ef6c3989 100644 --- a/src/Bot/Engine/Engine.cpp +++ b/src/Bot/Engine/Engine.cpp @@ -579,6 +579,13 @@ bool Engine::listenAndExecute(Action& action, Event event) botAI->TellMasterNoFacing(out); } + const std::string& actionName = action.getName(); + + if (actionName == "manage inventory") + { + LOG_ERROR("playerbots", "Pre override result"); + } + actionExecuted = actionExecutionListeners.overrideResult(action, actionExecuted, event); actionExecutionListeners.after(action, actionExecuted, event); return actionExecuted; diff --git a/src/Bot/Factory/PlayerbotFactory.cpp b/src/Bot/Factory/PlayerbotFactory.cpp index 32c3086f7ac..686665355df 100644 --- a/src/Bot/Factory/PlayerbotFactory.cpp +++ b/src/Bot/Factory/PlayerbotFactory.cpp @@ -231,7 +231,7 @@ void PlayerbotFactory::Randomize(bool incremental) // { // return; // } - LOG_DEBUG("playerbots", "{} randomizing {} (level {} class = {})...", (incremental ? "Incremental" : "Full"), + LOG_ERROR("playerbots", "{} randomizing {} (level {} class = {})...", (incremental ? "Incremental" : "Full"), bot->GetName().c_str(), level, bot->getClass()); // LOG_DEBUG("playerbots", "Preparing to {} randomize...", (incremental ? "incremental" : "full")); Prepare(); @@ -500,37 +500,46 @@ void PlayerbotFactory::Refresh() // { // InitEquipment(true); // } - InitAttunementQuests(); - ClearInventory(); - InitAmmo(); - InitFood(); - InitReagents(); - InitConsumables(); - InitPotions(); - InitPet(); - InitPetTalents(); - InitSkills(); - InitClassSpells(); - InitAvailableSpells(); - InitReputation(); - InitSpecialSpells(); - InitMounts(); - InitKeyring(); + if (!PlayerbotAIConfig::instance().disableRandomBotPeriodicRandomization) + { + InitAttunementQuests(); + ClearInventory(); + InitAmmo(); + InitFood(); + InitReagents(); + InitConsumables(); + InitPotions(); + InitPet(); + InitPetTalents(); + InitClassSpells(); + InitAvailableSpells(); + InitSkills(); + InitReputation(); + InitSpecialSpells(); + InitMounts(); + InitKeyring(); + } if (!sPlayerbotAIConfig.equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig.equipmentPersistenceLevel) { InitTalentsTree(true, true, true); } - if (bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel) + if (bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel && !PlayerbotAIConfig::instance().disableRandomBotPeriodicRandomization) { ApplyEnchantAndGemsNew(); } bot->DurabilityRepairAll(false, 1.0f, false); if (bot->isDead()) + { bot->ResurrectPlayer(1.0f, false); - uint32 money = urand(level * 1000, level * 5 * 1000); - if (bot->GetMoney() < money) - bot->SetMoney(money); - // bot->SaveToDB(false, false); + } + + if (!PlayerbotAIConfig::instance().disableRandomBotPeriodicRandomization) + { + uint32 money = urand(level * 1000, level * 5 * 1000); + if (bot->GetMoney() < money) + bot->SetMoney(money); + } +// bot->SaveToDB(false, false); } void PlayerbotFactory::InitConsumables() @@ -1576,135 +1585,6 @@ void Shuffle(std::vector& items) } } -// void PlayerbotFactory::InitEquipmentNew(bool incremental) -// { -// if (incremental) -// { -// DestroyItemsVisitor visitor(bot); -// IterateItems(&visitor, (IterateItemsMask)(ITERATE_ITEMS_IN_BAGS | ITERATE_ITEMS_IN_BANK)); -// } -// else -// { -// DestroyItemsVisitor visitor(bot); -// IterateItems(&visitor, ITERATE_ALL_ITEMS); -// } - -// std::string const& specName = AiFactory::GetPlayerSpecName(bot); -// if (specName.empty()) -// return; - -// // look for upgrades -// for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) -// { -// if (slot == EQUIPMENT_SLOT_TABARD && !bot->GetGuildId()) -// continue; - -// bool isUpgrade = false; -// bool found = false; -// bool noItem = false; -// uint32 quality = urand(ITEM_QUALITY_UNCOMMON, ITEM_QUALITY_EPIC); -// uint32 attempts = 10; -// if (urand(0, 100) < 100 * sPlayerbotAIConfig.randomGearLoweringChance && quality > ITEM_QUALITY_NORMAL) -// { -// quality--; -// } -// // current item; -// Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); -// if (oldItem) -// isUpgrade = true; - -// uint32 itemInSlot = isUpgrade ? oldItem->GetTemplate()->ItemId : 0; - -// uint32 maxLevel = sPlayerbotAIConfig.randomBotMaxLevel; -// if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) -// maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); - -// uint32 minLevel = sPlayerbotAIConfig.randomBotMinLevel; -// if (minLevel < sWorld->getIntConfig(CONFIG_START_PLAYER_LEVEL)) -// minLevel = sWorld->getIntConfig(CONFIG_START_PLAYER_LEVEL); - -// // test -// do -// { -// if (isUpgrade) -// { -// std::vector ids = sRandomItemMgr.GetUpgradeList(bot, specName, slot, 0, itemInSlot); -// if (!ids.empty()) -// Shuffle(ids); - -// for (uint32 index = 0; index < ids.size(); ++index) -// { -// uint32 newItemId = ids[index]; -// if (incremental && !IsDesiredReplacement(oldItem)) -// { -// continue; -// } - -// uint16 dest; -// if (!CanEquipUnseenItem(slot, dest, newItemId)) -// continue; - -// if (oldItem) -// { -// bot->RemoveItem(INVENTORY_SLOT_BAG_0, slot, true); -// oldItem->DestroyForPlayer(bot); -// } - -// Item* newItem = bot->EquipNewItem(dest, newItemId, true); -// if (newItem) -// { -// newItem->AddToWorld(); -// newItem->AddToUpdateQueueOf(bot); -// bot->AutoUnequipOffhandIfNeed(); -// newItem->SetOwnerGUID(bot->GetGUID()); -// EnchantItem(newItem); -// LOG_INFO("playerbots", "Bot {} {}:{} <{}>: Equip: {}, slot: {}, Old item: {}", -// bot->GetGUID().ToString().c_str(), IsAlliance(bot->getRace()) ? "A" : "H", -// bot->GetLevel(), bot->GetName(), newItemId, slot, itemInSlot); -// found = true; -// break; -// } -// } -// } -// else -// { -// std::vector ids = sRandomItemMgr.GetUpgradeList(bot, specName, slot, quality, itemInSlot); -// if (!ids.empty()) -// Shuffle(ids); - -// for (uint32 index = 0; index < ids.size(); ++index) -// { -// uint32 newItemId = ids[index]; -// uint16 dest; -// if (!CanEquipUnseenItem(slot, dest, newItemId)) -// continue; - -// Item* newItem = bot->EquipNewItem(dest, newItemId, true); -// if (newItem) -// { -// bot->AutoUnequipOffhandIfNeed(); -// newItem->SetOwnerGUID(bot->GetGUID()); -// EnchantItem(newItem); -// found = true; -// LOG_INFO("playerbots", "Bot {} {}:{} <{}>: Equip: {}, slot: {}", -// bot->GetGUID().ToString().c_str(), IsAlliance(bot->getRace()) ? "A" : "H", -// bot->GetLevel(), bot->GetName(), newItemId, slot); -// break; -// } -// } -// } -// quality--; -// } while (!found && quality != ITEM_QUALITY_POOR); -// if (!found) -// { -// LOG_INFO("playerbots", "Bot {} {}:{} <{}>: no item for slot {}", -// bot->GetGUID().ToString().c_str(), IsAlliance(bot->getRace()) ? "A" : "H", bot->GetLevel(), -// bot->GetName(), slot); -// continue; -// } -// } -// } - void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) { if (incremental && !sPlayerbotAIConfig.incrementalGearInit) @@ -1993,6 +1873,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) } } +// Unused method. bool PlayerbotFactory::IsDesiredReplacement(Item* item) { if (!item) @@ -2004,11 +1885,6 @@ bool PlayerbotFactory::IsDesiredReplacement(Item* item) { return true; } - // if (!requiredLevel) - // { - // requiredLevel = sRandomItemMgr.GetMinLevelFromCache(proto->ItemId); - // } - uint32 delta = 1 + (80 - bot->GetLevel()) / 10; return proto->Quality < ITEM_QUALITY_RARE || (bot->GetLevel() - requiredLevel) > delta; } @@ -2028,115 +1904,6 @@ inline Item* StoreNewItemInInventorySlot(Player* player, uint32 newItemId, uint3 return nullptr; } -// void PlayerbotFactory::InitSecondEquipmentSet() -// { -// if (bot->getClass() == CLASS_MAGE || bot->getClass() == CLASS_WARLOCK || bot->getClass() == CLASS_PRIEST) -// return; - -// std::map> items; - -// uint32 desiredQuality = itemQuality; -// while (urand(0, 100) < 100 * sPlayerbotAIConfig.randomGearLoweringChance && desiredQuality > -// ITEM_QUALITY_NORMAL) -// { -// desiredQuality--; -// } - -// ItemTemplateContainer const* itemTemplate = sObjectMgr->GetItemTemplateStore(); -// do -// { -// for (auto const& itr : *itemTemplate) -// { -// ItemTemplate const* proto = &itr.second; -// if (!proto) -// continue; -// if (!CanEquipItem(proto, desiredQuality)) -// continue; - -// if (proto->Class == ITEM_CLASS_WEAPON) -// { -// //if (!CanEquipWeapon(proto)) -// // continue; - -// if (sRandomItemMgr.HasStatWeight(proto->ItemId)) -// { -// if (!sRandomItemMgr.GetLiveStatWeight(bot, proto->ItemId)) -// continue; -// } - -// Item* existingItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); -// if (existingItem) -// { -// switch (existingItem->GetTemplate()->SubClass) -// { -// case ITEM_SUBCLASS_WEAPON_AXE: -// case ITEM_SUBCLASS_WEAPON_DAGGER: -// case ITEM_SUBCLASS_WEAPON_FIST: -// case ITEM_SUBCLASS_WEAPON_MACE: -// case ITEM_SUBCLASS_WEAPON_SWORD: -// if (proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE || proto->SubClass == -// ITEM_SUBCLASS_WEAPON_DAGGER || proto->SubClass == ITEM_SUBCLASS_WEAPON_FIST || -// proto->SubClass == ITEM_SUBCLASS_WEAPON_MACE || proto->SubClass == -// ITEM_SUBCLASS_WEAPON_SWORD) continue; -// break; -// default: -// if (proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && proto->SubClass != -// ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && -// proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != -// ITEM_SUBCLASS_WEAPON_SWORD) continue; -// break; -// } -// } -// } -// else if (proto->Class == ITEM_CLASS_ARMOR && proto->SubClass == ITEM_SUBCLASS_ARMOR_SHIELD) -// { -// //if (!CanEquipArmor(proto)) -// // continue; - -// if (sRandomItemMgr.HasStatWeight(proto->ItemId)) -// { -// if (!sRandomItemMgr.GetLiveStatWeight(bot, proto->ItemId)) -// continue; -// } - -// if (Item* existingItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND)) -// if (existingItem->GetTemplate()->SubClass == ITEM_SUBCLASS_ARMOR_SHIELD) -// continue; -// } -// else -// continue; - -// items[proto->Class].push_back(itr.first); -// } -// } while (items[ITEM_CLASS_ARMOR].empty() && items[ITEM_CLASS_WEAPON].empty() && desiredQuality-- > -// ITEM_QUALITY_NORMAL); - -// for (std::map>::iterator i = items.begin(); i != items.end(); ++i) -// { -// std::vector& ids = i->second; -// if (ids.empty()) -// { -// LOG_DEBUG("playerbots", "{}: no items to make second equipment set for slot {}", -// bot->GetName().c_str(), i->first); continue; -// } - -// for (uint32 attempts = 0; attempts < 15; attempts++) -// { -// uint32 index = urand(0, ids.size() - 1); -// uint32 newItemId = ids[index]; - -// if (Item* newItem = StoreNewItemInInventorySlot(bot, newItemId, 1)) -// { -// newItem->SetOwnerGUID(bot->GetGUID()); -// EnchantItem(newItem); -// newItem->AddToWorld(); -// newItem->AddToUpdateQueueOf(bot); -// break; -// } -// } -// } -// } - void PlayerbotFactory::InitBags(bool destroyOld) { for (uint8 slot = INVENTORY_SLOT_BAG_START; slot < INVENTORY_SLOT_BAG_END; ++slot) @@ -2276,6 +2043,7 @@ bool PlayerbotFactory::CanEquipUnseenItem(uint8 slot, uint16& dest, uint32 item) return false; } +// Unused method but looks pretty good. void PlayerbotFactory::InitTradeSkills() { uint16 firstSkill = sRandomPlayerbotMgr.GetValue(bot, "firstSkill"); @@ -2354,7 +2122,6 @@ void PlayerbotFactory::UpdateTradeSkills() void PlayerbotFactory::InitSkills() { - //uint32 maxValue = level * 5; //not used, line marked for removal. bot->UpdateSkillsForLevel(); bot->SetSkill(SKILL_RIDING, 0, 0, 0); @@ -2483,20 +2250,6 @@ void PlayerbotFactory::InitSkills() default: break; } - - // switch (bot->getClass()) - // { - // case CLASS_WARRIOR: - // case CLASS_PALADIN: - // bot->SetSkill(SKILL_PLATE_MAIL, 0, skillLevel, skillLevel); - // break; - // case CLASS_SHAMAN: - // case CLASS_HUNTER: - // bot->SetSkill(SKILL_MAIL, 0, skillLevel, skillLevel); - // break; - // default: - // break; - // } } void PlayerbotFactory::SetRandomSkill(uint16 id) @@ -2929,6 +2682,8 @@ void PlayerbotFactory::InitInstanceQuests() void PlayerbotFactory::ClearInventory() { + LOG_ERROR("playerbots", "Clearing inventory for {}", bot->GetName()); + DestroyItemsVisitor visitor(bot); IterateItems(&visitor); } @@ -3973,6 +3728,7 @@ void PlayerbotFactory::InitGuild() StoreItem(5976, 1); } +// Unused method void PlayerbotFactory::InitImmersive() { uint32 owner = bot->GetGUID().GetCounter(); diff --git a/src/Bot/RandomPlayerbotMgr.cpp b/src/Bot/RandomPlayerbotMgr.cpp index 2166eaa035b..e87c986fbca 100644 --- a/src/Bot/RandomPlayerbotMgr.cpp +++ b/src/Bot/RandomPlayerbotMgr.cpp @@ -1489,7 +1489,7 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot) bool RandomPlayerbotMgr::ProcessBotRandomization(Player* bot) { - if (sPlayerbotAIConfig->disableRandomBotPeriodicRandomization) + if (PlayerbotAIConfig::instance().disableRandomBotPeriodicRandomization) return false; uint64_t botId = bot->GetGUID().GetRawValue(); @@ -1510,7 +1510,7 @@ bool RandomPlayerbotMgr::ProcessBotRandomization(Player* bot) Randomize(bot); - uint32 time = urand(sPlayerbotAIConfig->minRandomBotRandomizeTime, sPlayerbotAIConfig->maxRandomBotRandomizeTime); + uint32 time = urand(PlayerbotAIConfig::instance().minRandomBotRandomizeTime, PlayerbotAIConfig::instance().maxRandomBotRandomizeTime); ScheduleRandomize(botId, time); @@ -1519,7 +1519,7 @@ bool RandomPlayerbotMgr::ProcessBotRandomization(Player* bot) bool RandomPlayerbotMgr::ProcessBotTeleportation(Player* bot) { - if (sPlayerbotAIConfig->disableRandomBotPeriodicTeleportation) + if (PlayerbotAIConfig::instance().disableRandomBotPeriodicTeleportation) return false; uint64_t botId = bot->GetGUID().GetRawValue(); @@ -1533,8 +1533,8 @@ bool RandomPlayerbotMgr::ProcessBotTeleportation(Player* bot) Refresh(bot); RandomTeleportForLevel(bot); - uint32_t time = urand(sPlayerbotAIConfig->minRandomBotTeleportInterval, - sPlayerbotAIConfig->maxRandomBotTeleportInterval); + uint32_t time = urand(PlayerbotAIConfig::instance().minRandomBotTeleportInterval, + PlayerbotAIConfig::instance().maxRandomBotTeleportInterval); ScheduleTeleport(botId, time); @@ -2318,6 +2318,8 @@ void RandomPlayerbotMgr::Randomize(Player* bot) { uint8 level = bot->GetLevel(); + LOG_ERROR("playerbots", "randomizing bot {}", bot->GetName()); + if (bot->InBattleground()) return; diff --git a/src/PlayerbotTitleMgr.cpp b/src/PlayerbotTitleMgr.cpp new file mode 100644 index 00000000000..a4d32c10f53 --- /dev/null +++ b/src/PlayerbotTitleMgr.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include // Added for gender choice + +enum Titles +{ + /** + * Classic + */ + + /** + * PvP + */ + + // Alliance + PRIVATE = 1, + CORPORAL = 2, + SERGEANT = 3, + MASTER_SERGEANT = 4, + SERGEANT_MAJOR = 5, + KNIGHT = 6, + KNIGHT_LIEUTENANT = 7, + KNIGHT_CAPTAIN = 8, + KNIGHT_CHAMPION = 9, + LIEUTENANT_COMMANDER = 10, + COMMANDER = 11, + MARSHAL = 12, + FIELD_MARSHAL = 13, + GRAND_MARSHAL = 14, + + // Horde + SCOUT = 15, + GRUNT = 16, + SERGEANT_H = 17, + SENIOR_SERGEANT = 18, + FIRST_SERGEANT = 19, + STONE_GUARD = 20, + BLOOD_GUARD = 21, + LEGIONNAIRE = 22, + CENTURION = 23, + CHAMPION = 24, + LIEUTENANT_GENERAL = 25, + GENERAL = 26, + WARLORD = 27, + HIGH_WARLORD = 28, + + /* + * PvE + */ + + SCARAB_LORD = 46, + + /** + * The Burning Crusade + */ + + /* + * PvP + */ + + // Arena + + GLADIATOR = 42, + DUELIST = 43, + RIVAL = 44, + CHALLENGER = 45, + MERCILESS_GLADIATOR = 62, + VENGEFUL_GLADIATOR = 71, + BRUTAL_GLADIATOR = 80, + + /** + * PvE + */ + + CHAMPION_OF_THE_NAARU = 53, + OF_THE_SHATTERED_SUN = 63, + HAND_OF_ADAL = 64, + + /** + * Wrath of the Lich King + */ + + /** + * PvP + */ + + BATTLEMASTER = 72, + ARENA_MASTER = 82, + + // Alliance + + JUSTICAR = 48, + + // Horde + + CONQUEROR = 47, + + /** + * Seasonal Events + */ + + ELDER = 74, + FLAME_WARDEN = 75, // Alliance + FLAME_KEEPER = 76, // Horde + + /** + * Miscellaneous + */ + + THE_EXALTED = 77, // 40 Exalted reputation + THE_EXPLORER = 78, // Explore all areas + THE_DIPLOMAT = 79, // Exalted Timbermaw, Sporeggar & Kurenai/Mag'har + THE_SEEKER = 81, // Complete 3000 quests + SALTY = 83, // Fishing + CHEF = 84, // Cooking + + /** + * Realm first + */ + + /** + * Unused + */ + + THE_SUPREME = 85, + OF_THE_TEN_STORMS = 86, + OF_THE_EMERALD_DREAM = 87, // Used in retail in Dragonflight only + PROPHET = 89, + THE_MALEFIC = 90, + STALKER = 91, // Used in retail in Warlords of Draenor only + OF_THE_EBON_BLADE = 92, + ARCHMAGE = 93, // Used in retail in Legion only + WARBRINGER = 94, // Used in retail in Legion only + ASSASSIN = 95, + GRAND_MASTER_ALCHEMIST = 96, + GRAND_MASTER_BLACKSMITH = 97, + IRON_CHEF = 98, + GRAND_MASTER_ENCHANTER = 99, + GRAND_MASTER_ENGINEER = 100, + DOCTOR = 101, + GRAND_MASTER_ANGLER = 102, + +}; + +class PlayerbotTitleMgr +{ +public: +private: +}; diff --git a/src/Script/Playerbots.cpp b/src/Script/Playerbots.cpp index 51b6c3f8b43..23ba096314f 100644 --- a/src/Script/Playerbots.cpp +++ b/src/Script/Playerbots.cpp @@ -536,7 +536,7 @@ class PlayerbotsGuildScript : public GuildScript void OnAddMember(Guild* guild, Player* player, [[maybe_unused]] uint8_t& plRank) { - if (sRandomPlayerbotMgr->IsRandomBot(player)) + if (RandomPlayerbotMgr::instance().IsRandomBot(player)) return; uint32_t guildId = guild->GetId(); @@ -557,7 +557,7 @@ class PlayerbotsGuildScript : public GuildScript */ void OnGuildRemoveMember(Guild* guild, Player* player) { - if (sRandomPlayerbotMgr->IsRandomBot(player)) + if (RandomPlayerbotMgr::instance().IsRandomBot(player)) return; uint32_t guildId = guild->GetId(); diff --git a/src/domain/item/inspector/abstract/AbstractItemInspector.h b/src/domain/item/inspector/abstract/AbstractItemInspector.h new file mode 100644 index 00000000000..ff8c662848e --- /dev/null +++ b/src/domain/item/inspector/abstract/AbstractItemInspector.h @@ -0,0 +1,261 @@ +#pragma once + +#include + +#include "ObjectGuid.h" +#include "ObjectAccessor.h" +#include "SharedDefines.h" +#include "Player.h" +#include "ObjectMgr.h" +#include "ItemTemplate.h" +#include "Item.h" + +#include "AbstractInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" + +class AbstractItemInspector : public AbstractInspector +{ +public: + virtual ItemActionStruct determineItemAction() const + { + return this->getDefaultItemAction(); + }; + + uint8_t getItemInventorySlot() const + { + const Item* const item = this->getCurrentItem(); + + if (item == nullptr) + return 0; + + return item->GetBagSlot(); + } + + uint8_t getItemSlot() const + { + const Item* const item = this->getCurrentItem(); + + if (item == nullptr) + return 0; + + return item->GetSlot(); + } + + uint8_t getItemPosition() const + { + const Item* const item = this->getCurrentItem(); + + if (item == nullptr) + return 0; + + return item->GetPos(); + } + + uint32_t getCurrentItemClass() const + { + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return 0; + + return itemTemplate->Class; + } + + uint32_t getCurrentItemSubclass() const + { + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return 255; + + return itemTemplate->SubClass; + } + + uint32_t getItemMaxStackSize() const + { + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return 0; + + return itemTemplate->GetMaxStackSize(); + } + + ItemUpdateState getItemUpdateState() const + { + const Item* const item = this->getCurrentItem(); + + if (item == nullptr) + return ITEM_REMOVED; + + return item->GetState(); + } + + bool itemIsSellable() const + { + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return itemTemplate->SellPrice != 0; + } + + bool itemIsInWorld() const + { + const Item* const item = this->getCurrentItem(); + + if (item == nullptr) + return false; + + return item->IsInWorld(); + } + + bool itemIsInUnsafeContainer() const + { + const Item* const item = this->getCurrentItem(); + + if (item == nullptr) + return true; + + const ObjectGuid containingField = item->GetGuidValue(ITEM_FIELD_CONTAINED); + + return containingField == ObjectGuid::Empty; + } + + const ItemTemplate* getCurrentItemTemplate() const + { + ObjectMgr* const objectManager = ObjectMgr::instance(); + + if (objectManager == nullptr) + return nullptr; + + const Item* const item = this->getCurrentItem(); + + if (item == nullptr) + return nullptr; + + return item->GetTemplate(); + } + + uint32_t getCurrentItemTemplateLowGUID() const + { + return this->itemTemplateLowGUID; + } + + const Item* getCurrentItem() const + { + const ObjectGuid playerGUID = ObjectGuid::Create(this->playerLowGUID); + const Player* const player = ObjectAccessor::FindPlayer(playerGUID); + + if (player == nullptr) + return nullptr; + + const ObjectGuid itemGUID = ObjectGuid::Create(this->itemLowGUID); + Item* const item = player->GetItemByGuid(itemGUID); + + return item; + } + + Item* getMutableCurrentItem() const + { + const ObjectGuid playerGUID = ObjectGuid::Create(this->playerLowGUID); + const Player* const player = ObjectAccessor::FindPlayer(playerGUID); + + if (player == nullptr) + return nullptr; + + const ObjectGuid itemGUID = ObjectGuid::Create(this->itemLowGUID); + Item* const item = player->GetItemByGuid(itemGUID); + + return item; + } + +protected: + AbstractItemInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : + itemLowGUID(itemLowGUID), + playerLowGUID(playerLowGUID), + itemTemplateLowGUID(0) + { + const Player* const player = ObjectAccessor::FindPlayer(ObjectGuid::Create(this->playerLowGUID)); + + if (player == nullptr) + return; + + const Item* const item = player->GetItemByGuid(ObjectGuid::Create(this->itemLowGUID)); + + if (item == nullptr) + { + this->itemTemplateLowGUID = 0; + + return; + } + + const ItemTemplate* const itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + this->itemTemplateLowGUID = 0; + + return; + } + + this->itemTemplateLowGUID = itemTemplate->ItemId; + } + + AbstractItemInspector& operator=(AbstractItemInspector const&) = delete; + + ItemActionStruct getDefaultItemAction() const + { + return this->getKeepItemAction(); + } + + ItemActionStruct getKeepItemAction() const + { + return { + .action = ItemActionEnum::NONE, + .inventorySlot = 0, + .equipmentSlot = 0 + }; + } + + ItemActionStruct getForbiddenItemAction() const + { + return this->getDestroyItemAction(); + } + + ItemActionStruct getDestroyItemAction() const + { + return { + .action = ItemActionEnum::DESTROY, + .inventorySlot = this->getItemInventorySlot(), + .equipmentSlot = 0 + }; + } + + ItemActionStruct getSellAction() const + { + const bool sellable = this->itemIsSellable(); + + if (sellable) + return { + .action = ItemActionEnum::SELL, + .inventorySlot = this->getItemInventorySlot(), + .equipmentSlot = 0 + }; + + return { + .action = ItemActionEnum::DESTROY, + .inventorySlot = this->getItemInventorySlot(), + .equipmentSlot = 0 + }; + } + + const uint64_t itemLowGUID; + const uint64_t playerLowGUID; + uint32_t itemTemplateLowGUID; +}; diff --git a/src/domain/item/inspector/abstract/definition/enum/ItemActionEnum.h b/src/domain/item/inspector/abstract/definition/enum/ItemActionEnum.h new file mode 100644 index 00000000000..4bdbc93083e --- /dev/null +++ b/src/domain/item/inspector/abstract/definition/enum/ItemActionEnum.h @@ -0,0 +1,9 @@ +#pragma once + +enum class ItemActionEnum +{ + NONE = 0, + EQUIP = 1, + SELL = 2, + DESTROY = 3 +}; diff --git a/src/domain/item/inspector/abstract/definition/struct/ItemActionStruct.h b/src/domain/item/inspector/abstract/definition/struct/ItemActionStruct.h new file mode 100644 index 00000000000..c26b7ab0570 --- /dev/null +++ b/src/domain/item/inspector/abstract/definition/struct/ItemActionStruct.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include "ItemActionEnum.h" + +struct ItemActionStruct +{ + const ItemActionEnum action; + const uint8_t inventorySlot; + const uint32_t equipmentSlot; +}; diff --git a/src/domain/item/inspector/armor/ArmorItemInspector.h b/src/domain/item/inspector/armor/ArmorItemInspector.h new file mode 100644 index 00000000000..051fb4dc3fa --- /dev/null +++ b/src/domain/item/inspector/armor/ArmorItemInspector.h @@ -0,0 +1,129 @@ +#pragma once + +#include + +#include "ItemTemplate.h" + +#include "StatsWeightCalculator.h" +#include "AbstractItemInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" +#include "InventoryService.h" + +class ArmorItemInspector : public AbstractItemInspector +{ +public: + ArmorItemInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractItemInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemClass = this->getCurrentItemClass(); + + return AbstractItemInspector::isInspectable() && itemClass == ITEM_CLASS_ARMOR; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + const ObjectGuid playerGUID = ObjectGuid::Create(this->playerLowGUID); + Player* player = ObjectAccessor::FindPlayer(playerGUID); + + if (player == nullptr) + return this->getDefaultItemAction(); + + Item* const item = this->getMutableCurrentItem(); + + if (item == nullptr) + return this->getDefaultItemAction(); + + const bool canUseItem = player->CanUseItem(item); + + if (!canUseItem) + return this->getDefaultItemAction(); + + player = ObjectAccessor::FindPlayer(playerGUID); + + if (player == nullptr) + return this->getDefaultItemAction(); + + StatsWeightCalculator statisticsWeightCalculator = StatsWeightCalculator(player); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return this->getDefaultItemAction(); + + std::vector slots = InventoryService::GetInstance().getItemEquipmentSlots(itemTemplate); + const ItemTemplate* const refreshedItemTemplate = this->getCurrentItemTemplate(); + + if (refreshedItemTemplate == nullptr) + return this->getDefaultItemAction(); + + const float newItemStatisticsWeight = statisticsWeightCalculator.CalculateItem(refreshedItemTemplate->ItemId); + + statisticsWeightCalculator.Reset(); + + for (uint8_t i = 0; i < slots.size(); ++i) + { + const uint32_t equipmentSlot = slots[i]; + player = ObjectAccessor::FindPlayer(playerGUID); + + if (player == nullptr) + return this->getDefaultItemAction(); + + const Item* const currentlyEquippedItem = player->GetItemByPos(equipmentSlot); + + if (currentlyEquippedItem == nullptr) + return { + .action = ItemActionEnum::EQUIP, + .inventorySlot = this->getItemInventorySlot(), + .equipmentSlot = equipmentSlot + }; + + const ItemTemplate* const currentlyEquippedItemTemplate = currentlyEquippedItem->GetTemplate(); + + if (currentlyEquippedItemTemplate == nullptr) + return this->getDefaultItemAction(); + + const float existingItemStatisticsWeight = statisticsWeightCalculator.CalculateItem(currentlyEquippedItemTemplate->ItemId); + + if (existingItemStatisticsWeight < newItemStatisticsWeight) + { + return { + .action = ItemActionEnum::EQUIP, + .inventorySlot = this->getItemInventorySlot(), + .equipmentSlot = equipmentSlot + }; + } + } + + return this->getSellAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return true; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() const + { + return {}; + } +}; diff --git a/src/domain/item/inspector/consumable/AbstractConsumableInspector.h b/src/domain/item/inspector/consumable/AbstractConsumableInspector.h new file mode 100644 index 00000000000..48127cf7f24 --- /dev/null +++ b/src/domain/item/inspector/consumable/AbstractConsumableInspector.h @@ -0,0 +1,74 @@ +#pragma once + +#include "SharedDefines.h" +#include "ItemTemplate.h" +#include "SpellMgr.h" + +#include "AbstractItemInspector.h" + +class AbstractConsumableInspector : public AbstractItemInspector +{ +public: + bool isInspectable() const override + { + const uint8_t itemClass = this->getCurrentItemClass(); + + return itemClass == ITEM_CLASS_CONSUMABLE; + } + + bool isHealthPotion() const + { + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + const SpellMgr* const spellMgr = SpellMgr::instance(); + + if (spellMgr == nullptr) + return false; + + const SpellInfo* const spellInfo = spellMgr->GetSpellInfo(itemTemplate->Spells[0].SpellId); + + if (spellInfo == nullptr) + return false; + + if (spellInfo->HasEffect(SPELL_EFFECT_HEAL)) + return true; + + return false; + } + + bool isManaPotion() const + { + const ItemTemplate* itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + const SpellMgr* const spellMgr = SpellMgr::instance(); + + if (spellMgr == nullptr) + return false; + + const SpellInfo* const spellInfo = spellMgr->GetSpellInfo(itemTemplate->Spells[0].SpellId); + + if (spellInfo == nullptr) + return false; + + if (spellInfo->HasEffect(SPELL_EFFECT_ENERGIZE) && spellInfo->PowerType == POWER_MANA) + return true; + + return false; + } + +protected: + AbstractConsumableInspector( + uint64_t playerGUID, + uint64_t itemGUID + ) : + AbstractItemInspector(playerGUID, itemGUID) + {} + + AbstractConsumableInspector& operator=(AbstractConsumableInspector const&) = delete; +}; diff --git a/src/domain/item/inspector/consumable/consumable/ConsumableConsumableInspector.h b/src/domain/item/inspector/consumable/consumable/ConsumableConsumableInspector.h new file mode 100644 index 00000000000..115e6937778 --- /dev/null +++ b/src/domain/item/inspector/consumable/consumable/ConsumableConsumableInspector.h @@ -0,0 +1,960 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractConsumableInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" +#include "GlobalPlayerInspector.h" + +class ConsumableConsumableInspector : public AbstractConsumableInspector +{ +public: + ConsumableConsumableInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractConsumableInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemSubclass = this->getCurrentItemSubclass(); + + return AbstractConsumableInspector::isInspectable() && itemSubclass == ITEM_SUBCLASS_CONSUMABLE; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + const ObjectGuid playerGUID = ObjectGuid::Create(this->playerLowGUID); + Player* player = ObjectAccessor::FindPlayer(playerGUID); + + if (player == nullptr) + return this->getDefaultItemAction(); + + const uint8_t playerClass = player->getClass(); + + if (playerClass == CLASS_ROGUE && this->isRoguePoisonItem()) + return this->getDefaultItemAction(); + + if (playerClass == CLASS_MAGE && this->isMageItem()) + return this->getDefaultItemAction(); + + if (playerClass == CLASS_WARLOCK && this->isWarlockItem()) + return this->getDefaultItemAction(); + + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return this->getDefaultItemAction(); + + player = ObjectAccessor::FindPlayer(playerGUID); + + if (player == nullptr) + return this->getDefaultItemAction(); + + if (this->isQuestConsumableItem() && player->HasQuestForItem(itemTemplate->ItemId)) + return this->getDefaultItemAction(); + + if (!this->isUnwantedItem()) + return this->getDefaultItemAction(); + + return this->getSellAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + + bool isUnwantedItem() const + { + const std::unordered_set unwantedItems = this->getUnwantedItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return unwantedItems.contains(itemTemplate->ItemId); + } + + bool isQuestConsumableItem() const + { + const std::unordered_set questConumableItems = this->getQuestConsumableItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return questConumableItems.contains(itemTemplate->ItemId); + } + + bool isRoguePoisonItem() const + { + const std::unordered_set roguePoisonItems = this->getRoguePoisonsItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return roguePoisonItems.contains(itemTemplate->ItemId); + } + + bool isUsefulConsumableItem() const + { + const std::unordered_set usefulConsumableItems = this->getUsefulConsumableItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return usefulConsumableItems.contains(itemTemplate->ItemId); + } + + bool isWarlockItem() const + { + const std::unordered_set warlockItems = this->getWarlockItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return warlockItems.contains(itemTemplate->ItemId); + } + + bool isMageItem() const + { + const std::unordered_set mageItems = this->getMageItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return mageItems.contains(itemTemplate->ItemId); + } + +protected: + + std::unordered_set getForbiddenItemsGUIDs() const + { + return { + 1199, // Charged Soulstone + 1700, // Admin Warlord's Claymore + 1995, // Deprecated Cat's Paw + 3438, // Ankh of Resurrection + 4420, // NPC Equip 4420 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 4423, // NPC Equip 4423 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 4842, // test => Illegal item that should not even be present in the core as it is not present in any WotLK database + 5046, // Locked Gift => Illegal item that should not even be present in the core as it is not present in any WotLK database + 5047, // Skull Wrapping Paper => Illegal item that should not even be present in the core as it is not present in any WotLK database + 5224, // NPC Equip 5224 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 5225, // NPC Equip 5225 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 5378, // Shane Test (DELETE ME) => Illegal item that should not even be present in the core as it is not present in any WotLK database + 6244, // ggggfg => Illegal item that should not even be present in the core as it is not present in any WotLK database + 6638, // Air Sapta => Apparently this sapta is the only one not needed by shamans? + 6852, // Eternal Eye => Quest item seemingly linked to no quest + 6927, // Big Will's Ear => Quest item seemingly linked to no quest. It was supposedly dropped by Big Will but the quest requires killing him without looting him. + 7147, // NPC Equip 7147 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 7168, // NPC Equip 7168 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 7388, // Skull Key => Quest item seemingly linked to no quest + 9232, // NPC Equip 9232 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 9319, // Nimboya's Laden Pike => Quest item seemingly linked to no quest + 10419, // NPC Equip 10419 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 10448, // NPC Equip 10448 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 10449, // NPC Equip 10449 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 10451, // NPC Equip 10451 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 10452, // NPC Equip 10452 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 10453, // NPC Equip 10453 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 11182, // NPC Equip 11182 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 11183, // NPC Equip 11183 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 11616, // DEBUG Samophlange Manual Page + 12246, // NPC Equip 12246 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 12385, // test => Illegal item that should not even be present in the core as it is not present in any WotLK database + 12439, // NPC Equip 12439 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 12441, // NPC Equip 12441 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 13294, // NPC Equip 13294 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 17350, // NPC Equip 17350 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 17729, // NPC Equip 17729 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 18595, // Blood Opal => Item not present in WotLK. + 19063, // NPC Equip 19063 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 19977, // NPC Equip 19977 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 20365, // NPC Equip 20365 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 20386, // NPC Equip 20386 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 20473, // NPC Equip 20473 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 20905, // NPC Equip 20905 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21238, // Winter Veil Cookie UNUSED + 21556, // NPC Equip 21556 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21739, // Lunar Festival Invitation DEBUG + 21832, // NPC Equip 21832 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21834, // NPC Equip 21834 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21922, // NPC Equip 21922 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21961, // NPC Equip 21961 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21965, // NPC Equip 21965 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21966, // NPC Equip 21966 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21967, // NPC Equip 21967 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21968, // NPC Equip 21968 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21969, // NPC Equip 21969 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21970, // NPC Equip 21970 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21971, // NPC Equip 21971 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21972, // NPC Equip 21972 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21973, // NPC Equip 21973 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21974, // NPC Equip 21974 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21976, // NPC Equip 21976 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21977, // NPC Equip 21977 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21978, // NPC Equip 21978 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 22118, // NPC Equip 22118 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 22124, // NPC Equip 22124 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 22125, // NPC Equip 22125 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 22126, // NPC Equip 22126 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 22127, // NPC Equip 22127 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 22129, // NPC Equip 22129 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 22323, // NPC Equip 22323 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 23163, // Performer's Wand => Unobtainable item + 23164, // Bubbly Beverage => Unobtainable item + 23175, // Tasty Summer Treat => Unobtainable item (iCoke promotion) + 23176, // Fizzy Energy Drink => Unobtainable item (iCoke promotion?) + 23209, // NPC Equip 23209 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 23210, // NPC Equip 23210 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 23496, // NPC Equip 23496 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 23640, // NPC Equip 23640 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 23641, // NPC Equip 23641 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 23715, // Permanent Lung Juice Cocktail => Unobtainable item + 23718, // Permanent Ground Scorpok Assay => Unobtainable item + 23719, // Permanent Cerebral Cortex Compound => Unobtainable item + 23721, // Permanent Gizzard Gum => Unobtainable item + 23722, // Permanent R.O.I.D.S. => Unobtainable item + 23794, // Permanent Sheen of Zanza => Unobtainable item + 23795, // Permanent Spirit of Zanza => Unobtainable item + 23796, // Permanent Swiftness of Zanza => Unobtainable item + 23872, // NPC Equip 23872 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 23982, // NPC Equip 23982 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 26046, // NPC Equip 26046 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 26047, // NPC Equip 26047 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 27504, // NPC Equip 27504 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 28036, // NPC Equip 28036 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 28110, // Fat Gnome and Little Elf => Unobtainable item, probably removed due to tasteless reference to a crime against humanity. + 28131, // Reaver Buster Launcher => Seemingly unobtainable item. + 29585, // NPC Equip 29585 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 29805, // Socrethar's Head => TBC Beta quest item, unobtainable. + 29868, // QAEnchant Gloves +26 Attack Power + 29877, // NPC Equip 29877 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 31954, // NPC Equip 31954 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 32424, // NPC Equip 32424 => Wrong item, should be "Blade's Edge Ogre Brew" + 32426, // NPC Equip 32426 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 32669, // NPC Equip 32669 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 32913, // NPC Equip 32913 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33063, // Really Tough Brewfest Bread + 33090, // NPC Equip 33090 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33100, // NPC Equip 33100 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33217, // NPC Equip 33217 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33218, // Goblin Gumbo + 33219, // Goblin Gumbo Kettle + 33570, // NPC Equip 33570 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33572, // NPC Equip 33572 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33573, // NPC Equip 33573 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33574, // NPC Equip 33574 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33600, // NPC Equip 33600 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33601, // NPC Equip 33601 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33602, // NPC Equip 33602 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33603, // NPC Equip 33603 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33623, // NPC Equip 33623 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33624, // NPC Equip 33624 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33625, // NPC Equip 33625 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33626, // NPC Equip 33626 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33785, // NPC Equip 33785 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33786, // NPC Equip 33786 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33787, // NPC Equip 33787 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33788, // NPC Equip 33788 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33800, // NPC Equip 33800 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34046, // NPC Equip 34046 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34047, // NPC Equip 34047 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34048, // NPC Equip 34048 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34071, // NPC Equip 34071 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34151, // NPC Equip 34151 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34152, // NPC Equip 34152 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34153, // NPC Equip 34153 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34154, // NPC Equip 34154 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34155, // NPC Equip 34155 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34156, // NPC Equip 34156 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34250, // NPC Equip 34250 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34251, // NPC Equip 34251 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34252, // NPC Equip 34252 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 35120, // NPC Equip 35120 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 35667, // NPC Equip 35667 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 38165, // NPC Equip 38165 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 38236, // NPC Equip 38236 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 38685, // NPC Equip 38685 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 38702, // NPC Equip 38702 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 38704, // NPC Equip 38704 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 39163, // Test expire transform + 39600, // NPC Equip 39600 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 40308, // NPC Equip 40308 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 42437, // NPC Equip 42437 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 42764, // NPC Equip 42764 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 44624, // NPC Equip 44624 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 44628, // NPC Equip 44628 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 44630, // NPC Equip 44630 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 44804, // NPC Equip 44804 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 44805, // NPC Equip 44805 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 44807, // Indalamar's Holy Hand Grenade + 44813, // NPC Equip 44813 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 44816, // NPC Equip 44816 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 44832, // Squirt Gun [PH] + 44866, // Faithful Mule + 44867, // NPC Equip 44867 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 44964, // NPC Equip 44964 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 45126, // Trusty Mount [PH] + 46717, // NPC Equip 46717 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 46765, // Blue War Fuel (Mountain Dew Promotion) + 46766, // Red War Fuel (Mountain Dew Promotion) + 48416, // NPC Equip 48416 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 48601, // Red Rider Air Rifle Ammo + 49223, // Permission Slip + 49224, // NPC Equip 49224 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 49225, // NPC Equip 49225 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 49349, // NPC Equip 49349 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 49372, // NPC Equip 49372 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 49373, // Permission Slip + 49374, // NPC Equip 49374 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 49692, // NPC Equip 49692 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 50076, // NPC Equip 50076 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 50091, // NPC Equip 50091 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 50092, // NPC Equip 50092 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 50093, // Pet Prep: A Beginner's Guide + 50164, // NPC Equip 50164 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 52202, // Elemental Sapta + 52361, // NPC Equip 52361 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 53476, // NPC Equip 53476 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 53477, // NPC Equip 53477 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 54647, // NPC Equip 54647 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 54822, // Sen'jin Overcloak + }; + } + + std::unordered_set getUnwantedItemsGUIDs() const + { + return { + 835, // Large Rope Net + 1127, // Flash Bundle + 1176, // Smelling Salts + 1187, // Spiked Collar + 1191, // Bag of Marbles + 1322, // Fishliver Oil + 1434, // Glowing Wax Stick + 1970, // Restoring Balm + 3434, // Slumber Sand + 3456, // Dog Whistle + 4546, // Call of the Raptor + 4598, // Goblin Fishing Pole + 4941, // Really Sticky Glue + 4945, // Faintly Glowing Skull + 4952, // Stormstout + 4953, // Trogg Brew + 5421, // Fiery Blaze Enchantment + 5457, // Severed Voodoo Claw + 5845, // Flank of Meat + 5951, // Moist Towelette + 7516, // Tabetha's Instructions => Bots don't need instructions. + 8410, // R.O.I.D.S. + 8411, // Lung Juice Cocktail + 8412, // Ground Scorpok Assay + 8423, // Cerebral Cortex Compound + 8424, // Gizzard Gum, + 8432, // Eau de Mixilpixil + 10684, // Colossal Parachute + 10830, // M73 Frag Grenade + 12924, // Ritual Candle + 13149, // Eldarathian Tome of Summoning Vol. 1 + 13151, // The Mystic Studies of Hor'ank + 13152, // The Story With No Conclusion + 13153, // Tome of Mal'cin Vorail + 13154, // Jael'marin's Studies of the Arcane + 13508, // Eye of Arachnida + 13509, // Clutch of Foresight + 13514, // Wail of the Banshee + 13813, // Blessed Sunfruit Juice + 14894, // Lily Root + 15723, // Tea with Sugar + 15778, // Mechanical Yeti + 17048, // Rumsey Rum + 18209, // Energized Sparkplug + 18269, // Gordok Green Grog + 18284, // Kreeg's Stout Beatdown + 18297, // Thornling Seed + 19060, // Warsong Gulch Enriched Ration + 19061, // Warsong Gulch Iron Ration + 19062, // Warsong Gulch Field Ration + 19318, // Bottled Alterac Spring Water + 19997, // Harvest Nectar + 20062, // Arathi Basin Enriched Ration + 20063, // Arathi Basin Field Ration + 20064, // Arathi Basin Iron Ration + 20079, // Spirit of Zanza + 20080, // Sheen of Zanza + 20081, // Swiftness of Zanza + 20222, // Defiler's Enriched Ration + 20223, // Defiler's Field Ration + 20224, // Defiler's Iron Ration + 20225, // Highlander's Enriched Ration + 20226, // Highlander's Field Ration + 20227, // Highlander's Iron Ration + 20388, // Lollipop + 20389, // Candy Corn + 20390, // Candy Bar + 20397, // Hallowed Wand - Pirate + 20398, // Hallowed Wand - Ninja + 20399, // Hallowed Wand - Leper Gnome + 20409, // Hallowed Wand - Ghost + 20410, // Hallowed Wand - Bat + 20411, // Hallowed Wand - Skeleton + 20413, // Hallowed Wand - Random + 20414, // Hallowed Wand - Wisp + 20557, // Hallow's End Pumpkin Treat + 21212, // Fresh Holly + 21241, // Winter Veil Eggnog + 21325, // Mechanical Greench + 21537, // Festival Dumplings + 21711, // Lunar Festival Invitation + 21744, // Lucky Rocket Cluster + 21745, // Elder's Moonstone + 21815, // Love Token + 22192, // Bloodkelp Elixir of Dodging + 22193, // Bloodkelp Elixir of Resistance + 22236, // Buttermilk Delight + 22237, // Dark Desire + 22238, // Very Berry Cream + 22239, // Sweet Surprise + 22259, // Unbestowed Friendship Bracelet + 22260, // Friendship Bracelet + 22743, // Bloodsail Sash + 22778, // Scourgebane Infusion + 22779, // Scourgebane Draught + 22795, // Fel Blossom + 23161, // Freshly-Squeezed Lemonade + 23194, // Lesser Mark of the Dawn + 23195, // Mark of the Dawn + 23196, // Greater Mark of the Dawn + 23211, // Toasted Smorc + 23215, // Bag of Smorc Ingredients + 23246, // Fiery Festival Brew + 23326, // Midsummer Sausage + 23327, // Fire-toasted Bun + 23379, // Cinder Bracers + 23435, // Elderberry Pie + 23492, // Suntouched Special Reserve + 23584, // Loch Modan Lager + 23585, // Stouthammer Lite + 23586, // Aerie Peak Pale Ale + 23985, // Crystal of Vitality + 23986, // Crystal of Insight + 23989, // Crystal of Ferocity + 24006, // Grunt's Waterskin + 24007, // Footman's Waterskin + 24408, // Edible Stalks + 24421, // Nagrand Cherry + 24429, // Expedition Flare + 24540, // Edible Fern + 25498, // Rough Stone Statue + 25880, // Coarse Stone Statue + 25881, // Heavy Stone Statue + 25882, // Solid Stone Statue + 25883, // Dense Stone Statue + 25884, // Primal Stone Statue + 27317, // Elemental Sapta + 27553, // Crimson Steer Energy Drink + 29482, // Ethereum Essence + 30309, // Stonebreaker Brew + 30499, // Brightstone Wine + 30615, // Halaani Whiskey + 30858, // Peon Sleep Potion + 31337, // Orb of the Blackwhelp + 31449, // Distilled Stalker Sight + 31450, // Sealth of the Stalker + 31451, // Pure Energy + 31535, // Bloodboil Poison + 32563, // Grilled Picnic Treat + 32971, // Water Bucket + 33023, // Savory Sausage + 33024, // Pickled Sausage + 33025, // Spicy Smoked Sausage + 33026, // The Golden Link + 33028, // Barleybrew Light + 33028, // Barleybrew Dark + 33030, // Barleybrew Clear + 33031, // Thunder 45 + 33032, // Thunderbrew Ale + 33033, // Thunderbrew Stout + 33034, // Gordok Grog + 33035, // Ogre Mead + 33036, // Mudder's Milk + 33043, // The Essential Brewfest Pretzel + 33226, // Tricky Treat + 33234, // Iced Berry Slush + 33236, // Fizzy Faire Drink "Classic" + 33246, // Funnel Cake + 33254, // Afrazi Forest Strider Drumstick + 33312, // Mana Sapphire + 33929, // Brewfest Brew + 33956, // Harkor's Home Brew + 34017, // Small Step Brew + 34018, // Long Stride Brew + 34019, // Path of Brew + 34020, // Jungle River Water + 34021, // Brewdo Magic + 34022, // Stout Shrunken Head + 34044, // B-Ball + 34063, // Dried Sausage + 34064, // Succulent Sausage + 34065, // Spiced Onion Cheese + 34068, // Weighted Jack-o'-Lantern + 34077, // Crudely Wrapped Gift + 34410, // Honeyed Holiday Ham + 34412, // Sparkling Apple Cider + 34599, // Juggling Torch + 35223, // Papa Hummel's Old-Fashioned Pet Biscuit + 35946, // Fizzcrank Practice Parachute + 36877, // Folded Letter + 37431, // Fetch Ball + 37488, // Wild Winter Pilsner + 37489, // Izzard's Ever Flavor + 37490, // Aromatic Honey Brew + 37491, // Metok's Bubble Bock + 37492, // Springtime Stout + 37493, // Blackrock Lager + 37494, // Stranglethorn Brew + 37495, // Draenic Pale Ale + 37496, // Binary Brew + 37497, // Autumnal Acorn Ale + 37498, // Bartlett's Bitter Brew + 37499, // Lord of Frost's Private Label + 37582, // Pyroblast Cinnamon Ball + 37583, // G.N.E.R.D.S. + 37584, // Soothing Spearmint Candy + 37585, // Chewy Fel Taffy + 37604, // Tooth Pick + 37750, // Fresh Brewfest Hops + 37863, // Direbrew's Remote + 37878, // Worg's Blood Elixir + 37898, // Wild Winter Pilsner + 37899, // Izzard's Ever Flavor + 37900, // Aromatic Honey Brew + 37901, // Metok's Bubble Bock + 37902, // Springtime Stout + 37903, // Blackrock Lager + 37904, // Stranglethorn Brew + 37905, // Draenic Pale Ale + 37906, // Binary Brew + 37907, // Autumnal Acorn Ale + 37908, // Bartlett's Bitter Brew + 37909, // Lord of Frost's Private Label + 37925, // Experimental Mixture + 38291, // Ethereal Mutagen + 38294, // Ethereal Liqueur + 38300, // Diluted Ethereum Essence + 38320, // Dire Brew + 38518, // Cro's Apple + 38577, // Party G.R.E.N.A.D.E. + 38587, // Empty Brewfest Stein + 38626, // Empty Brew Bottle + 39476, // Fresh Goblin Brewfest Hops + 39477, // Fresh Dwarven Brewfest Hops + 39738, // Thunderbrew's Hard Ale + 42342, // Bag of Popcorn + 42350, // Bag of Peanuts + 42381, // Anguish Ale + 42436, // Chocolate Celebration Cake + 42438, // Lovely Cake + 42439, // Big Berry Pie + 43088, // Dalaran Apple Bowl + 43135, // Fate Rune of Fleet Feet + 43352, // Pet Grooming Kit + 43462, // Airy Pale Ale + 43470, // Worg Tooth Oatmeal Stout + 43471, // Rork Red Ribbon + 43472, // Snowfall Lager + 43473, // Drakefire Chile Ale + 43489, // Mohawk Grenade + 43626, // Happy Pet Snack + 44064, // Nepeta Leaf + 44065, // Oracle Secret Solution + 44435, // Windle's Lighter + 44481, // Grindgear Toy Gorilla + 44482, // Trusty Copper Racer + 44599, // Zippy Copper Racer + 44601, // Heavy Copper Racer + 44610, // Fresh Dalaran Bread + 44612, // Dalaran Swiss Wheel + 44613, // Aged Dalaran Sharp Wheel + 44621, // Bottle of Dalaran White + 44622, // Cask of Dalaran White + 44623, // Bottle of Dalaran Red + 44625, // Bottle of Aged Dalaran Red + 44626, // Cask of Aged Dalaran Red + 44627, // Bottle of Peaked Dalaran Red + 44629, // Cask of Peaked Dalaran Red + 44632, // Cask of Dalaran Red + 44698, // Intravenous Healing Potion + 44716, // Mysterious Fermented Liquid + 44792, // Blossoming Branch + 44812, // Turkey Shooter + 44817, // The Mischief Maker + 44818, // Noblegarden Egg + 44844, // Turkey Caller + 44849, // Tiny Green Ragdoll + 44943, // Icy Prism + 45047, // Sandbox Tiger + 46319, // Tournament Brew + 46399, // Thunder's Plunder + 46400, // Barleybrew Gold + 46401, // Crimson Stripe + 46402, // Promise of the Pandaren + 46403, // Chuganpug's Delight + 46711, // Spirit Candle + 46718, // Orange Marigold + 46725, // Red Rider Air Rifle + 46783, // Pink Gumball + 49288, // Little Ivory Raptor Whistle + 49289, // Little White Stallion Bridle + 49856, // "VICTORY" Perfume + 49857, // "Enchantress" Perfume + 49858, // "Forever" Perfume + 49859, // "Bravado" Cologne + 49860, // "Wizardry" Cologne + 49861, // "STALWART" Cologne + 49936, // Lovely Stormwind Card + 49937, // Lovely Undercity Card + 49938, // Lovely Darnassus Card + 49939, // Lovely Orgrimmar Card + 49940, // Lovely Ironforge Card + 49941, // Lovely Thunder Bluff Card + 49942, // Lovely Exodar Card + 49943, // Lovely Silvermoon City Card + 50163, // Lovely Rose + 54455, // Paint Bomb + }; + } + + std::unordered_set getQuestConsumableItemsGUIDs() const + { + return { + 1262, // Keg of Thunderbrew Lager + 5880, // Crate With Holes + 6074, // War Horn Mouthpiece + 6464, // Wailing Essence + 6486, // Singed Scale + 6635, // Earth Sapta + 6636, // Fire Sapta + 6637, // Water Sapta + 6781, // Bartleby's Mug + 6782, // Marshal Haggard's Badge + 6812, // Case of Elunite + 6841, // Vial of Phlogiston + 6842, // Furen's Instructions + 6851, // Essence of the Exile + 6926, // Furen's Notes + 6929, // Bath'rah's Parchment + 7127, // Powdered Azurite + 7227, // Balnir Snapdragons + 7266, // Ur's Treatise on Shadow Magic + 7627, // Dolanaar Delivery + 7629, // Ukor's Burden + 7970, // E.C.A.C. + 8048, // Emerald Dreamcatcher + 8095, // Hinott's Oil + 8548, // Divino-matic Rod + 9546, // Simple Scroll + 9569, // Hallowed Scroll + 10621, // Runed Scroll + 10663, // Essence of Hakkar + 10687, // Empty Vial Labeled #1 + 10688, // Empty Vial Labeled #2 + 10689, // Empty Vial Labeled #3 + 10690, // Empty Vial Labeled #4 + 11148, // Samophlange Manual Page + 11405, // Giant Silver Vein + 11413, // Nagmara's Filled Vial + 11914, // Empty Cursed Ooze Jar + 11948, // Empty Tainted Ooze Jar + 11953, // Empty Pure Sample Jar + 12533, // Roughshod Pike + 12650, // Attuned Dampener + 12712, // Warosh's Mojo + 12884, // Arnak's Hoof + 12885, // Pamela's Doll + 12886, // Pamela's Doll's Head + 12894, // Joseph's Wedding Ring + 12922, // Empty Canteen + 13155, // Resonating Skull + 13156, // Mystic Crystal + 13562, // Remains of Trey Lightforge + 13703, // Kodo Bone + 13761, // Frozen Eggs + 14542, // Kravel's Crate + 15314, // Bundle of Relics + 16282, // Bundle of Hides + 20470, // Solanian's Scrying Orb + 20471, // Scroll of Scourge Magic + 20472, // Solanian's Journal + 23361, // Cleansing Vial + 23417, // Sanctified Crystal + 23645, // Seer's Relic + 24330, // Drain Schematics + 24355, // Ironvine Seeds + 24407, // Uncatalogued Species + 24474, // Violet Scrying Crystal + 25465, // Stormcrow Amulet + 28038, // Seaforium PU-36 Explosive Nether Modulator + 28132, // Area 52 Special + 28513, // Demonic Rune Stone + 28554, // Shredder Spare Parts + 28571, // Blank Scroll + 28580, // B'naar Console Transcription + 28607, // Sunfury Disguise + 28635, // Sunfury Arcanist Robes + 28636, // Sunfury Researcher Gloves + 28637, // Sunfury Guardsman Medallion + 28784, // Unyielding Banner Scrap => Maybe unused quest item? + 29162, // Galaxis Soul Shard + 29324, // Warp-Attuned Orb + 29443, // Bloodmaul Brutebane Brew + 29624, // First Half of Socrethar's Stone + 29625, // Second Half of Socrethar's Stone + 29699, // Socrethar's Teleportation Stone + 29778, // Phase Disruptor + 29796, // Socrethar's Teleportation Stone + 29905, // Kael's Vial Remnant + 29906, // Vashj's Vial Remnant + 30260, // Voren'thal's Package + 30540, // Tally's Waiver (Unsigned) + 30811, // Scroll of Demonic Unbanishing + 31121, // Costume Scraps + 31122, // Overseer Disguise + 31495, // Grishnath Orb + 31517, // Dire Pinfeather + 31518, // Exorcism Feather + 31702, // Challenge from the Horde + 31795, // Draenei Prayer Beads + 32406, // Skyguard Blasting Charges + 32602, // Crystalforged Darkrune + 32848, // Explosives Package + 33079, // Murloc Costume + 33099, // Intact Plague Container + 33277, // Tome of Thomas Thomson + 33349, // Plague Vials + 33614, // Empty Apothecary's Flask => Possibly unaccessible to players but still linked to a quest. + 33615, // Flask of Vrykul Blood => Possibly unaccessible to players but still linked to a quest. + 33616, // Unstable Mix => Possibly unaccessible to players but still linked to a quest. + 33617, // Balanced Concoction => Possibly unaccessible to players but still linked to a quest. + 33619, // Lysander's Strain + 33621, // Plague Spray + 33797, // Portable Brewfest Keg + 34023, // Empty Apothecary's Flask + 34024, // Flask of Vrykul Blood + 34076, // Fish Bladder + 34475, // Arcane Charges + 35704, // Incendiary Explosives + 37173, // Geomancer's Orb + 37198, // Prototype Neural Needler + 37265, // Tua'kea's Breathing Bladder + 37661, // Gossamer Potion + 37877, // Silver Feather + 38629, // Orders from the Lich King + 38657, // Freya's Ward + 39698, // Light-infused Artifact + 40390, // Vic's Emergency Air Tank + 40482, // Dual-plagued Brain + 40725, // Steam-Powered Auctioneer + 44576, // Bright Flare + 44806, // Brightly Colored Shell Fragment + 45784, // Thorim's Sigil + 45786, // Hodir's Sigil + 45787, // Mimiron's Sigil + 45788, // Freya's Sigil + 45791, // Sigils of the Watchers + 45814, // Freya's Sigil + 45815, // Hodir's Sigil + 45816, // Mimiron's Sigil + 45817, // Thorim's Sigil + 45855, // Sigils of the Watchers + }; + } + + std::unordered_set getRoguePoisonsItemsGUIDs() const + { + return { + 2892, // Deadly Poison + 2893, // Deadly Poison II + 3775, // Crippling Poison + 3776, // Crippling Poison II + 5237, // Mind-numbing Poison + 6947, // Instant Poison + 6949, // Instant Poison II + 6950, // Instant Poison III + 6951, // Mind-numbing Poison II => Useless in WotLK but useful in earlier expansions, kept for IP use. + 8926, // Instant Poison IV + 8927, // Instant Poison V + 8928, // Instant Poison VI + 8984, // Deadly Poison III + 8985, // Deadly Poison IV + 9186, // Mind-numbing Poison III => Useless in WotLK but useful in earlier expansions, kept for IP use. + 10918, // Wound Poison + 10920, // Wound Poison II + 10921, // Wound Poison III + 10922, // Wound Poison IV + 20844, // Deadly Poison V + 21835, // Anesthetic Poison + 21927, // Instant Poison VII + 22053, // Deadly Poison VI + 22054, // Deadly Poison VII + 22055, // Wound Poison V + }; + } + + std::unordered_set getUsefulConsumableItemsGUIDs() const + { + return { + 18606, // Alliance Battle Standard + 18607, // Horde Battle Standard + 19045, // Stormpike Battle Standard + 19046, // Frostwolf Battle Standard + 19150, // Sentinel Basic Care Package + 19151, // Sentinel Standard Care Package + 19152, // Sentinel Advanced Care Package + 19153, // Outrider Advanced Care Package + 19154, // Outrider Basic Care Package + 19155, // Outrider Standard Care Package + 19182, // Darkmoon Faire Prize Ticket + 19425, // Mysterious Lockbox + 20228, // Defiler's Advanced Care Package + 20229, // Defiler's Basic Care Package + 20230, // Defiler's Standard Care Package + 20231, // Arathor Advanced Care Package + 20233, // Arathor Basic Care Package + 20236, // Arathor Standard Care Package + 21740, // Small Rocket Recipes + 21741, // Cluster Rocket Recipes + 21742, // Large Rocket Recipes + 21743, // Large Cluster Rocket Recipes + 21746, // Lucky Red Envelope + 24140, // Blackened Urn => Useless in WotLK but useful in earlier expansions, kept for IP use. + 24289, // Chrono-beacon + 24494, // Tears of the Goddess + 24520, // Honor Hold Favor + 24522, // Thrallmar Favor + 24579, // Mark of Honor Hold + 24581, // Mark of Thrallmar + 27388, // Mr. Pinchy + 29735, // Holy Dust + 30690, // Power Converter + 32408, // Naj'entus Spine + 32542, // Imp in a Ball + 33865, // Amani Hex Stick + 33926, // Sealed Scroll Case + 33927, // Brewfest Pony Keg + 33928, // Hollowed Bone Decanter + 34583, // Aldor Supplies Package + 34584, // Scryer Supplies Package + 34585, // Scryer Supplies Package + 34587, // Aldor Supplies Package + 34592, // Aldor Supplies Package + 34593, // Scryer Supplies Package + 34594, // Scryer Supplies Package + 34595, // Aldor Supplies Package + 35232, // Shattered Sun Supplies + 34686, // Brazier of Dancing Flames + 34850, // Midsummer Ground Flower + 35512, // Pocket Full of Snow + 35945, // Brilliant Glass + 36748, // Dark Brewmaiden's Brew + 36862, // Worn Troll Dice + 36863, // Decahedral Dwarven Dice + 37815, // Emerald Essence + 37859, // Amber Essence + 37860, // Ruby Essence + 38186, // Ethereal Credit + 38233, // Path of Illidan + 39878, // Mysterious Egg + 39883, // Cracked Egg + 40110, // Haunted Memento + 41426, // Magically Wrapped Gift + 44113, // Small Spice Bag + 44606, // Toy Train Set + 44717, // Disgusting Jar + 44718, // Ripe Disgusting Jar + 44751, // Hyldnir Spoils + 45011, // Stormwind Banner + 45013, // Thunder Bluff Banner + 45014, // Orgrimmar Banner + 45015, // Sen'jin Banner + 45016, // Undercity Banner + 45017, // Silvermoon City Banner + 45018, // Ironforge Banner + 45019, // Gnomeregan Banner + 45020, // Exodar Banner + 45021, // Darnassus Banner + 45038, // Fragment of Val'anyr + 45039, // Shattered Fragments of Val'anyr + 45057, // Wind-Up Train Wrecker + 45063, // Foam Sword Rack + 45506, // Archivum Data Disc + 45705, // Argent Tournament Invitation + 45798, // Heroic Celestial Planetarium Key + 45857, // Archivum Data Disc + 45896, // Unbound Fragments of Val'anyr + 45897, // Reforged Hammer of Ancient Kings + 46029, // Magnetic Core + 46779, // Path of Cenarius + 46780, // Ogre Pinata + 46843, // Argent Crusader's Banner + 46847, // Seaforium Bombs + 47030, // Huge Seaforium Bombs + 47541, // Argent Pony Bridle + 49631, // Standard Apothecary Serving Kit + 50307, // Infernal Spear + 50471, // The Heartbreaker + 54212, // Instant Statue Pedestal + 54437, // Tiny Green Ragdoll + 54438, // Tiny Blue Ragdoll + 54651, // Gnomeregan Pride + 54653, // Darkspear Pride + }; + } + + std::unordered_set getWarlockItemsGUIDs() const + { + return { + 5232, // Minor Soulstone + 16892, // Lesser Soulstone + 16893, // Soulstone + 16895, // Greater Soulstone + 16896, // Major Soulstone + 22116, // Master Soulstone + 36895, // Demonic Soulstone + }; + } + + std::unordered_set getMageItemsGUIDs() const + { + return { + 5513, // Mana Jade + 8007, // Mana Citrine + 8008, // Mana Ruby + 22044, // Mana Emerald + 36799, // Mana Gem + }; + } +}; diff --git a/src/domain/item/inspector/consumable/elixir/ConsumableElixirInspector.h b/src/domain/item/inspector/consumable/elixir/ConsumableElixirInspector.h new file mode 100644 index 00000000000..4ffd23ae612 --- /dev/null +++ b/src/domain/item/inspector/consumable/elixir/ConsumableElixirInspector.h @@ -0,0 +1,83 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractConsumableInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" +#include "GlobalPlayerInspector.h" + +class ConsumableElixirInspector : public AbstractConsumableInspector +{ +public: + ConsumableElixirInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractConsumableInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemSubclass = this->getCurrentItemSubclass(); + + return AbstractConsumableInspector::isInspectable() && itemSubclass == ITEM_SUBCLASS_ELIXIR; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + const uint32_t maxStackSize = this->getItemMaxStackSize(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return this->getDefaultItemAction(); + + const ObjectGuid playerGUID = ObjectGuid::Create(this->playerLowGUID); + Player* player = ObjectAccessor::FindPlayer(playerGUID); + + if (player == nullptr) + return this->getDefaultItemAction(); + + const uint32_t totalQuantity = player->GetItemCount(itemTemplate->ItemId); + const ItemTemplate* const refreshedItemTemplate = this->getCurrentItemTemplate(); + + if (refreshedItemTemplate == nullptr) + return this->getDefaultItemAction(); + + if (totalQuantity < maxStackSize) + return { + .action = ItemActionEnum::NONE, + .inventorySlot = 0, + .equipmentSlot = 0 + }; + + return this->getSellAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() const + { + return {}; + } + +}; diff --git a/src/domain/item/inspector/consumable/potion/ConsumablePotionInspector.h b/src/domain/item/inspector/consumable/potion/ConsumablePotionInspector.h new file mode 100644 index 00000000000..84362ec6d3f --- /dev/null +++ b/src/domain/item/inspector/consumable/potion/ConsumablePotionInspector.h @@ -0,0 +1,97 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractConsumableInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" +#include "GlobalPlayerInspector.h" + +class ConsumablePotionInspector : public AbstractConsumableInspector +{ +public: + ConsumablePotionInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractConsumableInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemSubclass = this->getCurrentItemSubclass(); + + return AbstractConsumableInspector::isInspectable() && itemSubclass == ITEM_SUBCLASS_POTION; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + const uint32_t maxStackSize = this->getItemMaxStackSize(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return this->getDefaultItemAction(); + + const ObjectGuid playerGUID = ObjectGuid::Create(this->playerLowGUID); + Player* player = ObjectAccessor::FindPlayer(playerGUID); + + if (player == nullptr) + return this->getDefaultItemAction(); + + const uint32_t totalQuantity = player->GetItemCount(itemTemplate->ItemId); + + if (totalQuantity < maxStackSize) + return { + .action = ItemActionEnum::NONE, + .inventorySlot = 0, + .equipmentSlot = 0 + }; + + return this->getSellAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() const + { + return { + 2461, // Deprecated Elemental Resistance Potion + 2462, // Deprecated Potion of Lesser Invulnerability (Fix) + 5632, // Deprecated Cowardly Flight Potion + 23578, // Diet McWeaksauce + 23579, // The McWeaksauce Classic + 23696, // [PH] Potion of Heightened Senses [DEP] + 23698, // [PH] Nature Resist Potion [DEP] + 30793, // NPC Equip 30793 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 32762, // Rulkster's Brain Juice + 32763, // Rulkster's Secret Sauce + 34646, // NPC Equip 34646 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 37926, // NPC Equip 37926 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 39971, // NPC Equip 39971 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 40677, // NPC Equip 40677 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 42548, // NPC Equip 42548 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 45276, // Jillian's Genius Juice + 45277, // Jillian's Savior Sauce + }; + } + +}; diff --git a/src/domain/item/inspector/container/ContainerInspector.h b/src/domain/item/inspector/container/ContainerInspector.h new file mode 100644 index 00000000000..d1b78bdbbff --- /dev/null +++ b/src/domain/item/inspector/container/ContainerInspector.h @@ -0,0 +1,122 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractItemInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" +#include "GlobalPlayerInspector.h" + +class ContainerInspector : public AbstractItemInspector +{ +public: + ContainerInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractItemInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemSubclass = this->getCurrentItemSubclass(); + + return AbstractItemInspector::isInspectable() && itemSubclass == ITEM_SUBCLASS_CONTAINER; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + const ItemTemplate* itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return this->getDestroyItemAction(); + + const ObjectGuid playerGUID = ObjectGuid::Create(this->playerLowGUID); + Player* player = ObjectAccessor::FindPlayer(playerGUID); + + if (player == nullptr) + return this->getDefaultItemAction(); + + const uint8_t playerClass = player->getClass(); + const uint32_t inventoryType = itemTemplate->InventoryType; + + // @TODO: Add hunter quiver equipment decision (NOT process) here. + if (inventoryType == INVTYPE_QUIVER && playerClass != CLASS_HUNTER) + return this->getSellAction(); + + const Item* item = this->getCurrentItem(); + + if (item == nullptr) + return this->getKeepItemAction(); + + const Bag* bag = item->ToBag(); + + if (bag == nullptr) + return this->getKeepItemAction(); + + const uint8_t bagSize = bag->GetBagSize(); + + for (uint8_t i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) + { + const ObjectGuid playerGUID = ObjectGuid::Create(this->playerLowGUID); + Player* player = ObjectAccessor::FindPlayer(playerGUID); + + if (player == nullptr) + return this->getDefaultItemAction(); + + const Item* const equippedItem = player->GetItemByPos(i); + + if (equippedItem == nullptr) + return { + .action = ItemActionEnum::EQUIP, + .inventorySlot = this->getItemInventorySlot(), + .equipmentSlot = i + }; + + const Bag* const equippedBag = equippedItem->ToBag(); + + if (equippedBag == nullptr) + return this->getKeepItemAction(); + + const uint8_t equippedBagSize = equippedBag->GetBagSize(); + + if (equippedBagSize < bagSize) + return { + .action = ItemActionEnum::EQUIP, + .inventorySlot = this->getItemInventorySlot(), + .equipmentSlot = i + }; + + } + + return this->getSellAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + +protected: + + std::unordered_set getForbiddenItemsGUIDs() const + { + return {}; + } + +}; diff --git a/src/domain/item/inspector/gem/GemInspector.h b/src/domain/item/inspector/gem/GemInspector.h new file mode 100644 index 00000000000..89490de86c1 --- /dev/null +++ b/src/domain/item/inspector/gem/GemInspector.h @@ -0,0 +1,57 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractItemInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" + +class GemInspector : public AbstractItemInspector +{ +public: + GemInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractItemInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemClass = this->getCurrentItemClass(); + + return AbstractItemInspector::isInspectable() && itemClass == ITEM_CLASS_GEM; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + return this->getSellAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() const + { + return {}; + } +}; diff --git a/src/domain/item/inspector/generic/GenericItemInspector.h b/src/domain/item/inspector/generic/GenericItemInspector.h new file mode 100644 index 00000000000..ea9d9a4a480 --- /dev/null +++ b/src/domain/item/inspector/generic/GenericItemInspector.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractItemInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" + +class GenericItemInspector : public AbstractItemInspector +{ +public: + GenericItemInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractItemInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemClass = this->getCurrentItemClass(); + + return AbstractItemInspector::isInspectable() && itemClass == ITEM_CLASS_GENERIC; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + return this->getSellAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() const + { + return {}; + } +}; diff --git a/src/domain/item/inspector/global/GlobalItemInspector.h b/src/domain/item/inspector/global/GlobalItemInspector.h new file mode 100644 index 00000000000..dad0d1b7ea4 --- /dev/null +++ b/src/domain/item/inspector/global/GlobalItemInspector.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractItemInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" + +class GlobalItemInspector : public AbstractItemInspector +{ +public: + GlobalItemInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractItemInspector(playerLowGUID, itemLowGUID) + {} +}; diff --git a/src/domain/item/inspector/glyph/GlyphInspector.h b/src/domain/item/inspector/glyph/GlyphInspector.h new file mode 100644 index 00000000000..0efaccaa615 --- /dev/null +++ b/src/domain/item/inspector/glyph/GlyphInspector.h @@ -0,0 +1,57 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractItemInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" + +class GlyphInspector : public AbstractItemInspector +{ +public: + GlyphInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractItemInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemClass = this->getCurrentItemClass(); + + return AbstractItemInspector::isInspectable() && itemClass == ITEM_CLASS_GLYPH; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + return this->getSellAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() const + { + return {}; + } +}; diff --git a/src/domain/item/inspector/key/KeyInspector.h b/src/domain/item/inspector/key/KeyInspector.h new file mode 100644 index 00000000000..0bc61be391b --- /dev/null +++ b/src/domain/item/inspector/key/KeyInspector.h @@ -0,0 +1,57 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractItemInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" + +class KeyInspector : public AbstractItemInspector +{ +public: + KeyInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractItemInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemClass = this->getCurrentItemClass(); + + return AbstractItemInspector::isInspectable() && itemClass == ITEM_CLASS_KEY; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + return this->getKeepItemAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() const + { + return {}; + } +}; diff --git a/src/domain/item/inspector/miscellaneous/MiscellaneousItemInspector.h b/src/domain/item/inspector/miscellaneous/MiscellaneousItemInspector.h new file mode 100644 index 00000000000..f178f4b14dc --- /dev/null +++ b/src/domain/item/inspector/miscellaneous/MiscellaneousItemInspector.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractItemInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" + +class MiscellaneousItemInspector : public AbstractItemInspector +{ +public: + MiscellaneousItemInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractItemInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemClass = this->getCurrentItemClass(); + + return AbstractItemInspector::isInspectable() && itemClass == ITEM_CLASS_MISC; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + return this->getSellAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() const + { + return {}; + } +}; diff --git a/src/domain/item/inspector/money/MoneyInspector.h b/src/domain/item/inspector/money/MoneyInspector.h new file mode 100644 index 00000000000..4cbd3fe84f9 --- /dev/null +++ b/src/domain/item/inspector/money/MoneyInspector.h @@ -0,0 +1,65 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractItemInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" + +class MoneyInspector : public AbstractItemInspector +{ +public: + MoneyInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractItemInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemClass = this->getCurrentItemClass(); + + return AbstractItemInspector::isInspectable() && itemClass == ITEM_CLASS_MONEY; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + return this->getKeepItemAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() const + { + return { + 37711, // Currency Token Test Token 1 + 37742, // Currency Token Test Token 2 + 38644, // Currency Token Test Token 3 + 41749, // Birmingham Test Item 3 + 43308, // Honor Points (item, not the real currency) + 43949, // zzzOLDDaily Quest Faction Token + 44209, // NPC Equip 44209 => Illegal item that should not even be present in the core as it is not present in any WotLK database + }; + } +}; diff --git a/src/domain/item/inspector/permanent/PermanentItemInspector.h b/src/domain/item/inspector/permanent/PermanentItemInspector.h new file mode 100644 index 00000000000..b2a42aaf84f --- /dev/null +++ b/src/domain/item/inspector/permanent/PermanentItemInspector.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractItemInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" + +class PermanentItemInspector : public AbstractItemInspector +{ +public: + PermanentItemInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractItemInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemClass = this->getCurrentItemClass(); + + return AbstractItemInspector::isInspectable() && itemClass == ITEM_CLASS_PERMANENT; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + return this->getSellAction(); + } + + bool isForbiddenItem() const + { + return true; + } +}; diff --git a/src/domain/item/inspector/projectile/ProjectItemInspector.h b/src/domain/item/inspector/projectile/ProjectItemInspector.h new file mode 100644 index 00000000000..02985b1b2d7 --- /dev/null +++ b/src/domain/item/inspector/projectile/ProjectItemInspector.h @@ -0,0 +1,67 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractItemInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" + +class ProjectItemInspector : public AbstractItemInspector +{ +public: + ProjectItemInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractItemInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemClass = this->getCurrentItemClass(); + + return AbstractItemInspector::isInspectable() && itemClass == ITEM_CLASS_PROJECTILE; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + const ObjectGuid playerGUID = ObjectGuid::Create(this->playerLowGUID); + Player* player = ObjectAccessor::FindPlayer(playerGUID); + + if (player == nullptr) + return this->getDefaultItemAction(); + + const uint8_t playerClass = player->getClass(); + + if (playerClass == CLASS_HUNTER) + return this->getKeepItemAction(); + + return this->getSellAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() const + { + return {}; + } +}; diff --git a/src/domain/item/inspector/quest/QuestItemInspector.h b/src/domain/item/inspector/quest/QuestItemInspector.h new file mode 100644 index 00000000000..551d8144a2a --- /dev/null +++ b/src/domain/item/inspector/quest/QuestItemInspector.h @@ -0,0 +1,70 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractItemInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" + +class QuestItemInspector : public AbstractItemInspector +{ +public: + QuestItemInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractItemInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemClass = this->getCurrentItemClass(); + + return AbstractItemInspector::isInspectable() && itemClass == ITEM_CLASS_QUEST; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return this->getDefaultItemAction(); + + const ObjectGuid playerGUID = ObjectGuid::Create(this->playerLowGUID); + Player* player = ObjectAccessor::FindPlayer(playerGUID); + + if (player == nullptr) + return this->getDefaultItemAction(); + + if (player->HasQuestForItem(itemTemplate->ItemId)) + return this->getKeepItemAction(); + + return this->getDestroyItemAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() const + { + return {}; + } +}; diff --git a/src/domain/item/inspector/quiver/QuiverItemInspector.h b/src/domain/item/inspector/quiver/QuiverItemInspector.h new file mode 100644 index 00000000000..d4725693695 --- /dev/null +++ b/src/domain/item/inspector/quiver/QuiverItemInspector.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractItemInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" + +class QuiverItemInspector : public AbstractItemInspector +{ +public: + QuiverItemInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractItemInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemClass = this->getCurrentItemClass(); + + return AbstractItemInspector::isInspectable() && itemClass == ITEM_CLASS_QUIVER; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + return this->getSellAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() const + { + return {}; + } +}; diff --git a/src/domain/item/inspector/reagent/ReagentItemInspector.h b/src/domain/item/inspector/reagent/ReagentItemInspector.h new file mode 100644 index 00000000000..9c6a6df8e03 --- /dev/null +++ b/src/domain/item/inspector/reagent/ReagentItemInspector.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractItemInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" + +class ReagentItemInspector : public AbstractItemInspector +{ +public: + ReagentItemInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractItemInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemClass = this->getCurrentItemClass(); + + return AbstractItemInspector::isInspectable() && itemClass == ITEM_CLASS_REAGENT; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + return this->getSellAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() const + { + return {}; + } +}; diff --git a/src/domain/item/inspector/recipe/RecipeItemInspector.h b/src/domain/item/inspector/recipe/RecipeItemInspector.h new file mode 100644 index 00000000000..f492a60c8a0 --- /dev/null +++ b/src/domain/item/inspector/recipe/RecipeItemInspector.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractItemInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" + +class RecipeItemInspector : public AbstractItemInspector +{ +public: + RecipeItemInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractItemInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemClass = this->getCurrentItemClass(); + + return AbstractItemInspector::isInspectable() && itemClass == ITEM_CLASS_RECIPE; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + return this->getSellAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() const + { + return {}; + } +}; diff --git a/src/domain/item/inspector/trade-good/TradeGoodItemInspector.h b/src/domain/item/inspector/trade-good/TradeGoodItemInspector.h new file mode 100644 index 00000000000..ba57f7d537d --- /dev/null +++ b/src/domain/item/inspector/trade-good/TradeGoodItemInspector.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractItemInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" + +class TradeGoodItemInspector : public AbstractItemInspector +{ +public: + TradeGoodItemInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractItemInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemClass = this->getCurrentItemClass(); + + return AbstractItemInspector::isInspectable() && itemClass == ITEM_CLASS_TRADE_GOODS; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + return this->getSellAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() const + { + return {}; + } +}; diff --git a/src/domain/item/inspector/weapon/WeaponItemInspector.h b/src/domain/item/inspector/weapon/WeaponItemInspector.h new file mode 100644 index 00000000000..44c2e9d7012 --- /dev/null +++ b/src/domain/item/inspector/weapon/WeaponItemInspector.h @@ -0,0 +1,126 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "StatsWeightCalculator.h" +#include "AbstractItemInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" +#include "InventoryService.h" +#include "GlobalPlayerInspector.h" + +class WeaponItemInspector : public AbstractItemInspector +{ +public: + WeaponItemInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractItemInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const override + { + const uint8_t itemClass = this->getCurrentItemClass(); + + return AbstractItemInspector::isInspectable() && itemClass == ITEM_CLASS_WEAPON; + } + + ItemActionStruct determineItemAction() const override + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + const ObjectGuid playerGUID = ObjectGuid::Create(this->playerLowGUID); + Player* player = ObjectAccessor::FindPlayer(playerGUID); + + if (player == nullptr) + return this->getDefaultItemAction(); + + Item* currentItem = this->getMutableCurrentItem(); + + if (currentItem == nullptr) + return this->getDefaultItemAction(); + + const bool canUseItem = player->CanUseItem(currentItem); + + if (!canUseItem) + return this->getDefaultItemAction(); + + StatsWeightCalculator statisticsWeightCalculator(player); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return this->getDefaultItemAction(); + + std::vector slots = InventoryService::GetInstance().getItemEquipmentSlots(itemTemplate); + const ItemTemplate* const refreshedItemTemplate = this->getCurrentItemTemplate(); + + if (refreshedItemTemplate == nullptr) + return this->getDefaultItemAction(); + + const float newItemStatisticsWeight = statisticsWeightCalculator.CalculateItem(refreshedItemTemplate->ItemId); + + statisticsWeightCalculator.Reset(); + + for (uint8_t i = 0; i < slots.size(); ++i) + { + const uint32_t equipmentSlot = slots[i]; + player = ObjectAccessor::FindPlayer(playerGUID); + + if (player == nullptr) + return this->getDefaultItemAction(); + + const Item* const currentlyEquippedItem = player->GetItemByPos(equipmentSlot); + + if (currentlyEquippedItem == nullptr) + return { + .action = ItemActionEnum::EQUIP, + .inventorySlot = this->getItemInventorySlot(), + .equipmentSlot = equipmentSlot + }; + + const ItemTemplate* const currentlyEquippedItemTemplate = currentlyEquippedItem->GetTemplate(); + + if (currentlyEquippedItemTemplate == nullptr) + return this->getDefaultItemAction(); + + const float existingItemStatisticsWeight = statisticsWeightCalculator.CalculateItem(currentlyEquippedItemTemplate->ItemId); + + if (existingItemStatisticsWeight < newItemStatisticsWeight) + { + return { + .action = ItemActionEnum::EQUIP, + .inventorySlot = this->getItemInventorySlot(), + .equipmentSlot = equipmentSlot + }; + } + } + + return this->getSellAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() const + { + return {}; + } +}; diff --git a/src/domain/player/facade/abstract/AbstractPlayerFacade.h b/src/domain/player/facade/abstract/AbstractPlayerFacade.h new file mode 100644 index 00000000000..d3d8f2fde27 --- /dev/null +++ b/src/domain/player/facade/abstract/AbstractPlayerFacade.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include "ObjectGuid.h" +#include "ObjectAccessor.h" +#include "SharedDefines.h" +#include "Player.h" + +class AbstractPlayerFacade +{ +public: + AbstractPlayerFacade( + uint64_t playerGUID + ) : + playerGUID(playerGUID) + {} + + AbstractPlayerFacade& operator=(AbstractPlayerFacade const&) = delete; + + Player* getCurrentPlayer() const + { + const ObjectGuid playerFullGUID = ObjectGuid::Create(this->playerGUID); + Player* const player = ObjectAccessor::FindPlayer(playerFullGUID); + + return player; + } + + bool isMutable() const + { + return true; + } + +protected: + const uint64_t playerGUID; +}; diff --git a/src/domain/player/facade/equipment/PlayerEquipmentFacade.h b/src/domain/player/facade/equipment/PlayerEquipmentFacade.h new file mode 100644 index 00000000000..4bccc2ef6b1 --- /dev/null +++ b/src/domain/player/facade/equipment/PlayerEquipmentFacade.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +#include "Packet.h" +#include "ItemPackets.h" +#include "ObjectGuid.h" +#include "ObjectAccessor.h" +#include "SharedDefines.h" +#include "Player.h" +#include "WorldSession.h" + +#include "AbstractPlayerFacade.h" +#include "PlayerEquipmentFacadeResultEnum.h" +#include "GlobalPlayerInspector.h" +#include "InventoryService.h" + +class PlayerEquipmentFacade : public AbstractPlayerFacade +{ +public: + PlayerEquipmentFacade( + uint64_t playerGUID + ) : + AbstractPlayerFacade(playerGUID) + {} + + PlayerEquipmentFacadeResultEnum equipItem(uint64_t itemLowGUID, EquipmentSlots destinationSlot) + { + if (destinationSlot < EQUIPMENT_SLOT_START || destinationSlot > EQUIPMENT_SLOT_TABARD) + return PlayerEquipmentFacadeResultEnum::IMPOSSIBLE; + + GlobalPlayerInspector playerInspector(this->playerGUID); + GlobalItemInspector itemInspector(this->playerGUID, itemLowGUID); + + if (!playerInspector.canEquipItem(itemLowGUID, destinationSlot)) + return PlayerEquipmentFacadeResultEnum::IMPOSSIBLE; + + Player* const player = this->getCurrentPlayer(); + + if (player == nullptr) + return PlayerEquipmentFacadeResultEnum::IMPOSSIBLE; + + player->SwapItem(itemInspector.getItemPosition(), destinationSlot); + + return PlayerEquipmentFacadeResultEnum::REQUESTED; + } +}; diff --git a/src/domain/player/facade/equipment/definition/enum/PlayerEquipmentFacadeResultEnum.h b/src/domain/player/facade/equipment/definition/enum/PlayerEquipmentFacadeResultEnum.h new file mode 100644 index 00000000000..d7a4a90a6f2 --- /dev/null +++ b/src/domain/player/facade/equipment/definition/enum/PlayerEquipmentFacadeResultEnum.h @@ -0,0 +1,6 @@ +enum class PlayerEquipmentFacadeResultEnum +{ + REQUESTED = 0, + IMPOSSIBLE = 1 +}; + diff --git a/src/domain/player/facade/inventory/PlayerInventoryFacade.h b/src/domain/player/facade/inventory/PlayerInventoryFacade.h new file mode 100644 index 00000000000..d3472cd5068 --- /dev/null +++ b/src/domain/player/facade/inventory/PlayerInventoryFacade.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +#include "Packet.h" +#include "ItemPackets.h" +#include "ObjectGuid.h" +#include "ObjectAccessor.h" +#include "SharedDefines.h" +#include "Player.h" +#include "WorldSession.h" + +#include "AbstractPlayerFacade.h" +#include "PlayerInventoryFacadeResultEnum.h" +#include "GlobalPlayerInspector.h" +#include "InventoryService.h" +#include "GlobalItemInspector.h" + +class PlayerInventoryFacade : public AbstractPlayerFacade +{ +public: + PlayerInventoryFacade( + uint64_t playerGUID + ) : + AbstractPlayerFacade(playerGUID) + {} + + PlayerInventoryFacadeResultEnum destroyItem(uint64_t itemLowGUID, EquipmentSlots slot) + { + if (slot < INVENTORY_SLOT_BAG_START || slot > INVENTORY_SLOT_ITEM_END) + return PlayerInventoryFacadeResultEnum::IMPOSSIBLE; + + GlobalPlayerInspector playerInspector(this->playerGUID); + GlobalItemInspector itemInspector(this->playerGUID, itemLowGUID); + Player* const player = this->getCurrentPlayer(); + + if (player == nullptr) + return PlayerInventoryFacadeResultEnum::IMPOSSIBLE; + + player->DestroyItem(itemInspector.getItemInventorySlot(), itemInspector.getItemSlot(), true); + + return PlayerInventoryFacadeResultEnum::OK; + } +}; diff --git a/src/domain/player/facade/inventory/definition/enum/PlayerInventoryFacadeResultEnum.h b/src/domain/player/facade/inventory/definition/enum/PlayerInventoryFacadeResultEnum.h new file mode 100644 index 00000000000..384e00d8e6a --- /dev/null +++ b/src/domain/player/facade/inventory/definition/enum/PlayerInventoryFacadeResultEnum.h @@ -0,0 +1,6 @@ +enum class PlayerInventoryFacadeResultEnum +{ + OK = 0, + IMPOSSIBLE = 1 +}; + diff --git a/src/domain/player/inspector/global/GlobalPlayerInspector.h b/src/domain/player/inspector/global/GlobalPlayerInspector.h index 6387df10c6d..f99fc8bd8cf 100644 --- a/src/domain/player/inspector/global/GlobalPlayerInspector.h +++ b/src/domain/player/inspector/global/GlobalPlayerInspector.h @@ -15,6 +15,8 @@ #include "PlayerbotAI.h" #include "AbstractPlayerInspector.h" #include "PlayerbotMgr.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.h" class GlobalPlayerInspector : public AbstractPlayerInspector { @@ -107,4 +109,245 @@ class GlobalPlayerInspector : public AbstractPlayerInspector return !targets.empty(); } + + bool canEquipItem(const uint32_t itemLowGUID, uint16_t destinationSlot) const + { + Item* const item = this->getItemByGUID(itemLowGUID); + + if (item == nullptr) + return false; + + const Player* const player = this->getCurrentPlayer(); + + if (player == nullptr) + return false; + + return player->CanEquipItem(item->GetSlot(), destinationSlot, item, true); + } + + bool canUseItem(const uint32_t itemGUID) const + { + const Player* const player = this->getCurrentPlayer(); + + if (player == nullptr) + return false; + + const ObjectGuid itemFullGUID = ObjectGuid::Create(itemGUID); + Item* const item = player->GetItemByGuid(itemFullGUID); + + if (item == nullptr) + return false; + + return player->CanUseItem(item); + } + + uint8_t getCurrentPlayerClass() const + { + const Player* const player = this->getCurrentPlayer(); + + // If the player is offline, we bail out by returning a void class to avoid any offline mutation. + if (player == nullptr) + return CLASS_NONE; + + return player->getClass(); + } + + bool requiresItemForActiveQuest(const uint32_t itemTemplateId) const + { + const Player* const player = this->getCurrentPlayer(); + + // If the player is offline, we consider the item as being in use to avoid modifying an offline inventory. + if (player == nullptr) + return true; + + return player->HasQuestForItem(itemTemplateId); + } + + uint32_t getItemCount(const uint32_t itemTemplateId) const + { + const Player* const player = this->getCurrentPlayer(); + + if (player == nullptr) + return 0; + + return player->GetItemCount(itemTemplateId); + } + + Item* getItemByPosition(const uint16_t slot) const + { + Player* const player = this->getCurrentPlayer(); + + if (player == nullptr) + return nullptr; + + Item* const item = player->GetItemByPos(slot); + + return item; + } + + Item* getItemByPosition(const uint8_t bag, const uint8_t slot) const + { + Player* const player = this->getCurrentPlayer(); + + if (player == nullptr) + return nullptr; + + Item* const item = player->GetItemByPos(bag, slot); + + return item; + } + + uint32_t getPlayerMoney() const + { + Player* const player = this->getCurrentPlayer(); + + if (player == nullptr) + return 0; + + return player->GetMoney(); + } + + // Item* getBagItemByPosition(const uint8_t bagNumber, const uint8_t slot) const + // { + // if (bagNumber >= INVENTORY_SLOT_BAG_END) + // return nullptr; + + // if (bagNumber == 0) + // { + // const Player* const player = this->getCurrentPlayer(); + + // if (player == nullptr) + // return nullptr; + + // return player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + // } + + // Player* const player = this->getCurrentPlayer(); + + // if (player == nullptr) + // return nullptr; + + // Item* const bagItem = player->GetItemByPos(INVENTORY_SLOT_BAG_START + bagNumber - 1); + + // if (bagItem == nullptr) + // return nullptr; + + // Bag* const bag = bagItem->ToBag(); + + // if (bag == nullptr) + // return nullptr; + + // return bag->GetItemByPos(slot); + // } + + Item* getItemByGUID(const uint64_t guid) const + { + Player* const player = this->getCurrentPlayer(); + + if (player == nullptr) + return nullptr; + + const ObjectGuid fullItemGUID = ObjectGuid::Create(guid); + + Item* const item = player->GetItemByGuid(fullItemGUID); + + return item; + } + + Item* getMutableItemByGUID(const uint64_t guid) const + { + Player* const player = this->getCurrentPlayer(); + + if (player == nullptr) + return nullptr; + + const ObjectGuid fullItemGUID = ObjectGuid::Create(guid); + + Item* const item = player->GetItemByGuid(fullItemGUID); + + return item; + } + + uint8_t getDominantPlayerTalentTab() const + { + Player* const player = this->getCurrentPlayer(); + + if (player == nullptr) + return 255; + + return AiFactory::GetPlayerSpecTab(player); + } + + Stats getPrimaryStat() const + { + const uint8_t playerClass = this->getCurrentPlayerClass(); + + switch (playerClass) + { + case CLASS_WARRIOR: + return STAT_STRENGTH; + case CLASS_PALADIN: + { + const uint8_t dominantTab = this->getDominantPlayerTalentTab(); + + switch (dominantTab) + { + case PALADIN_TAB_HOLY: + return STAT_INTELLECT; + case PALADIN_TAB_PROTECTION: + return STAT_STAMINA; + case PALADIN_TAB_RETRIBUTION: + return STAT_STRENGTH; + } + + return STAT_STAMINA; + } + case CLASS_HUNTER: + return STAT_AGILITY; + case CLASS_ROGUE: + return STAT_AGILITY; + case CLASS_PRIEST: + return STAT_SPIRIT; + case CLASS_DEATH_KNIGHT: + return STAT_STRENGTH; + case CLASS_SHAMAN: + { + const uint8_t dominantTab = this->getDominantPlayerTalentTab(); + + switch (dominantTab) + { + case SHAMAN_TAB_ELEMENTAL: + return STAT_INTELLECT; + case SHAMAN_TAB_ENHANCEMENT: + return STAT_AGILITY; + case SHAMAN_TAB_RESTORATION: + return STAT_INTELLECT; + } + + return STAT_STAMINA; + } + case CLASS_MAGE: + return STAT_INTELLECT; + case CLASS_DRUID: + { + const uint8_t dominantTab = this->getDominantPlayerTalentTab(); + + switch (dominantTab) + { + case DRUID_TAB_BALANCE: + return STAT_INTELLECT; + case DRUID_TAB_FERAL: + return STAT_AGILITY; + case DRUID_TAB_RESTORATION: + return STAT_SPIRIT; + } + + return STAT_STAMINA; + } + default: + { + return STAT_STAMINA; + } + } + } }; diff --git a/src/repository/PlayerGuildRepository.cpp b/src/repository/PlayerGuildRepository.cpp index 90d9f0ff686..fc709b0e80a 100644 --- a/src/repository/PlayerGuildRepository.cpp +++ b/src/repository/PlayerGuildRepository.cpp @@ -2,14 +2,14 @@ #include #include "PlayerGuildRepository.h" #include "PlayerbotAIConfig.h" +#include "QueryResult.h" #include "UInt32VectorToString.h" #include "DatabaseEnv.h" -#include "QueryResult.h" #include "Log.h" std::unordered_set PlayerGuildRepository::GetPlayerGuildsIds() { - std::string randomBotsAccountsIdsString = UInt32VectorToString(sPlayerbotAIConfig->randomBotAccounts); + std::string randomBotsAccountsIdsString = UInt32VectorToString(PlayerbotAIConfig::instance().randomBotAccounts); std::unordered_set guildIds; @@ -44,7 +44,7 @@ std::unordered_set PlayerGuildRepository::GetPlayerGuildsIds() std::unordered_set PlayerGuildRepository::GetGuildMembersIds(uint32_t guildId) { - std::string randomBotsAccountsIdsString = UInt32VectorToString(sPlayerbotAIConfig->randomBotAccounts); + std::string randomBotsAccountsIdsString = UInt32VectorToString(PlayerbotAIConfig::instance().randomBotAccounts); std::unordered_set membersIds; diff --git a/src/repository/PlayerGuildRepository.h b/src/repository/PlayerGuildRepository.h index d8c3a25e6ea..068ab8e1c63 100644 --- a/src/repository/PlayerGuildRepository.h +++ b/src/repository/PlayerGuildRepository.h @@ -2,9 +2,7 @@ #define PLAYER_GUILD_REPOSITORY_H #include -#include #include -#include class PlayerGuildRepository { public: diff --git a/src/service/inventory/abstract/AbstractItemVisitor.h b/src/service/inventory/abstract/AbstractItemVisitor.h new file mode 100644 index 00000000000..124bcb9b0ed --- /dev/null +++ b/src/service/inventory/abstract/AbstractItemVisitor.h @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include +#include +#include +#include "Define.h" +#include "ItemTemplate.h" +#include "Player.h" + +#include "Log.h" + +class AbstractItemVisitor +{ +public: + virtual ~AbstractItemVisitor() = default; + + virtual bool process(Player* player, Item* item) + { + return false; + } + + virtual bool visit(Player* player, Item* item) + { + return false; + } + + bool equipArmor(Player* player, Item* item, EquipmentSlots slot) + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_ARMOR) + return false; + + // Necessary because CanEquipItem is poorly typed. + // We also don't want our parameter to be mutated so we use a disposable variable instead. + uint16_t destinationSlot = slot; + bool canEquipItem = player->CanEquipItem(item->GetSlot(), destinationSlot, item, true); + + if (!canEquipItem) + return false; + + const Item* equippedItem = player->GetItemByPos(slot); + + if (equippedItem == nullptr) + { + player->EquipItem(slot, item, true); + + return true; + } + + player->SwapItem(item->GetSlot(), slot); + + return true; + } + + bool equipWeapon(Player* player, Item* item, EquipmentSlots slot) + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_WEAPON) + return false; + + // Necessary because CanEquipItem is poorly typed. + // We also don't want our parameter to be mutated so we use a disposable variable instead. + uint16_t destinationSlot = slot; + bool canEquipItem = player->CanEquipItem(item->GetSlot(), destinationSlot, item, true); + + if (!canEquipItem) + return false; + + // In case we need to unequip the off-hand we need at least one slot + if (player->GetFreeInventorySpace() == 0) + return false; + + const Item* equippedItem = player->GetItemByPos(slot); + + if (equippedItem == nullptr) + { + player->EquipItem(slot, item, true); + player->AutoUnequipOffhandIfNeed(); + + return true; + } + + player->SwapItem(item->GetSlot(), slot); + + return true; + } + + void destroy(Player* player, Item* item) + { + if (player == nullptr) + return; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return; + } + + LOG_ERROR("playerbots", "Destroy item {}", std::to_string(itemTemplate->ItemId)); + + player->DestroyItem(item->GetBagSlot(), item->GetSlot(), true); + } + + void destroyAll(Player* player, Item* item) + { + if (player == nullptr) + return; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return; + } + + uint32_t count = item->GetCount(); + + LOG_ERROR("playerbots", "Destroy multiple items of {}, ({})", std::to_string(itemTemplate->ItemId), std::to_string(count)); + + player->DestroyItemCount(item, count, true); + } + + void magicSell(Player* player, Item* item) + { + if (player == nullptr || item == nullptr) + return; + + if (item->GetState() == ITEM_REMOVED) + return; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return; + } + + LOG_ERROR("playerbots", "Magic selling item {}", std::to_string(itemTemplate->ItemId)); + + if (itemTemplate->SellPrice) + { + uint32_t count = item->GetCount(); + + player->ModifyMoney(itemTemplate->SellPrice * count); + LOG_ERROR("playerbots", "Magic sold item {} for {}", std::to_string(itemTemplate->ItemId), std::to_string(itemTemplate->SellPrice * count)); + } + + this->destroy(player, item); + } + +protected: + bool isAllowed(const ItemTemplate* itemTemplate) + { + if (itemTemplate == nullptr) + return false; + + const std::unordered_set forbiddenItemsGUIDs = this->getForbiddenItemsGUIDs(); + + return forbiddenItemsGUIDs.find(itemTemplate->ItemId) == forbiddenItemsGUIDs.end(); + } + + bool isAllowed(Item* item) + { + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + return this->isAllowed(itemTemplate); + } + + virtual const std::unordered_set getForbiddenItemsGUIDs() + { + return {}; + } +}; diff --git a/src/service/inventory/armor/ArmorItemVisitor.h b/src/service/inventory/armor/ArmorItemVisitor.h new file mode 100644 index 00000000000..458e705f3d9 --- /dev/null +++ b/src/service/inventory/armor/ArmorItemVisitor.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include +#include + +#include "InventoryService.h" +#include "Log.h" +#include "StatsWeightCalculator.h" +#include "ItemTemplate.h" +#include "Player.h" + +#include "AbstractItemVisitor.h" + +class ArmorItemVisitor : public AbstractItemVisitor +{ +public: + bool process(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (!player->CanUseItem(item)) + return false; + + + StatsWeightCalculator statisticsWeightCalculator = StatsWeightCalculator(player); + std::vector slots = InventoryService::GetInstance().getItemEquipmentSlots(itemTemplate); + + const float newItemStatisticsWeight = statisticsWeightCalculator.CalculateItem(itemTemplate->ItemId); + + statisticsWeightCalculator.Reset(); + + bool hasEquippedArmor = false; + + for (uint8_t i = 0; i < slots.size(); ++i) + { + Item* currentlyEquippedItem = player->GetItemByPos(slots[i]); + + if (currentlyEquippedItem == nullptr) + continue; + + hasEquippedArmor = true; + + const float existingItemStatisticsWeight = statisticsWeightCalculator.CalculateItem(currentlyEquippedItem->GetTemplate()->ItemId); + + if (existingItemStatisticsWeight < newItemStatisticsWeight) + { + this->equipArmor(player, item, slots[i]); + + return true; + } + } + + return false; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_ARMOR) + { + LOG_ERROR("playerbots", "Item {} was not of class ARMOR ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + if (!this->isAllowed(item)) + { + this->destroyAll(player, item); + + return true; + } + + if (this->process(player, item)) + { + LOG_ERROR("playerbots", "Item {} was a useful ARMOR item", itemTemplate->ItemId); + + return true; + } + + this->magicSell(player, item); + + return true; + } +}; diff --git a/src/service/inventory/consumable/ConsumableItemVisitor.h b/src/service/inventory/consumable/ConsumableItemVisitor.h new file mode 100644 index 00000000000..f255baedd07 --- /dev/null +++ b/src/service/inventory/consumable/ConsumableItemVisitor.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include "ItemTemplate.h" +#include "AbstractItemVisitor.h" +#include "Player.h" + +#include "ConsumableConsumableItemVisitor.h" +#include "ConsumablePotionItemVisitor.h" +#include "ConsumableElixirItemVisitor.h" + +class ConsumableItemVisitor : public AbstractItemVisitor +{ +public: + bool process(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + switch (itemTemplate->SubClass) + { + case ITEM_SUBCLASS_CONSUMABLE: + { + ConsumableConsumableItemVisitor visitor = ConsumableConsumableItemVisitor(); + bool visited = visitor.visit(player, item); + + return visited; + } + + case ITEM_SUBCLASS_POTION: + { + ConsumablePotionItemVisitor visitor = ConsumablePotionItemVisitor(); + bool visited = visitor.visit(player, item); + + return visited; + } + + case ITEM_SUBCLASS_ELIXIR: + { + ConsumableElixirItemVisitor visitor = ConsumableElixirItemVisitor(); + bool visited = visitor.visit(player, item); + + return visited; + } + + default: + + return false; + } + + return false; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_CONSUMABLE) + { + LOG_ERROR("playerbots", "Item {} was not of class CONSUMABLE ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + if (this->process(player, item)) + { + LOG_ERROR("playerbots", "Item {} was a useful CONSUMABLE item", itemTemplate->ItemId); + + return true; + } + + this->magicSell(player, item); + + return true; + } + + void destroyAll(Player* player, Item* item) + { + if (player == nullptr) + return; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return; + } + + uint32_t count = item->GetCount(); + + LOG_ERROR("playerbots", "Destroy multiple items of {}, ({})", std::to_string(item->GetTemplate()->ItemId), std::to_string(count)); + + player->DestroyItemCount(item, count, true); + } + + void magicSell(Player* player, Item* item) + { + if (player == nullptr || item == nullptr) + return; + + if (item->GetState() == ITEM_REMOVED) + return; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return; + } + + LOG_ERROR("playerbots", "Magic selling item {}", std::to_string(itemTemplate->ItemId)); + + uint32_t count = itemTemplate->GetMaxStackSize(); + + if (itemTemplate->SellPrice) + { + player->ModifyMoney(itemTemplate->SellPrice * count); + LOG_ERROR("playerbots", "Magic sold item {} for {}", std::to_string(itemTemplate->ItemId), std::to_string(itemTemplate->SellPrice * count)); + } + + player->DestroyItemCount(item, count, true); + } +}; diff --git a/src/service/inventory/consumable/consumable/ConsumableConsumableItemVisitor.h b/src/service/inventory/consumable/consumable/ConsumableConsumableItemVisitor.h new file mode 100644 index 00000000000..90e1e9ee2a0 --- /dev/null +++ b/src/service/inventory/consumable/consumable/ConsumableConsumableItemVisitor.h @@ -0,0 +1,914 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include "ItemTemplate.h" +#include "AbstractItemVisitor.h" +#include "Player.h" + +class ConsumableConsumableItemVisitor final : public AbstractItemVisitor +{ +public: + + bool process(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + const uint8_t playerClass = player->getClass(); + const bool isRoguePoison = this->getRoguePoisonsItemsGUIDs().find(itemTemplate->ItemId) != this->getRoguePoisonsItemsGUIDs().end(); + + if (playerClass != CLASS_ROGUE && isRoguePoison) + return false; + + const bool isMageItem = this->getMageItemsGUIDs().find(itemTemplate->ItemId) != this->getMageItemsGUIDs().end(); + + if (playerClass != CLASS_MAGE && isMageItem) + return false; + + const bool isWarlockItem = this->getWarlockItemsGUIDs().find(itemTemplate->ItemId) != this->getWarlockItemsGUIDs().end(); + + if (playerClass != CLASS_WARLOCK && isWarlockItem) + return false; + + const bool isUnwantedItem = this->getUnwantedItemsGUIDs().find(itemTemplate->ItemId) != this->getUnwantedItemsGUIDs().end(); + + if (isUnwantedItem) + return false; + + const bool isQuestConsumableItem = this->getQuestConsumableItemsGUIDs().find(itemTemplate->ItemId) != this->getQuestConsumableItemsGUIDs().end(); + + if (isQuestConsumableItem && !player->HasQuestForItem(itemTemplate->ItemId)) + return false; + + return true; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_CONSUMABLE) + { + LOG_ERROR("playerbots", "Item {} was not of class CONSUMABLE ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + if (itemTemplate->SubClass != ITEM_SUBCLASS_CONSUMABLE) + { + LOG_ERROR("playerbots", "Item {} was not of subclass CONSUMABLE ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_SUBCLASS_CONSUMABLE), std::to_string(itemTemplate->SubClass)); + + return false; + } + + if (!this->isAllowed(item)) + { + this->destroyAll(player, item); + + return true; + } + + if (this->process(player, item)) + { + LOG_ERROR("playerbots", "Item {} was a useful CONSUMABLE item", itemTemplate->ItemId); + + return true; + } + + return false; + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() + { + return { + 1199, // Charged Soulstone + 1700, // Admin Warlord's Claymore + 1995, // Deprecated Cat's Paw + 3438, // Ankh of Resurrection + 4420, // NPC Equip 4420 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 4423, // NPC Equip 4423 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 4842, // test => Illegal item that should not even be present in the core as it is not present in any WotLK database + 5046, // Locked Gift => Illegal item that should not even be present in the core as it is not present in any WotLK database + 5047, // Skull Wrapping Paper => Illegal item that should not even be present in the core as it is not present in any WotLK database + 5224, // NPC Equip 5224 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 5225, // NPC Equip 5225 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 5378, // Shane Test (DELETE ME) => Illegal item that should not even be present in the core as it is not present in any WotLK database + 6244, // ggggfg => Illegal item that should not even be present in the core as it is not present in any WotLK database + 6638, // Air Sapta => Apparently this sapta is the only one not needed by shamans? + 6852, // Eternal Eye => Quest item seemingly linked to no quest + 6927, // Big Will's Ear => Quest item seemingly linked to no quest. It was supposedly dropped by Big Will but the quest requires killing him without looting him. + 7147, // NPC Equip 7147 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 7168, // NPC Equip 7168 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 7388, // Skull Key => Quest item seemingly linked to no quest + 9232, // NPC Equip 9232 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 9319, // Nimboya's Laden Pike => Quest item seemingly linked to no quest + 10419, // NPC Equip 10419 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 10448, // NPC Equip 10448 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 10449, // NPC Equip 10449 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 10451, // NPC Equip 10451 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 10452, // NPC Equip 10452 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 10453, // NPC Equip 10453 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 11182, // NPC Equip 11182 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 11183, // NPC Equip 11183 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 11616, // DEBUG Samophlange Manual Page + 12246, // NPC Equip 12246 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 12385, // test => Illegal item that should not even be present in the core as it is not present in any WotLK database + 12439, // NPC Equip 12439 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 12441, // NPC Equip 12441 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 13294, // NPC Equip 13294 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 17350, // NPC Equip 17350 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 17729, // NPC Equip 17729 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 18595, // Blood Opal => Item not present in WotLK. + 19063, // NPC Equip 19063 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 19977, // NPC Equip 19977 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 20365, // NPC Equip 20365 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 20386, // NPC Equip 20386 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 20473, // NPC Equip 20473 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 20905, // NPC Equip 20905 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21238, // Winter Veil Cookie UNUSED + 21556, // NPC Equip 21556 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21739, // Lunar Festival Invitation DEBUG + 21832, // NPC Equip 21832 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21834, // NPC Equip 21834 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21922, // NPC Equip 21922 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21961, // NPC Equip 21961 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21965, // NPC Equip 21965 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21966, // NPC Equip 21966 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21967, // NPC Equip 21967 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21968, // NPC Equip 21968 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21969, // NPC Equip 21969 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21970, // NPC Equip 21970 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21971, // NPC Equip 21971 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21972, // NPC Equip 21972 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21973, // NPC Equip 21973 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21974, // NPC Equip 21974 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21976, // NPC Equip 21976 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21977, // NPC Equip 21977 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 21978, // NPC Equip 21978 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 22118, // NPC Equip 22118 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 22124, // NPC Equip 22124 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 22125, // NPC Equip 22125 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 22126, // NPC Equip 22126 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 22127, // NPC Equip 22127 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 22129, // NPC Equip 22129 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 22323, // NPC Equip 22323 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 23163, // Performer's Wand => Unobtainable item + 23164, // Bubbly Beverage => Unobtainable item + 23175, // Tasty Summer Treat => Unobtainable item (iCoke promotion) + 23176, // Fizzy Energy Drink => Unobtainable item (iCoke promotion?) + 23209, // NPC Equip 23209 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 23210, // NPC Equip 23210 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 23496, // NPC Equip 23496 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 23640, // NPC Equip 23640 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 23641, // NPC Equip 23641 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 23715, // Permanent Lung Juice Cocktail => Unobtainable item + 23718, // Permanent Ground Scorpok Assay => Unobtainable item + 23719, // Permanent Cerebral Cortex Compound => Unobtainable item + 23721, // Permanent Gizzard Gum => Unobtainable item + 23722, // Permanent R.O.I.D.S. => Unobtainable item + 23794, // Permanent Sheen of Zanza => Unobtainable item + 23795, // Permanent Spirit of Zanza => Unobtainable item + 23796, // Permanent Swiftness of Zanza => Unobtainable item + 23872, // NPC Equip 23872 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 23982, // NPC Equip 23982 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 26046, // NPC Equip 26046 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 26047, // NPC Equip 26047 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 27504, // NPC Equip 27504 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 28036, // NPC Equip 28036 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 28110, // Fat Gnome and Little Elf => Unobtainable item, probably removed due to tasteless reference to a crime against humanity. + 28131, // Reaver Buster Launcher => Seemingly unobtainable item. + 29585, // NPC Equip 29585 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 29805, // Socrethar's Head => TBC Beta quest item, unobtainable. + 29868, // QAEnchant Gloves +26 Attack Power + 29877, // NPC Equip 29877 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 31954, // NPC Equip 31954 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 32424, // NPC Equip 32424 => Wrong item, should be "Blade's Edge Ogre Brew" + 32426, // NPC Equip 32426 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 32669, // NPC Equip 32669 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 32913, // NPC Equip 32913 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33063, // Really Tough Brewfest Bread + 33090, // NPC Equip 33090 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33100, // NPC Equip 33100 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33217, // NPC Equip 33217 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33218, // Goblin Gumbo + 33219, // Goblin Gumbo Kettle + 33570, // NPC Equip 33570 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33572, // NPC Equip 33572 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33573, // NPC Equip 33573 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33574, // NPC Equip 33574 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33600, // NPC Equip 33600 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33601, // NPC Equip 33601 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33602, // NPC Equip 33602 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33603, // NPC Equip 33603 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33623, // NPC Equip 33623 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33624, // NPC Equip 33624 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33625, // NPC Equip 33625 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33626, // NPC Equip 33626 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33785, // NPC Equip 33785 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33786, // NPC Equip 33786 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33787, // NPC Equip 33787 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33788, // NPC Equip 33788 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 33800, // NPC Equip 33800 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34046, // NPC Equip 34046 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34047, // NPC Equip 34047 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34048, // NPC Equip 34048 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34071, // NPC Equip 34071 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34151, // NPC Equip 34151 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34152, // NPC Equip 34152 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34153, // NPC Equip 34153 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34154, // NPC Equip 34154 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34155, // NPC Equip 34155 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34156, // NPC Equip 34156 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34250, // NPC Equip 34250 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34251, // NPC Equip 34251 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 34252, // NPC Equip 34252 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 35120, // NPC Equip 35120 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 35667, // NPC Equip 35667 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 38165, // NPC Equip 38165 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 38236, // NPC Equip 38236 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 38685, // NPC Equip 38685 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 38702, // NPC Equip 38702 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 38704, // NPC Equip 38704 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 39163, // Test expire transform + 39600, // NPC Equip 39600 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 40308, // NPC Equip 40308 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 42437, // NPC Equip 42437 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 42764, // NPC Equip 42764 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 44624, // NPC Equip 44624 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 44628, // NPC Equip 44628 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 44630, // NPC Equip 44630 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 44804, // NPC Equip 44804 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 44805, // NPC Equip 44805 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 44807, // Indalamar's Holy Hand Grenade + 44813, // NPC Equip 44813 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 44816, // NPC Equip 44816 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 44832, // Squirt Gun [PH] + 44866, // Faithful Mule + 44867, // NPC Equip 44867 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 44964, // NPC Equip 44964 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 45126, // Trusty Mount [PH] + 46717, // NPC Equip 46717 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 46765, // Blue War Fuel (Mountain Dew Promotion) + 46766, // Red War Fuel (Mountain Dew Promotion) + 48416, // NPC Equip 48416 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 48601, // Red Rider Air Rifle Ammo + 49223, // Permission Slip + 49224, // NPC Equip 49224 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 49225, // NPC Equip 49225 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 49349, // NPC Equip 49349 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 49372, // NPC Equip 49372 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 49373, // Permission Slip + 49374, // NPC Equip 49374 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 49692, // NPC Equip 49692 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 50076, // NPC Equip 50076 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 50091, // NPC Equip 50091 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 50092, // NPC Equip 50092 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 50093, // Pet Prep: A Beginner's Guide + 50164, // NPC Equip 50164 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 52202, // Elemental Sapta + 52361, // NPC Equip 52361 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 53476, // NPC Equip 53476 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 53477, // NPC Equip 53477 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 54647, // NPC Equip 54647 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 54822, // Sen'jin Overcloak + }; + } + + const std::unordered_set getUnwantedItemsGUIDs() + { + return { + 835, // Large Rope Net + 1127, // Flash Bundle + 1176, // Smelling Salts + 1187, // Spiked Collar + 1191, // Bag of Marbles + 1322, // Fishliver Oil + 1434, // Glowing Wax Stick + 1970, // Restoring Balm + 3434, // Slumber Sand + 3456, // Dog Whistle + 4546, // Call of the Raptor + 4598, // Goblin Fishing Pole + 4941, // Really Sticky Glue + 4945, // Faintly Glowing Skull + 4952, // Stormstout + 4953, // Trogg Brew + 5421, // Fiery Blaze Enchantment + 5457, // Severed Voodoo Claw + 5845, // Flank of Meat + 5951, // Moist Towelette + 7516, // Tabetha's Instructions => Bots don't need instructions. + 8410, // R.O.I.D.S. + 8411, // Lung Juice Cocktail + 8412, // Ground Scorpok Assay + 8423, // Cerebral Cortex Compound + 8424, // Gizzard Gum, + 8432, // Eau de Mixilpixil + 10684, // Colossal Parachute + 10830, // M73 Frag Grenade + 12924, // Ritual Candle + 13149, // Eldarathian Tome of Summoning Vol. 1 + 13151, // The Mystic Studies of Hor'ank + 13152, // The Story With No Conclusion + 13153, // Tome of Mal'cin Vorail + 13154, // Jael'marin's Studies of the Arcane + 13508, // Eye of Arachnida + 13509, // Clutch of Foresight + 13514, // Wail of the Banshee + 13813, // Blessed Sunfruit Juice + 14894, // Lily Root + 15723, // Tea with Sugar + 15778, // Mechanical Yeti + 17048, // Rumsey Rum + 18209, // Energized Sparkplug + 18269, // Gordok Green Grog + 18284, // Kreeg's Stout Beatdown + 18297, // Thornling Seed + 19060, // Warsong Gulch Enriched Ration + 19061, // Warsong Gulch Iron Ration + 19062, // Warsong Gulch Field Ration + 19318, // Bottled Alterac Spring Water + 19997, // Harvest Nectar + 20062, // Arathi Basin Enriched Ration + 20063, // Arathi Basin Field Ration + 20064, // Arathi Basin Iron Ration + 20079, // Spirit of Zanza + 20080, // Sheen of Zanza + 20081, // Swiftness of Zanza + 20222, // Defiler's Enriched Ration + 20223, // Defiler's Field Ration + 20224, // Defiler's Iron Ration + 20225, // Highlander's Enriched Ration + 20226, // Highlander's Field Ration + 20227, // Highlander's Iron Ration + 20388, // Lollipop + 20389, // Candy Corn + 20390, // Candy Bar + 20397, // Hallowed Wand - Pirate + 20398, // Hallowed Wand - Ninja + 20399, // Hallowed Wand - Leper Gnome + 20409, // Hallowed Wand - Ghost + 20410, // Hallowed Wand - Bat + 20411, // Hallowed Wand - Skeleton + 20413, // Hallowed Wand - Random + 20414, // Hallowed Wand - Wisp + 20557, // Hallow's End Pumpkin Treat + 21212, // Fresh Holly + 21241, // Winter Veil Eggnog + 21325, // Mechanical Greench + 21537, // Festival Dumplings + 21711, // Lunar Festival Invitation + 21744, // Lucky Rocket Cluster + 21745, // Elder's Moonstone + 21815, // Love Token + 22192, // Bloodkelp Elixir of Dodging + 22193, // Bloodkelp Elixir of Resistance + 22236, // Buttermilk Delight + 22237, // Dark Desire + 22238, // Very Berry Cream + 22239, // Sweet Surprise + 22259, // Unbestowed Friendship Bracelet + 22260, // Friendship Bracelet + 22743, // Bloodsail Sash + 22778, // Scourgebane Infusion + 22779, // Scourgebane Draught + 22795, // Fel Blossom + 23161, // Freshly-Squeezed Lemonade + 23194, // Lesser Mark of the Dawn + 23195, // Mark of the Dawn + 23196, // Greater Mark of the Dawn + 23211, // Toasted Smorc + 23215, // Bag of Smorc Ingredients + 23246, // Fiery Festival Brew + 23326, // Midsummer Sausage + 23327, // Fire-toasted Bun + 23379, // Cinder Bracers + 23435, // Elderberry Pie + 23492, // Suntouched Special Reserve + 23584, // Loch Modan Lager + 23585, // Stouthammer Lite + 23586, // Aerie Peak Pale Ale + 23985, // Crystal of Vitality + 23986, // Crystal of Insight + 23989, // Crystal of Ferocity + 24006, // Grunt's Waterskin + 24007, // Footman's Waterskin + 24408, // Edible Stalks + 24421, // Nagrand Cherry + 24429, // Expedition Flare + 24540, // Edible Fern + 25498, // Rough Stone Statue + 25880, // Coarse Stone Statue + 25881, // Heavy Stone Statue + 25882, // Solid Stone Statue + 25883, // Dense Stone Statue + 25884, // Primal Stone Statue + 27317, // Elemental Sapta + 27553, // Crimson Steer Energy Drink + 29482, // Ethereum Essence + 30309, // Stonebreaker Brew + 30499, // Brightstone Wine + 30615, // Halaani Whiskey + 30858, // Peon Sleep Potion + 31337, // Orb of the Blackwhelp + 31449, // Distilled Stalker Sight + 31450, // Sealth of the Stalker + 31451, // Pure Energy + 31535, // Bloodboil Poison + 32563, // Grilled Picnic Treat + 32971, // Water Bucket + 33023, // Savory Sausage + 33024, // Pickled Sausage + 33025, // Spicy Smoked Sausage + 33026, // The Golden Link + 33028, // Barleybrew Light + 33028, // Barleybrew Dark + 33030, // Barleybrew Clear + 33031, // Thunder 45 + 33032, // Thunderbrew Ale + 33033, // Thunderbrew Stout + 33034, // Gordok Grog + 33035, // Ogre Mead + 33036, // Mudder's Milk + 33043, // The Essential Brewfest Pretzel + 33226, // Tricky Treat + 33234, // Iced Berry Slush + 33236, // Fizzy Faire Drink "Classic" + 33246, // Funnel Cake + 33254, // Afrazi Forest Strider Drumstick + 33312, // Mana Sapphire + 33929, // Brewfest Brew + 33956, // Harkor's Home Brew + 34017, // Small Step Brew + 34018, // Long Stride Brew + 34019, // Path of Brew + 34020, // Jungle River Water + 34021, // Brewdo Magic + 34022, // Stout Shrunken Head + 34044, // B-Ball + 34063, // Dried Sausage + 34064, // Succulent Sausage + 34065, // Spiced Onion Cheese + 34068, // Weighted Jack-o'-Lantern + 34077, // Crudely Wrapped Gift + 34410, // Honeyed Holiday Ham + 34412, // Sparkling Apple Cider + 34599, // Juggling Torch + 35223, // Papa Hummel's Old-Fashioned Pet Biscuit + 35946, // Fizzcrank Practice Parachute + 36877, // Folded Letter + 37431, // Fetch Ball + 37488, // Wild Winter Pilsner + 37489, // Izzard's Ever Flavor + 37490, // Aromatic Honey Brew + 37491, // Metok's Bubble Bock + 37492, // Springtime Stout + 37493, // Blackrock Lager + 37494, // Stranglethorn Brew + 37495, // Draenic Pale Ale + 37496, // Binary Brew + 37497, // Autumnal Acorn Ale + 37498, // Bartlett's Bitter Brew + 37499, // Lord of Frost's Private Label + 37582, // Pyroblast Cinnamon Ball + 37583, // G.N.E.R.D.S. + 37584, // Soothing Spearmint Candy + 37585, // Chewy Fel Taffy + 37604, // Tooth Pick + 37750, // Fresh Brewfest Hops + 37863, // Direbrew's Remote + 37878, // Worg's Blood Elixir + 37898, // Wild Winter Pilsner + 37899, // Izzard's Ever Flavor + 37900, // Aromatic Honey Brew + 37901, // Metok's Bubble Bock + 37902, // Springtime Stout + 37903, // Blackrock Lager + 37904, // Stranglethorn Brew + 37905, // Draenic Pale Ale + 37906, // Binary Brew + 37907, // Autumnal Acorn Ale + 37908, // Bartlett's Bitter Brew + 37909, // Lord of Frost's Private Label + 37925, // Experimental Mixture + 38291, // Ethereal Mutagen + 38294, // Ethereal Liqueur + 38300, // Diluted Ethereum Essence + 38320, // Dire Brew + 38518, // Cro's Apple + 38577, // Party G.R.E.N.A.D.E. + 38587, // Empty Brewfest Stein + 38626, // Empty Brew Bottle + 39476, // Fresh Goblin Brewfest Hops + 39477, // Fresh Dwarven Brewfest Hops + 39738, // Thunderbrew's Hard Ale + 42342, // Bag of Popcorn + 42350, // Bag of Peanuts + 42381, // Anguish Ale + 42436, // Chocolate Celebration Cake + 42438, // Lovely Cake + 42439, // Big Berry Pie + 43088, // Dalaran Apple Bowl + 43135, // Fate Rune of Fleet Feet + 43352, // Pet Grooming Kit + 43462, // Airy Pale Ale + 43470, // Worg Tooth Oatmeal Stout + 43471, // Rork Red Ribbon + 43472, // Snowfall Lager + 43473, // Drakefire Chile Ale + 43489, // Mohawk Grenade + 43626, // Happy Pet Snack + 44064, // Nepeta Leaf + 44065, // Oracle Secret Solution + 44435, // Windle's Lighter + 44481, // Grindgear Toy Gorilla + 44482, // Trusty Copper Racer + 44599, // Zippy Copper Racer + 44601, // Heavy Copper Racer + 44610, // Fresh Dalaran Bread + 44612, // Dalaran Swiss Wheel + 44613, // Aged Dalaran Sharp Wheel + 44621, // Bottle of Dalaran White + 44622, // Cask of Dalaran White + 44623, // Bottle of Dalaran Red + 44625, // Bottle of Aged Dalaran Red + 44626, // Cask of Aged Dalaran Red + 44627, // Bottle of Peaked Dalaran Red + 44629, // Cask of Peaked Dalaran Red + 44632, // Cask of Dalaran Red + 44698, // Intravenous Healing Potion + 44716, // Mysterious Fermented Liquid + 44792, // Blossoming Branch + 44812, // Turkey Shooter + 44817, // The Mischief Maker + 44818, // Noblegarden Egg + 44844, // Turkey Caller + 44849, // Tiny Green Ragdoll + 44943, // Icy Prism + 45047, // Sandbox Tiger + 46319, // Tournament Brew + 46399, // Thunder's Plunder + 46400, // Barleybrew Gold + 46401, // Crimson Stripe + 46402, // Promise of the Pandaren + 46403, // Chuganpug's Delight + 46711, // Spirit Candle + 46718, // Orange Marigold + 46725, // Red Rider Air Rifle + 46783, // Pink Gumball + 49288, // Little Ivory Raptor Whistle + 49289, // Little White Stallion Bridle + 49856, // "VICTORY" Perfume + 49857, // "Enchantress" Perfume + 49858, // "Forever" Perfume + 49859, // "Bravado" Cologne + 49860, // "Wizardry" Cologne + 49861, // "STALWART" Cologne + 49936, // Lovely Stormwind Card + 49937, // Lovely Undercity Card + 49938, // Lovely Darnassus Card + 49939, // Lovely Orgrimmar Card + 49940, // Lovely Ironforge Card + 49941, // Lovely Thunder Bluff Card + 49942, // Lovely Exodar Card + 49943, // Lovely Silvermoon City Card + 50163, // Lovely Rose + 54455, // Paint Bomb + }; + } + + const std::unordered_set getQuestConsumableItemsGUIDs() + { + return { + 1262, // Keg of Thunderbrew Lager + 5880, // Crate With Holes + 6074, // War Horn Mouthpiece + 6464, // Wailing Essence + 6486, // Singed Scale + 6635, // Earth Sapta + 6636, // Fire Sapta + 6637, // Water Sapta + 6781, // Bartleby's Mug + 6782, // Marshal Haggard's Badge + 6812, // Case of Elunite + 6841, // Vial of Phlogiston + 6842, // Furen's Instructions + 6851, // Essence of the Exile + 6926, // Furen's Notes + 6929, // Bath'rah's Parchment + 7127, // Powdered Azurite + 7227, // Balnir Snapdragons + 7266, // Ur's Treatise on Shadow Magic + 7627, // Dolanaar Delivery + 7629, // Ukor's Burden + 7970, // E.C.A.C. + 8048, // Emerald Dreamcatcher + 8095, // Hinott's Oil + 8548, // Divino-matic Rod + 9546, // Simple Scroll + 9569, // Hallowed Scroll + 10621, // Runed Scroll + 10663, // Essence of Hakkar + 10687, // Empty Vial Labeled #1 + 10688, // Empty Vial Labeled #2 + 10689, // Empty Vial Labeled #3 + 10690, // Empty Vial Labeled #4 + 11148, // Samophlange Manual Page + 11405, // Giant Silver Vein + 11413, // Nagmara's Filled Vial + 11914, // Empty Cursed Ooze Jar + 11948, // Empty Tainted Ooze Jar + 11953, // Empty Pure Sample Jar + 12533, // Roughshod Pike + 12650, // Attuned Dampener + 12712, // Warosh's Mojo + 12884, // Arnak's Hoof + 12885, // Pamela's Doll + 12886, // Pamela's Doll's Head + 12894, // Joseph's Wedding Ring + 12922, // Empty Canteen + 13155, // Resonating Skull + 13156, // Mystic Crystal + 13562, // Remains of Trey Lightforge + 13703, // Kodo Bone + 13761, // Frozen Eggs + 14542, // Kravel's Crate + 15314, // Bundle of Relics + 16282, // Bundle of Hides + 20470, // Solanian's Scrying Orb + 20471, // Scroll of Scourge Magic + 20472, // Solanian's Journal + 23361, // Cleansing Vial + 23417, // Sanctified Crystal + 23645, // Seer's Relic + 24330, // Drain Schematics + 24355, // Ironvine Seeds + 24407, // Uncatalogued Species + 24474, // Violet Scrying Crystal + 25465, // Stormcrow Amulet + 28038, // Seaforium PU-36 Explosive Nether Modulator + 28132, // Area 52 Special + 28513, // Demonic Rune Stone + 28554, // Shredder Spare Parts + 28571, // Blank Scroll + 28580, // B'naar Console Transcription + 28607, // Sunfury Disguise + 28635, // Sunfury Arcanist Robes + 28636, // Sunfury Researcher Gloves + 28637, // Sunfury Guardsman Medallion + 28784, // Unyielding Banner Scrap => Maybe unused quest item? + 29162, // Galaxis Soul Shard + 29324, // Warp-Attuned Orb + 29443, // Bloodmaul Brutebane Brew + 29624, // First Half of Socrethar's Stone + 29625, // Second Half of Socrethar's Stone + 29699, // Socrethar's Teleportation Stone + 29778, // Phase Disruptor + 29796, // Socrethar's Teleportation Stone + 29905, // Kael's Vial Remnant + 29906, // Vashj's Vial Remnant + 30260, // Voren'thal's Package + 30540, // Tally's Waiver (Unsigned) + 30811, // Scroll of Demonic Unbanishing + 31121, // Costume Scraps + 31122, // Overseer Disguise + 31495, // Grishnath Orb + 31517, // Dire Pinfeather + 31518, // Exorcism Feather + 31702, // Challenge from the Horde + 31795, // Draenei Prayer Beads + 32406, // Skyguard Blasting Charges + 32602, // Crystalforged Darkrune + 32848, // Explosives Package + 33079, // Murloc Costume + 33099, // Intact Plague Container + 33277, // Tome of Thomas Thomson + 33349, // Plague Vials + 33614, // Empty Apothecary's Flask => Possibly unaccessible to players but still linked to a quest. + 33615, // Flask of Vrykul Blood => Possibly unaccessible to players but still linked to a quest. + 33616, // Unstable Mix => Possibly unaccessible to players but still linked to a quest. + 33617, // Balanced Concoction => Possibly unaccessible to players but still linked to a quest. + 33619, // Lysander's Strain + 33621, // Plague Spray + 33797, // Portable Brewfest Keg + 34023, // Empty Apothecary's Flask + 34024, // Flask of Vrykul Blood + 34076, // Fish Bladder + 34475, // Arcane Charges + 35704, // Incendiary Explosives + 37173, // Geomancer's Orb + 37198, // Prototype Neural Needler + 37265, // Tua'kea's Breathing Bladder + 37661, // Gossamer Potion + 37877, // Silver Feather + 38629, // Orders from the Lich King + 38657, // Freya's Ward + 39698, // Light-infused Artifact + 40390, // Vic's Emergency Air Tank + 40482, // Dual-plagued Brain + 40725, // Steam-Powered Auctioneer + 44576, // Bright Flare + 44806, // Brightly Colored Shell Fragment + 45784, // Thorim's Sigil + 45786, // Hodir's Sigil + 45787, // Mimiron's Sigil + 45788, // Freya's Sigil + 45791, // Sigils of the Watchers + 45814, // Freya's Sigil + 45815, // Hodir's Sigil + 45816, // Mimiron's Sigil + 45817, // Thorim's Sigil + 45855, // Sigils of the Watchers + }; + } + + const std::unordered_set getRoguePoisonsItemsGUIDs() + { + return { + 2892, // Deadly Poison + 2893, // Deadly Poison II + 3775, // Crippling Poison + 3776, // Crippling Poison II + 5237, // Mind-numbing Poison + 6947, // Instant Poison + 6949, // Instant Poison II + 6950, // Instant Poison III + 6951, // Mind-numbing Poison II => Useless in WotLK but useful in earlier expansions, kept for IP use. + 8926, // Instant Poison IV + 8927, // Instant Poison V + 8928, // Instant Poison VI + 8984, // Deadly Poison III + 8985, // Deadly Poison IV + 9186, // Mind-numbing Poison III => Useless in WotLK but useful in earlier expansions, kept for IP use. + 10918, // Wound Poison + 10920, // Wound Poison II + 10921, // Wound Poison III + 10922, // Wound Poison IV + 20844, // Deadly Poison V + 21835, // Anesthetic Poison + 21927, // Instant Poison VII + 22053, // Deadly Poison VI + 22054, // Deadly Poison VII + 22055, // Wound Poison V + }; + } + + const std::unordered_set getUsefulConsumableItemsGUIDs() + { + return { + 18606, // Alliance Battle Standard + 18607, // Horde Battle Standard + 19045, // Stormpike Battle Standard + 19046, // Frostwolf Battle Standard + 19150, // Sentinel Basic Care Package + 19151, // Sentinel Standard Care Package + 19152, // Sentinel Advanced Care Package + 19153, // Outrider Advanced Care Package + 19154, // Outrider Basic Care Package + 19155, // Outrider Standard Care Package + 19182, // Darkmoon Faire Prize Ticket + 19425, // Mysterious Lockbox + 20228, // Defiler's Advanced Care Package + 20229, // Defiler's Basic Care Package + 20230, // Defiler's Standard Care Package + 20231, // Arathor Advanced Care Package + 20233, // Arathor Basic Care Package + 20236, // Arathor Standard Care Package + 21740, // Small Rocket Recipes + 21741, // Cluster Rocket Recipes + 21742, // Large Rocket Recipes + 21743, // Large Cluster Rocket Recipes + 21746, // Lucky Red Envelope + 24140, // Blackened Urn => Useless in WotLK but useful in earlier expansions, kept for IP use. + 24289, // Chrono-beacon + 24494, // Tears of the Goddess + 24520, // Honor Hold Favor + 24522, // Thrallmar Favor + 24579, // Mark of Honor Hold + 24581, // Mark of Thrallmar + 27388, // Mr. Pinchy + 29735, // Holy Dust + 30690, // Power Converter + 32408, // Naj'entus Spine + 32542, // Imp in a Ball + 33865, // Amani Hex Stick + 33926, // Sealed Scroll Case + 33927, // Brewfest Pony Keg + 33928, // Hollowed Bone Decanter + 34583, // Aldor Supplies Package + 34584, // Scryer Supplies Package + 34585, // Scryer Supplies Package + 34587, // Aldor Supplies Package + 34592, // Aldor Supplies Package + 34593, // Scryer Supplies Package + 34594, // Scryer Supplies Package + 34595, // Aldor Supplies Package + 35232, // Shattered Sun Supplies + 34686, // Brazier of Dancing Flames + 34850, // Midsummer Ground Flower + 35512, // Pocket Full of Snow + 35945, // Brilliant Glass + 36748, // Dark Brewmaiden's Brew + 36862, // Worn Troll Dice + 36863, // Decahedral Dwarven Dice + 37815, // Emerald Essence + 37859, // Amber Essence + 37860, // Ruby Essence + 38186, // Ethereal Credit + 38233, // Path of Illidan + 39878, // Mysterious Egg + 39883, // Cracked Egg + 40110, // Haunted Memento + 41426, // Magically Wrapped Gift + 44113, // Small Spice Bag + 44606, // Toy Train Set + 44717, // Disgusting Jar + 44718, // Ripe Disgusting Jar + 44751, // Hyldnir Spoils + 45011, // Stormwind Banner + 45013, // Thunder Bluff Banner + 45014, // Orgrimmar Banner + 45015, // Sen'jin Banner + 45016, // Undercity Banner + 45017, // Silvermoon City Banner + 45018, // Ironforge Banner + 45019, // Gnomeregan Banner + 45020, // Exodar Banner + 45021, // Darnassus Banner + 45038, // Fragment of Val'anyr + 45039, // Shattered Fragments of Val'anyr + 45057, // Wind-Up Train Wrecker + 45063, // Foam Sword Rack + 45506, // Archivum Data Disc + 45705, // Argent Tournament Invitation + 45798, // Heroic Celestial Planetarium Key + 45857, // Archivum Data Disc + 45896, // Unbound Fragments of Val'anyr + 45897, // Reforged Hammer of Ancient Kings + 46029, // Magnetic Core + 46779, // Path of Cenarius + 46780, // Ogre Pinata + 46843, // Argent Crusader's Banner + 46847, // Seaforium Bombs + 47030, // Huge Seaforium Bombs + 47541, // Argent Pony Bridle + 49631, // Standard Apothecary Serving Kit + 50307, // Infernal Spear + 50471, // The Heartbreaker + 54212, // Instant Statue Pedestal + 54437, // Tiny Green Ragdoll + 54438, // Tiny Blue Ragdoll + 54651, // Gnomeregan Pride + 54653, // Darkspear Pride + }; + } + + const std::unordered_set getWarlockItemsGUIDs() + { + return { + 5232, // Minor Soulstone + 16892, // Lesser Soulstone + 16893, // Soulstone + 16895, // Greater Soulstone + 16896, // Major Soulstone + 22116, // Master Soulstone + 36895, // Demonic Soulstone + }; + } + + const std::unordered_set getMageItemsGUIDs() + { + return { + 5513, // Mana Jade + 8007, // Mana Citrine + 8008, // Mana Ruby + 22044, // Mana Emerald + 36799, // Mana Gem + }; + } +}; diff --git a/src/service/inventory/consumable/elixir/ConsumableElixirItemVisitor.h b/src/service/inventory/consumable/elixir/ConsumableElixirItemVisitor.h new file mode 100644 index 00000000000..48eb7f1fb12 --- /dev/null +++ b/src/service/inventory/consumable/elixir/ConsumableElixirItemVisitor.h @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include "ItemTemplate.h" +#include "AbstractItemVisitor.h" +#include "Player.h" +#include "SpellAuraDefines.h" +#include "SpellMgr.h" + +#include "AiFactory.h" +#include "PlayerbotAI.h" + +class ConsumableElixirItemVisitor final : public AbstractItemVisitor +{ +public: + bool process(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (!this->isAppropriateForPlayer(player, itemTemplate)) + return false; + + const uint32_t stackSize = itemTemplate->GetMaxStackSize(); + const uint32_t totalQuantity = player->GetItemCount(itemTemplate->ItemId); + + if (totalQuantity > stackSize) + return false; + + return true; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_CONSUMABLE) + { + LOG_ERROR("playerbots", "Item {} was not of class CONSUMABLE ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + if (itemTemplate->SubClass != ITEM_SUBCLASS_ELIXIR) + { + LOG_ERROR("playerbots", "Item {} was not of subclass ELIXIR ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_SUBCLASS_CONSUMABLE), std::to_string(itemTemplate->SubClass)); + + return false; + } + + if (!this->isAllowed(item)) + { + this->destroyAll(player, item); + + return true; + } + + if (this->process(player, item)) + { + LOG_ERROR("playerbots", "Item {} was a useful CONSUMABLE item", itemTemplate->ItemId); + + return true; + } + + return false; + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() + { + return { + 2461, // Deprecated Elemental Resistance Potion + 2462, // Deprecated Potion of Lesser Invulnerability (Fix) + 5632, // Deprecated Cowardly Flight Potion + 23578, // Diet McWeaksauce + 23579, // The McWeaksauce Classic + 23696, // [PH] Potion of Heightened Senses [DEP] + 23698, // [PH] Nature Resist Potion [DEP] + 30793, // NPC Equip 30793 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 32762, // Rulkster's Brain Juice + 32763, // Rulkster's Secret Sauce + 34646, // NPC Equip 34646 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 37926, // NPC Equip 37926 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 39971, // NPC Equip 39971 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 40677, // NPC Equip 40677 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 42548, // NPC Equip 42548 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 45276, // Jillian's Genius Juice + 45277, // Jillian's Savior Sauce + }; + } + +private: + + bool isAppropriateForPlayer(Player* player, const ItemTemplate* itemTemplate) + { + if (player == nullptr) + return false; + + const SpellInfo* const spellInfo = SpellMgr::instance()->GetSpellInfo(itemTemplate->Spells[0].SpellId); + + if (spellInfo == nullptr) + return false; + + if (!spellInfo->HasEffect(SPELL_EFFECT_APPLY_AURA)) + return false; + + if (!spellInfo->HasAura(SPELL_AURA_MOD_STAT)) + return false; + + const std::array effects = spellInfo->GetEffects(); + + bool isUseful = false; + + for (const SpellEffectInfo effect : effects) + { + if (effect.MiscValue < STAT_STRENGTH || effect.MiscValue > STAT_SPIRIT) + continue; + + const Stats primaryStat = this->getPrimaryStat(player); + + if (primaryStat == effect.MiscValue) + return true; + } + + return false; + } + + const Stats getPrimaryStat(Player* player) + { + if (player == nullptr) + return STAT_STAMINA; + + const uint8_t playerClass = player->getClass(); + + switch (playerClass) + { + case CLASS_WARRIOR: + return STAT_STRENGTH; + case CLASS_PALADIN: + { + const uint8_t tab = AiFactory::GetPlayerSpecTab(player); + + switch (tab) + { + case PALADIN_TAB_HOLY: + return STAT_INTELLECT; + case PALADIN_TAB_PROTECTION: + return STAT_STAMINA; + case PALADIN_TAB_RETRIBUTION: + return STAT_STRENGTH; + } + + return STAT_STAMINA; + } + case CLASS_HUNTER: + return STAT_AGILITY; + case CLASS_ROGUE: + return STAT_AGILITY; + case CLASS_PRIEST: + return STAT_SPIRIT; + case CLASS_DEATH_KNIGHT: + return STAT_STRENGTH; + case CLASS_SHAMAN: + { + const uint8_t tab = AiFactory::GetPlayerSpecTab(player); + + switch (tab) + { + case SHAMAN_TAB_ELEMENTAL: + return STAT_INTELLECT; + case SHAMAN_TAB_ENHANCEMENT: + return STAT_AGILITY; + case SHAMAN_TAB_RESTORATION: + return STAT_INTELLECT; + } + + return STAT_STAMINA; + } + case CLASS_MAGE: + return STAT_INTELLECT; + case CLASS_DRUID: + { + const uint8_t tab = AiFactory::GetPlayerSpecTab(player); + + switch (tab) + { + case DRUID_TAB_BALANCE: + return STAT_INTELLECT; + case DRUID_TAB_FERAL: + return STAT_AGILITY; + case DRUID_TAB_RESTORATION: + return STAT_SPIRIT; + } + + return STAT_STAMINA; + } + } + } +}; diff --git a/src/service/inventory/consumable/potion/ConsumablePotionItemVisitor.h b/src/service/inventory/consumable/potion/ConsumablePotionItemVisitor.h new file mode 100644 index 00000000000..0be310a2b94 --- /dev/null +++ b/src/service/inventory/consumable/potion/ConsumablePotionItemVisitor.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include "ItemTemplate.h" +#include "AbstractItemVisitor.h" +#include "Player.h" +#include "SpellMgr.h" + +class ConsumablePotionItemVisitor final : public AbstractItemVisitor +{ +public: + bool process(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + const uint32_t maxStackSize = itemTemplate->GetMaxStackSize(); + const uint32_t totalQuantity = player->GetItemCount(itemTemplate->ItemId); + + if (totalQuantity > maxStackSize) + return false; + + return true; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_CONSUMABLE) + { + LOG_ERROR("playerbots", "Item {} was not of class CONSUMABLE ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + if (itemTemplate->SubClass != ITEM_SUBCLASS_CONSUMABLE) + { + LOG_ERROR("playerbots", "Item {} was not of subclass CONSUMABLE ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_SUBCLASS_CONSUMABLE), std::to_string(itemTemplate->SubClass)); + + return false; + } + + if (!this->isAllowed(item)) + { + this->destroyAll(player, item); + + return true; + } + + if (this->process(player, item)) + { + LOG_ERROR("playerbots", "Item {} was a useful CONSUMABLE item", itemTemplate->ItemId); + + return true; + } + + return false; + } + +protected: + + const bool isHealthPotion(Item* item) + { + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + return this->isHealthPotion(itemTemplate); + } + + const bool isHealthPotion(const ItemTemplate* itemTemplate) + { + const SpellInfo* const spellInfo = SpellMgr::instance()->GetSpellInfo(itemTemplate->Spells[0].SpellId); + + if (spellInfo == nullptr) + return false; + + if (spellInfo->HasEffect(SPELL_EFFECT_HEAL)) + return true; + + return false; + } + + const bool isManaPotion(const Item* item) + { + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + return this->isManaPotion(itemTemplate); + } + + const bool isManaPotion(const ItemTemplate* ItemTemplate) + { + const SpellInfo* const spellInfo = SpellMgr::instance()->GetSpellInfo(ItemTemplate->Spells[0].SpellId); + + if (spellInfo == nullptr) + return false; + + if (spellInfo->HasEffect(SPELL_EFFECT_ENERGIZE) && spellInfo->PowerType == POWER_MANA) + return true; + + return false; + } + + const std::unordered_set getForbiddenItemsGUIDs() + { + return { + 2461, // Deprecated Elemental Resistance Potion + 2462, // Deprecated Potion of Lesser Invulnerability (Fix) + 5632, // Deprecated Cowardly Flight Potion + 23578, // Diet McWeaksauce + 23579, // The McWeaksauce Classic + 23696, // [PH] Potion of Heightened Senses [DEP] + 23698, // [PH] Nature Resist Potion [DEP] + 30793, // NPC Equip 30793 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 32762, // Rulkster's Brain Juice + 32763, // Rulkster's Secret Sauce + 34646, // NPC Equip 34646 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 37926, // NPC Equip 37926 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 39971, // NPC Equip 39971 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 40677, // NPC Equip 40677 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 42548, // NPC Equip 42548 => Illegal item that should not even be present in the core as it is not present in any WotLK database + 45276, // Jillian's Genius Juice + 45277, // Jillian's Savior Sauce + }; + } + +}; diff --git a/src/service/inventory/container/ContainerItemVisitor.h b/src/service/inventory/container/ContainerItemVisitor.h new file mode 100644 index 00000000000..5f18485c631 --- /dev/null +++ b/src/service/inventory/container/ContainerItemVisitor.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" +#include "Player.h" + +#include "AbstractItemVisitor.h" + +class ContainerItemVisitor : public AbstractItemVisitor +{ +public: + bool process(Player* player, Item* item) override + { + + if (player == nullptr) + return false; + + const Bag* bag = item->ToBag(); + + // @TODO: Handle all container type + if (bag == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->InventoryType == INVTYPE_QUIVER && player->getClass() != CLASS_HUNTER) + return false; + + uint8_t initialSlot = INVENTORY_SLOT_BAG_START; + + if (itemTemplate->InventoryType == INVTYPE_QUIVER) + initialSlot = INVENTORY_SLOT_BAG_END - 1; + + for (uint8_t i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) + { + Bag* equipedBag = player->GetBagByPos(i); + + if (!equipedBag || bag->GetBagSize() > equipedBag->GetBagSize()) + { + player->EquipItem(i, item, true); + + return true; + } + } + + return false; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_CONTAINER) + { + LOG_ERROR("playerbots", "Item {} was not of class CONTAINER ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + if (itemTemplate->InventoryType != INVTYPE_BAG) + return false; + + if (this->process(player, item)) + { + LOG_ERROR("playerbots", "Item {} was a useful CONTAINER item", itemTemplate->ItemId); + + return true; + } + + this->magicSell(player, item); + + return true; + } +}; diff --git a/src/service/inventory/gem/GemItemVisitor.h b/src/service/inventory/gem/GemItemVisitor.h new file mode 100644 index 00000000000..ff4d8e9729d --- /dev/null +++ b/src/service/inventory/gem/GemItemVisitor.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include "ItemTemplate.h" +#include "AbstractItemVisitor.h" +#include "Player.h" + +class GemItemVisitor : public AbstractItemVisitor +{ +public: + bool process(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + if (item == nullptr) + return false; + + // @TODO: Implement logic on gem processing. + return false; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_GEM) + { + LOG_ERROR("playerbots", "Item {} was not of class GEM ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + if (this->process(player, item)) + { + LOG_ERROR("playerbots", "Item {} was a useful GEM item", itemTemplate->ItemId); + + return true; + } + + this->magicSell(player, item); + + return true; + } +}; diff --git a/src/service/inventory/generic/GenericItemVisitor.h b/src/service/inventory/generic/GenericItemVisitor.h new file mode 100644 index 00000000000..a2cc9087bb3 --- /dev/null +++ b/src/service/inventory/generic/GenericItemVisitor.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include +#include "ItemTemplate.h" +#include "AbstractItemVisitor.h" +#include "Player.h" + +/** + * @brief Handles the inventory management of generic items (class 8). + * + * @remark There is only one 1 generic item in the game (6987 - Fish Scale) and it never made it live. + * This category of item is thus always useless. + * + */ +class GenericItemVisitor : public AbstractItemVisitor +{ +public: + bool process(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + if (player == nullptr) + return false; + + return false; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_GENERIC) + { + LOG_ERROR("playerbots", "Item {} was not of class GENERIC ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + LOG_ERROR("playerbots", "Player (bot) {} (#{}) had item of impossible category GENERIC (8) #{}", + player->GetName(), std::to_string(player->GetGUID().GetEntry()), std::to_string(item->GetTemplate()->ItemId)); + + this->destroy(player, item); + + return true; + } +}; diff --git a/src/service/inventory/glyph/GlyphItemVisitor.h b/src/service/inventory/glyph/GlyphItemVisitor.h new file mode 100644 index 00000000000..dbfcd412fda --- /dev/null +++ b/src/service/inventory/glyph/GlyphItemVisitor.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include "ItemTemplate.h" +#include "AbstractItemVisitor.h" +#include "Player.h" + +class GlyphItemVisitor : public AbstractItemVisitor +{ +public: + bool process(Player* , Item* ) override + { + // @TODO: Implement logic on glyph item processing. + return false; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_GLYPH) + { + LOG_ERROR("playerbots", "Item {} was not of class GLYPH ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + if (this->process(player, item)) + { + LOG_ERROR("playerbots", "Item {} was a useful GLYPH item", itemTemplate->ItemId); + + return true; + } + + this->magicSell(player, item); + + return true; + } +}; diff --git a/src/service/inventory/inventory/InventoryService.h b/src/service/inventory/inventory/InventoryService.h new file mode 100644 index 00000000000..9555b88738b --- /dev/null +++ b/src/service/inventory/inventory/InventoryService.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include +#include "ItemTemplate.h" +#include "Player.h" + +class InventoryService +{ +public: + + static InventoryService& GetInstance() + { + static InventoryService instance; + + return instance; + } + + InventoryService(const InventoryService&) = delete; + InventoryService& operator=(const InventoryService&) = delete; + + std::vector getItemEquipmentSlots(const ItemTemplate* itemTemplate) const + { + if (itemTemplate == nullptr) + return {}; + + switch (itemTemplate->InventoryType) + { + case INVTYPE_HEAD: + return { EQUIPMENT_SLOT_HEAD }; + + case INVTYPE_NECK: + return { EQUIPMENT_SLOT_NECK }; + + case INVTYPE_SHOULDERS: + return { EQUIPMENT_SLOT_SHOULDERS }; + + case INVTYPE_BODY: + return { EQUIPMENT_SLOT_BODY }; + + case INVTYPE_ROBE: + case INVTYPE_CHEST: + return { EQUIPMENT_SLOT_CHEST }; + + case INVTYPE_WAIST: + return { EQUIPMENT_SLOT_WAIST }; + + case INVTYPE_LEGS: + return { EQUIPMENT_SLOT_LEGS }; + + case INVTYPE_FEET: + return { EQUIPMENT_SLOT_FEET }; + + case INVTYPE_WRISTS: + return { EQUIPMENT_SLOT_WRISTS }; + + case INVTYPE_HANDS: + return { EQUIPMENT_SLOT_HANDS }; + + case INVTYPE_FINGER: + return { EQUIPMENT_SLOT_FINGER1, EQUIPMENT_SLOT_FINGER2 }; + + case INVTYPE_TRINKET: + return { EQUIPMENT_SLOT_TRINKET1, EQUIPMENT_SLOT_TRINKET2 }; + + case INVTYPE_WEAPON: + return { EQUIPMENT_SLOT_MAINHAND, EQUIPMENT_SLOT_OFFHAND }; + + case INVTYPE_WEAPONOFFHAND: + case INVTYPE_SHIELD: + return { EQUIPMENT_SLOT_OFFHAND }; + + case INVTYPE_RANGED: + case INVTYPE_THROWN: + case INVTYPE_RELIC: + return { EQUIPMENT_SLOT_RANGED }; + + case INVTYPE_CLOAK: + return { EQUIPMENT_SLOT_BACK }; + + case INVTYPE_WEAPONMAINHAND: + case INVTYPE_2HWEAPON: + return { EQUIPMENT_SLOT_MAINHAND }; + + case INVTYPE_TABARD: + return { EQUIPMENT_SLOT_TABARD }; + + case INVTYPE_BAG: + case INVTYPE_HOLDABLE: + case INVTYPE_AMMO: + case INVTYPE_RANGEDRIGHT: + case INVTYPE_QUIVER: + default: + return {}; + } + } +private: + InventoryService() = default; + ~InventoryService() = default; +}; diff --git a/src/service/inventory/key/KeyItemVisitor.h b/src/service/inventory/key/KeyItemVisitor.h new file mode 100644 index 00000000000..51307dfc6f0 --- /dev/null +++ b/src/service/inventory/key/KeyItemVisitor.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include "ItemTemplate.h" +#include "AbstractItemVisitor.h" +#include "Player.h" + +class KeyItemVisitor : public AbstractItemVisitor +{ +public: + bool process(Player* , Item* ) override + { + return true; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_QUEST) + { + LOG_ERROR("playerbots", "Item {} was not of class QUEST ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + if (this->process(player, item)) + { + LOG_ERROR("playerbots", "Item {} was a useful KEY item", itemTemplate->ItemId); + + return true; + } + + this->destroy(player, item); + + return true; + } +}; diff --git a/src/service/inventory/miscellaneous/MiscellaneousItemVisitor.h b/src/service/inventory/miscellaneous/MiscellaneousItemVisitor.h new file mode 100644 index 00000000000..631220c7fdd --- /dev/null +++ b/src/service/inventory/miscellaneous/MiscellaneousItemVisitor.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include "ItemTemplate.h" +#include "AbstractItemVisitor.h" +#include "Player.h" + +class MiscellaneousItemVisitor : public AbstractItemVisitor +{ +public: + bool process(Player* , Item* ) override + { + // @TODO: Implement logic on miscellaneous item processing. + return false; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_MISC) + { + LOG_ERROR("playerbots", "Item {} was not of class MISC ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + if (this->process(player, item)) + { + LOG_ERROR("playerbots", "Item {} was a useful MISCELLANEOUS item", itemTemplate->ItemId); + + return true; + } + + this->magicSell(player, item); + + return true; + } +}; diff --git a/src/service/inventory/money/MoneyItemVisitor.h b/src/service/inventory/money/MoneyItemVisitor.h new file mode 100644 index 00000000000..0323b8dac07 --- /dev/null +++ b/src/service/inventory/money/MoneyItemVisitor.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include +#include "ItemTemplate.h" +#include "AbstractItemVisitor.h" +#include "Player.h" + +// Currency items +class MoneyItemVisitor : public AbstractItemVisitor +{ +public: + bool process(Player* player, Item* item) override + { + return true; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_MONEY) + { + LOG_ERROR("playerbots", "Item {} was not of class MONEY ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + if (!this->isAllowed(item)) + { + this->destroyAll(player, item); + + return true; + } + + if (this->process(player, item)) + { + LOG_ERROR("playerbots", "Item {} was a useful MONEY item", itemTemplate->ItemId); + + return true; + } + + this->magicSell(player, item); + + return true; + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() override + { + return { + 37711, // Currency Token Test Token 1 + 37742, // Currency Token Test Token 2 + 38644, // Currency Token Test Token 3 + 41749, // Birmingham Test Item 3 + 43308, // Honor Points (item, not the real currency) + 43949, // zzzOLDDaily Quest Faction Token + 44209, // NPC Equip 44209 => Illegal item that should not even be present in the core as it is not present in any WotLK database + }; + } +}; diff --git a/src/service/inventory/permanent/PermanentItemVisitor.h b/src/service/inventory/permanent/PermanentItemVisitor.h new file mode 100644 index 00000000000..ef6337d7b28 --- /dev/null +++ b/src/service/inventory/permanent/PermanentItemVisitor.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include "ItemTemplate.h" +#include "AbstractItemVisitor.h" +#include "Player.h" + +/** + * @brief Handles the inventory management of generic items (class 14). + * + * @remark There is no item of this category in the game data. + * This category of item is thus always useless. + * + */ +class PermanentItemVisitor : public AbstractItemVisitor +{ +public: + bool process(Player* , Item* ) override + { + return true; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_PERMANENT) + { + LOG_ERROR("playerbots", "Item {} was not of class PERMANENT ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + LOG_ERROR("playerbots", "Player (bot) {} (#{}) had item of impossible category PERMANENT (14) #{}", + player->GetName(), std::to_string(player->GetGUID().GetEntry()), std::to_string(item->GetTemplate()->ItemId)); + + this->destroy(player, item); + + return true; + } +}; diff --git a/src/service/inventory/projectile/ProjectileItemVisitor.h b/src/service/inventory/projectile/ProjectileItemVisitor.h new file mode 100644 index 00000000000..3f0baaec7c4 --- /dev/null +++ b/src/service/inventory/projectile/ProjectileItemVisitor.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include "ItemTemplate.h" +#include "AbstractItemVisitor.h" +#include "Player.h" +#include "SharedDefines.h" + +class ProjectileItemVisitor : public AbstractItemVisitor +{ +public: + bool process(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + if (item == nullptr) + return false; + + if (player->getClass() == CLASS_HUNTER) + return true; + + // @TODO: Implement logic on projectile processing. + return false; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_PROJECTILE) + { + LOG_ERROR("playerbots", "Item {} was not of class PROJECTILE ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + if (this->process(player, item)) + { + LOG_ERROR("playerbots", "Item {} was a useful PROJECTILE item", itemTemplate->ItemId); + + return true; + } + + this->magicSell(player, item); + + return true; + } +}; diff --git a/src/service/inventory/quest/QuestItemVisitor.h b/src/service/inventory/quest/QuestItemVisitor.h new file mode 100644 index 00000000000..f840083e5d0 --- /dev/null +++ b/src/service/inventory/quest/QuestItemVisitor.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include +#include "ItemTemplate.h" +#include "AbstractItemVisitor.h" +#include "Log.h" +#include "Player.h" + +class QuestItemVisitor : public AbstractItemVisitor +{ +public: + bool process(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + const bool hasQuestForItem = player->HasQuestForItem(itemTemplate->ItemId, 0, true); + + LOG_ERROR("playerbots", "Has quest for item {}: {}", std::to_string(itemTemplate->ItemId), std::to_string(hasQuestForItem)); + + return hasQuestForItem; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_QUEST) + { + LOG_ERROR("playerbots", "Item {} was not of class QUEST ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + if (this->process(player, item)) + { + LOG_ERROR("playerbots", "Item {} was a useful QUEST item", itemTemplate->ItemId); + + return true; + } + + this->destroy(player, item); + + return true; + } +}; diff --git a/src/service/inventory/quiver/QuiverItemVisitor.h b/src/service/inventory/quiver/QuiverItemVisitor.h new file mode 100644 index 00000000000..35a6dbb5bf2 --- /dev/null +++ b/src/service/inventory/quiver/QuiverItemVisitor.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include "ItemTemplate.h" +#include "AbstractItemVisitor.h" +#include "Player.h" + +class QuiverItemVisitor : public AbstractItemVisitor +{ +public: + bool process(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + if (item == nullptr) + return false; + + // @TODO: Implement logic on quiver processing. + return false; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_QUIVER) + { + LOG_ERROR("playerbots", "Item {} was not of class QUIVER ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + if (this->process(player, item)) + { + LOG_ERROR("playerbots", "Item {} was a useful QUIVER item", itemTemplate->ItemId); + + return true; + } + + + this->magicSell(player, item); + + return true; + } +}; diff --git a/src/service/inventory/reagent/ReagentItemVisitor.h b/src/service/inventory/reagent/ReagentItemVisitor.h new file mode 100644 index 00000000000..721c73ea603 --- /dev/null +++ b/src/service/inventory/reagent/ReagentItemVisitor.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include "ItemTemplate.h" +#include "Player.h" + +#include "AbstractItemVisitor.h" + +class ReagentItemVisitor : public AbstractItemVisitor +{ +public: + bool process(Player* , Item* ) override + { + // @TODO: Implement logic on reagent processing. + return false; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_REAGENT) + { + LOG_ERROR("playerbots", "Item {} was not of class REAGENT ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + if (this->process(player, item)) + { + LOG_ERROR("playerbots", "Item {} was a useful REAGENT item", itemTemplate->ItemId); + + return true; + } + + this->magicSell(player, item); + + return true; + } +}; diff --git a/src/service/inventory/recipe/RecipeItemVisitor.h b/src/service/inventory/recipe/RecipeItemVisitor.h new file mode 100644 index 00000000000..32318009cba --- /dev/null +++ b/src/service/inventory/recipe/RecipeItemVisitor.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include "ItemTemplate.h" +#include "AbstractItemVisitor.h" +#include "Player.h" + +class RecipeItemVisitor : public AbstractItemVisitor +{ +public: + bool process(Player* , Item* ) override + { + // @TODO: Implement logic on recipe processing. + return false; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_RECIPE) + { + LOG_ERROR("playerbots", "Item {} was not of class RECIPE ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + if (this->process(player, item)) + { + LOG_ERROR("playerbots", "Item {} was a useful RECIPE item", itemTemplate->ItemId); + + return true; + } + + this->magicSell(player, item); + + return true; + } +}; diff --git a/src/service/inventory/trade-good/TradeGoodItemVisitor.h b/src/service/inventory/trade-good/TradeGoodItemVisitor.h new file mode 100644 index 00000000000..929331df288 --- /dev/null +++ b/src/service/inventory/trade-good/TradeGoodItemVisitor.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include "ItemTemplate.h" +#include "AbstractItemVisitor.h" +#include "Player.h" + +class TradeGoodItemVisitor : public AbstractItemVisitor +{ +public: + bool process(Player* , Item* ) override + { + // @TODO: Implement logic on projectile processing. + return false; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_TRADE_GOODS) + { + LOG_ERROR("playerbots", "Item {} was not of class TRADE GOODS ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + if (this->process(player, item)) + { + LOG_ERROR("playerbots", "Item {} was a useful TRADE GOODS item", itemTemplate->ItemId); + + return true; + } + + this->magicSell(player, item); + + return true; + } +}; diff --git a/src/service/inventory/weapon/WeaponItemVisitor.h b/src/service/inventory/weapon/WeaponItemVisitor.h new file mode 100644 index 00000000000..b97b440d759 --- /dev/null +++ b/src/service/inventory/weapon/WeaponItemVisitor.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include + +#include "ItemTemplate.h" +#include "Player.h" +#include "StatsWeightCalculator.h" + +#include "AbstractItemVisitor.h" +#include "InventoryService.h" + +class WeaponItemVisitor : public AbstractItemVisitor +{ +public: + bool process(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (!player->CanUseItem(item)) + return false; + + StatsWeightCalculator statisticsWeightCalculator = StatsWeightCalculator(player); + std::vector slots = InventoryService::GetInstance().getItemEquipmentSlots(itemTemplate); + + float newItemStatisticsWeight = statisticsWeightCalculator.CalculateItem(itemTemplate->ItemId); + + statisticsWeightCalculator.Reset(); + + for (uint8_t i = 0; i < slots.size(); ++i) + { + Item* currentlyEquippedItem = player->GetItemByPos(slots[i]); + + if (currentlyEquippedItem == nullptr) + continue; + + float existingItemStatisticsWeight = statisticsWeightCalculator.CalculateItem(currentlyEquippedItem->GetTemplate()->ItemId); + + if (existingItemStatisticsWeight < newItemStatisticsWeight) + { + this->equipWeapon(player, item, slots[i]); + + return true; + } + } + + return false; + } + + bool visit(Player* player, Item* item) override + { + if (player == nullptr) + return false; + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + if (itemTemplate == nullptr) + { + LOG_ERROR("playerbots", "Item {} had nullptr template", std::to_string(item->GetGUID().GetRawValue())); + + return false; + } + + if (itemTemplate->Class != ITEM_CLASS_WEAPON) + { + LOG_ERROR("playerbots", "Item {} was not of class WEAPON ({}), found {}", itemTemplate->ItemId, std::to_string(ITEM_CLASS_ARMOR), std::to_string(itemTemplate->Class)); + + return false; + } + + if (this->process(player, item)) + { + LOG_ERROR("playerbots", "Item {} was a useful WEAPON item", itemTemplate->ItemId); + + return true; + } + + this->magicSell(player, item); + + return true; + } +}; diff --git a/src/service/statistic/abstract/AbstractItemValueCalculationService.h b/src/service/statistic/abstract/AbstractItemValueCalculationService.h new file mode 100644 index 00000000000..db9c3b787e3 --- /dev/null +++ b/src/service/statistic/abstract/AbstractItemValueCalculationService.h @@ -0,0 +1,198 @@ +#ifndef _PLAYERBOT_ABSTRACT_ITEM_VALUE_CALCULATION_SERVICE_H +#define _PLAYERBOT_ABSTRACT_ITEM_VALUE_CALCULATION_SERVICE_H + +#include + +#include "ItemTemplate.h" +#include "Player.h" + +class AbstractItemValueCalculationService +{ +public: + + inline uint64_t calculateItemValue(Player* player, ItemTemplate* itemTemplate) + { + uint64_t itemValue = 0; + + for (uint8_t i = 0; i < itemTemplate->StatsCount; ++i) + { + uint8_t statisticWeight = this->getStatisticWeight(player, itemTemplate, itemTemplate->ItemStat[i].ItemStatType); + + itemValue += statisticWeight * itemTemplate->ItemStat[i].ItemStatValue; + } + + if (itemTemplate->Armor > 0) + itemValue += this->getArmorRatingWeight(player) * itemTemplate->Armor; + + if (itemTemplate->FireRes > 0) + itemValue += this->getFireResistanceWeight(player); + + if (itemTemplate->NatureRes > 0) + itemValue += this->getNatureResistanceWeight(player); + + if (itemTemplate->FrostRes > 0) + itemValue += this->getFrostResistanceWeight(player); + + if (itemTemplate->ShadowRes > 0) + itemValue += this->getShadowResistanceWeight(player); + + if (itemTemplate->ArcaneRes > 0) + itemValue += this->getArcaneResistanceWeight(player); + + return itemValue; + } + + inline uint8_t getStatisticWeight(Player* player, ItemTemplate* itemTemplate, uint32_t type) + { + uint8_t primaryStatisticWeight = this->getPrimaryStatisticWeight(player, type); + + if (primaryStatisticWeight != 0) + return primaryStatisticWeight; + + uint8_t physicalStatisticWeight = this->getPhysicalStatisticWeight(player, type); + + if (physicalStatisticWeight != 0) + return physicalStatisticWeight; + + uint8_t spellStatisticWeight = this->getSpellStatisticWeight(player, type); + + if (spellStatisticWeight != 0) + return spellStatisticWeight; + + return this->getDefenseStatisticWeight(player, type); + } + + inline uint8_t getPrimaryStatisticWeight(Player* player, uint32_t type) + { + switch (type) + { + case ITEM_MOD_STRENGTH: + return this->getStrengthWeight(player); + case ITEM_MOD_AGILITY: + return this->getAgilityWeight(player); + case ITEM_MOD_STAMINA: + return this->getStaminaWeight(player); + case ITEM_MOD_INTELLECT: + return this->getIntellectWeight(player); + case ITEM_MOD_SPIRIT: + return this->getSpiritWeight(player); + default: + return 0; + } + } + + inline uint8_t getPhysicalStatisticWeight(Player* player, uint32_t type) + { + switch (type) + { + case ITEM_MOD_ATTACK_POWER: + return this->getAttackPowerWeight(player); + case ITEM_MOD_HASTE_RATING: + return this->getHasteRatingWeight(player); + case ITEM_MOD_HIT_RATING: + return this->getHitRatingWeight(player); + case ITEM_MOD_CRIT_RATING: + return this->getCriticalStrikeRatingWeight(player); + case ITEM_MOD_EXPERTISE_RATING: + return this->getExperiseRatingWeight(player); + case ITEM_MOD_ARMOR_PENETRATION_RATING: + return this->getArmorPenetrationRatingWeight(player); + default: + return 0; + } + } + + inline uint8_t getSpellStatisticWeight(Player* player, uint32_t type) + { + switch (type) + { + case ITEM_MOD_SPELL_POWER: + return this->getSpellPowerWeight(player); + case ITEM_MOD_SPELL_HEALING_DONE: + return this->getHealingPowerWeight(player); + case ITEM_MOD_CRIT_SPELL_RATING: + return this->getSpellCriticalStrikeRatingWeight(player); + case ITEM_MOD_HIT_SPELL_RATING: + return this->getSpellHitRatingWeight(player); + case ITEM_MOD_HASTE_SPELL_RATING: + return this->getSpellHasteRatingWeight(player); + case ITEM_MOD_MANA_REGENERATION: + return this->getManaPer5SecondsWeight(player); + case ITEM_MOD_SPELL_PENETRATION: + return this->getSpellPenetrationRatingWeight(player); + default: + return 0; + } + } + + inline uint8_t getDefenseStatisticWeight(Player* player, uint32_t type) + { + switch (type) + { + case ITEM_MOD_DEFENSE_SKILL_RATING: + return this->getDefenseRatingWeight(player); + case ITEM_MOD_DODGE_RATING: + return this->getDodgeRatingWeight(player); + case ITEM_MOD_PARRY_RATING: + return this->getParryRatingWeight(player); + case ITEM_MOD_BLOCK_RATING: + return this->getBlockRatingWeight(player); + case ITEM_MOD_BLOCK_VALUE: + return this->getBlockValueWeight(player); + case ITEM_MOD_RESILIENCE_RATING: + return this->getResilienceRatingWeight(player); + default: + return 0; + } + } + +protected: + + // Primary + virtual inline const uint8_t getStrengthWeight(Player* player) { return 0; }; + virtual inline const uint8_t getAgilityWeight(Player* player) { return 0; }; + virtual inline const uint8_t getStaminaWeight(Player* player) { return 0; }; + virtual inline const uint8_t getIntellectWeight(Player* player) { return 0; }; + virtual inline const uint8_t getSpiritWeight(Player* player) { return 0; }; + + // Physical + virtual inline const uint8_t getAttackPowerWeight(Player* player) { return 0; }; + virtual inline const uint8_t getHasteRatingWeight(Player* player) { return 0; }; + virtual inline const uint8_t getHitRatingWeight(Player* player) { return 0; }; + virtual inline const uint8_t getCriticalStrikeRatingWeight(Player* player) { return 0; }; + virtual inline const uint8_t getExperiseRatingWeight(Player* player) { return 0; }; + virtual inline const uint8_t getArmorPenetrationRatingWeight(Player* player) { return 0; }; + + // Spell + virtual inline const uint8_t getSpellPowerWeight(Player* player) { return 0; }; + + // Unable to find these in the ItemTemplate struct + // virtual inline const uint8_t getHolySpellPowerWeight(Player* player) { return 0; }; + // virtual inline const uint8_t getFireSpellPowerWeight(Player* player) { return 0; }; + // virtual inline const uint8_t getNatureSpellPowerWeight(Player* player) { return 0; }; + // virtual inline const uint8_t getFrostSpellPowerWeight(Player* player) { return 0; }; + // virtual inline const uint8_t getShadowSpellPowerWeight(Player* player) { return 0; }; + // virtual inline const uint8_t getArcaneSpellPowerWeight(Player* player) { return 0; }; + virtual inline const uint8_t getHealingPowerWeight(Player* player) { return 0; }; + virtual inline const uint8_t getSpellHitRatingWeight(Player* player) { return 0; }; + virtual inline const uint8_t getSpellCriticalStrikeRatingWeight(Player* player) { return 0; }; + virtual inline const uint8_t getSpellHasteRatingWeight(Player* player) { return 0; }; + virtual inline const uint8_t getManaPer5SecondsWeight(Player* player) { return 0; }; + virtual inline const uint8_t getSpellPenetrationRatingWeight(Player* player) { return 0; }; + + // Defense + virtual inline const uint8_t getArmorRatingWeight(Player* player) { return 0; }; + virtual inline const uint8_t getDefenseRatingWeight(Player* player) { return 0; }; + virtual inline const uint8_t getDodgeRatingWeight(Player* player) { return 0; }; + virtual inline const uint8_t getParryRatingWeight(Player* player) { return 0; }; + virtual inline const uint8_t getBlockRatingWeight(Player* player) { return 0; }; + virtual inline const uint8_t getBlockValueWeight(Player* player) { return 0; }; + virtual inline const uint8_t getResilienceRatingWeight(Player* player) { return 0; }; + virtual inline const uint8_t getFireResistanceWeight(Player* player) { return 0; }; + virtual inline const uint8_t getNatureResistanceWeight(Player* player) { return 0; }; + virtual inline const uint8_t getFrostResistanceWeight(Player* player) { return 0; }; + virtual inline const uint8_t getShadowResistanceWeight(Player* player) { return 0; }; + virtual inline const uint8_t getArcaneResistanceWeight(Player* player) { return 0; }; +}; + +#endif diff --git a/src/service/statistic/warrior/GenericWarriorItemValueCalculationService.h b/src/service/statistic/warrior/GenericWarriorItemValueCalculationService.h new file mode 100644 index 00000000000..bd18b1f5220 --- /dev/null +++ b/src/service/statistic/warrior/GenericWarriorItemValueCalculationService.h @@ -0,0 +1,75 @@ +#ifndef _PLAYERBOT_GENERIC_WARRIOR_ITEM_VALUE_CALCULATION_SERVICE_H +#define _PLAYERBOT_GENERIC_WARRIOR_ITEM_VALUE_CALCULATION_SERVICE_H + +#include + +#include "AbstractItemValueCalculationService.h" +#include "Player.h" + +class GenericWarriorItemValueCalculationService : public AbstractItemValueCalculationService +{ +protected: + + // Primary + inline const uint8_t getStrengthWeight(Player* player) override + { + return 4; + }; + + inline const uint8_t getAgilityWeight(Player* player) override + { + return 1; + }; + + inline const uint8_t getStaminaWeight(Player* player) override + { + return 2; + }; + + // Physical + inline const uint8_t getAttackPowerWeight(Player* player) override + { + return 2; + }; + + inline const uint8_t getHasteRatingWeight(Player* player) override + { + return 2; + }; + + inline const uint8_t getHitRatingWeight(Player* player) override + { + return 2; + }; + + inline const uint8_t getCriticalStrikeRatingWeight(Player* player) override + { + return 2; + }; + + inline const uint8_t getExperiseRatingWeight(Player* player) override + { + return 2; + }; + + inline const uint8_t getArmorPenetrationRatingWeight(Player* player) override + { + return 2; + }; + + // Defense + // virtual inline const uint8_t getArmorRatingWeight(Player* player) { return 0; }; + // virtual inline const uint8_t getDefenseRatingWeight(Player* player) { return 0; }; + // virtual inline const uint8_t getDodgeRatingWeight(Player* player) { return 0; }; + // virtual inline const uint8_t getParryRatingWeight(Player* player) { return 0; }; + // virtual inline const uint8_t getBlockRatingWeight(Player* player) { return 0; }; + // virtual inline const uint8_t getBlockValueWeight(Player* player) { return 0; }; + // virtual inline const uint8_t getResilienceRatingWeight(Player* player) { return 0; }; + // virtual inline const uint8_t getFireResistanceWeight(Player* player) { return 0; }; + // virtual inline const uint8_t getNatureResistanceWeight(Player* player) { return 0; }; + // virtual inline const uint8_t getFrostResistanceWeight(Player* player) { return 0; }; + // virtual inline const uint8_t getShadowResistanceWeight(Player* player) { return 0; }; + // virtual inline const uint8_t getArcaneResistanceWeight(Player* player) { return 0; }; +}; + +#endif diff --git a/src/strategy/actions/player/inventory/ManageInventoryAction.cpp b/src/strategy/actions/player/inventory/ManageInventoryAction.cpp new file mode 100644 index 00000000000..1f1c60c638b --- /dev/null +++ b/src/strategy/actions/player/inventory/ManageInventoryAction.cpp @@ -0,0 +1,564 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * /modules/playerbots/src/strategy/actions/player/inventoryand/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "ManageInventoryAction.h" + +#include +#include +#include +#include + +#include "ItemActionEnum.h" +#include "Log.h" +#include "Player.h" +#include "ChatHelper.h" +#include "ItemTemplate.h" + +#include "GlobalPlayerInspector.h" +#include "GlobalItemInspector.h" +#include "PlayerbotAI.h" +#include "Event.h" +#include "RandomPlayerbotMgr.h" +#include "ItemActionStruct.h" + +using InspectorFactory = std::function; +using SubclassMap = std::unordered_map; +using ClassMap = std::unordered_map; + +static const ClassMap inspectorFactories = { + { + ITEM_CLASS_ARMOR, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return ArmorItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_CONSUMABLE, + { + { + { + ITEM_SUBCLASS_CONSUMABLE, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return ConsumableConsumableInspector(botGUID, itemGUID).determineItemAction(); + } + }, + { + ITEM_SUBCLASS_ELIXIR, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return ConsumableElixirInspector(botGUID, itemGUID).determineItemAction(); + } + }, + { + ITEM_SUBCLASS_POTION, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return ConsumablePotionInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + } + }, + { + ITEM_CLASS_CONTAINER, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return ContainerInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_GEM, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return GemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_GENERIC, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return GenericItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_GLYPH, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return GlyphInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_KEY, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return KeyInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_MISC, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return MiscellaneousItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_MONEY, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return MoneyInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_PERMANENT, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return PermanentItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_PROJECTILE, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return ProjectItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_QUEST, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return QuestItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_QUIVER, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return QuiverItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_REAGENT, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return ReagentItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_RECIPE, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return RecipeItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_TRADE_GOODS, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return TradeGoodItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_WEAPON, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + return WeaponItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, +}; + +bool ManageInventoryAction::Execute(Event) +{ + LOG_ERROR("playerbots", "starting inventory management action"); + + std::unordered_map itemActions = {}; + + if (this->bot == nullptr) + { + LOG_ERROR("playerbots", "ManageInventoryAction::Execute bot is nullptr"); + + return false; + } + + const std::string botName = this->bot->GetName(); + + LOG_ERROR("playerbots", "managing inventory for {}", botName); + + if (!RandomPlayerbotMgr::instance().IsRandomBot(bot)) + return true; + + this->iterateBags(); + + LOG_ERROR("playerbots", "done processing bags for {}", botName); + + if (this->botAI != nullptr) + { + const Player* const master = botAI->GetMaster(); + + LOG_ERROR("playerbots", "fetched master for {}", botName); + + if (master != nullptr) + { + const std::string masterName = master->GetName(); + + LOG_ERROR("playerbots", "telling master ({}) of bot {}", masterName, botName); + + std::ostringstream out; + + out << "I am done sorting my inventory."; + + if (this->botAI != nullptr) + { + this->botAI->TellMaster(out); + } + + LOG_ERROR("playerbots", "told master ({}) of bot {}", masterName, botName); + } + } + + for (const auto& [itemGUID, action] : this->itemActions) + { + + std::string actionName = "UNKNOWN_ACTION"; + + switch (action.action) + { + case ItemActionEnum::NONE: + actionName = "ITEM_ACTION_NONE"; + break; + case ItemActionEnum::EQUIP: + actionName = "ITEM_ACTION_EQUIP"; + break; + case ItemActionEnum::SELL: + actionName = "ITEM_ACTION_SELL"; + break; + case ItemActionEnum::DESTROY: + actionName = "ITEM_ACTION_DESTROY"; + break; + } + + LOG_ERROR("playerbots", "Item {} action: {}, inventorySlot: {}, equipmentSlot: {}", std::to_string(itemGUID), actionName, std::to_string(action.inventorySlot), std::to_string(action.equipmentSlot)); + } + + LOG_ERROR("playerbots", "Done processing inventory"); + + return true; + // if (this->bot != nullptr) + // { + // bot->SaveToDB(false, false); + + // return true; + // } + + // LOG_ERROR("playerbots", "ManageInventoryAction::Execute can't save, bot is nullptr"); + + // return false; +} + +void ManageInventoryAction::iterateBags() +{ + LOG_ERROR("playerbots", "Processing main bag"); + + if (this->bot == nullptr) + { + LOG_ERROR("playerbots", "ManageInventoryAction::iterateBags bot became nullptr"); + + return; + } + + const GlobalPlayerInspector playerInspector(this->bot->GetGUID().GetRawValue()); + + for (uint8_t slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++) + { + LOG_ERROR("playerbots", "Processing item in main bag slot {}", std::to_string(slot)); + + Item* item = playerInspector.getItemByPosition(INVENTORY_SLOT_BAG_0, slot); + + if (item == nullptr) + { + LOG_ERROR("playerbots", "Item in main bag slot {} did not exist", std::to_string(slot)); + + continue; + } + + const uint64_t itemLowGUID = item->GetGUID().GetRawValue(); + + this->processItem(itemLowGUID); + } + + for (uint8_t slot = INVENTORY_SLOT_BAG_START; slot < INVENTORY_SLOT_BAG_END; ++slot) + { + LOG_ERROR("playerbots", "Processing bag in slot {}", std::to_string(slot)); + + if (this->bot == nullptr) + { + LOG_ERROR("playerbots", "ManageInventoryAction::iterateBags bot became nullptr"); + + return; + } + + const Item* const possibleBag = playerInspector.getItemByPosition(INVENTORY_SLOT_BAG_0, slot); + + if (possibleBag == nullptr) + continue; + + const uint64_t bagLowGUID = possibleBag->GetGUID().GetRawValue(); + + this->iterateBag(bagLowGUID); + } + + if (this->bot == nullptr) + { + LOG_ERROR("playerbots", "ManageInventoryAction::iterateBags bot became nullptr"); + + return; + } + + LOG_ERROR("playerbots", "done processing bags for {}", this->bot->GetName()); +} + +void ManageInventoryAction::iterateBag(const uint64_t bagGUID) +{ + const GlobalPlayerInspector playerInspector(this->bot->GetGUID().GetRawValue()); + const Item* const possibleBag = playerInspector.getItemByGUID(bagGUID); + + if (possibleBag == nullptr) + return; + + const Bag* bag = possibleBag->ToBag(); + + if (bag == nullptr) + { + LOG_ERROR("playerbots", "Bag did not exist"); + + return; + } + + const uint8_t size = bag->GetBagSize(); + + for (uint8_t slot = 0; slot < size; ++slot) + { + LOG_ERROR("playerbots", "Processing item {} in bag", std::to_string(slot)); + + if (bag == nullptr) + { + LOG_ERROR("playerbots", "ManageInventoryAction::iterateBag bag became nullptr"); + + return; + } + + Item* item = bag->GetItemByPos(slot); + + if (item == nullptr) + { + LOG_ERROR("playerbots", "Item {} in bag did not exist", std::to_string(slot)); + + continue; + } + + const uint64_t itemGUID = item->GetGUID().GetRawValue(); + + this->processItem(itemGUID); + } + + LOG_ERROR("playerbots", "done processing bag {}", std::to_string(bag->GetSlot())); +} + +void ManageInventoryAction::processItem(const uint64_t itemGUID) +{ + if (this->bot == nullptr) + { + LOG_ERROR("playerbots", "ManageInventoryAction::processItem bot became nullptr"); + + return; + } + + const uint32_t botGUID = this->bot->GetGUID().GetRawValue(); + const GlobalPlayerInspector playerInspector(botGUID); + const GlobalItemInspector itemInspector(botGUID, itemGUID); + const ItemUpdateState itemState = itemInspector.getItemUpdateState(); + const uint32_t itemTemplateLowGUID = itemInspector.getCurrentItemTemplateLowGUID(); + + if (itemState == ITEM_REMOVED) + { + LOG_ERROR("playerbots", "Item {} (template {}) was already flagged for removal.", itemGUID, itemTemplateLowGUID); + + return; + } + + if (itemState == ITEM_CHANGED) + { + LOG_ERROR("playerbots", "Item {} (template {}) was already modified.", itemGUID, itemTemplateLowGUID); + + return; + } + + if (!itemInspector.itemIsInWorld()) + { + LOG_ERROR("playerbots", "Item {} (template {}) is not in world.", itemGUID, itemTemplateLowGUID); + + return; + } + + const uint8_t itemSlot = itemInspector.getItemSlot(); + + if (itemSlot == NULL_SLOT) + { + LOG_ERROR("playerbots", "Item {} (template {}) is null slot.", itemGUID, itemTemplateLowGUID); + + return; + } + + if (itemInspector.itemIsInUnsafeContainer()) + { + LOG_ERROR("playerbots", "Item {} (template {}) is in unsafe container", itemGUID, itemTemplateLowGUID); + + return; + } + + const uint32_t itemClass = itemInspector.getCurrentItemClass(); + const uint32_t itemSubClass = itemInspector.getCurrentItemSubclass(); + + LOG_ERROR("playerbots", "processing item {}", std::to_string(itemTemplateLowGUID)); + + ClassMap::const_iterator classIterator = inspectorFactories.find(itemClass); + + if (classIterator == inspectorFactories.end()) + { + LOG_ERROR("playerbots", "Unable to locate service for item class {} item service", std::to_string(itemClass)); + + return; + } + + const SubclassMap& subclassMap = classIterator->second; + + SubclassMap::const_iterator subclassIterator = subclassMap.find(itemSubClass); + + if (subclassIterator == subclassMap.end()) + { + subclassIterator = subclassMap.find(ManageInventoryAction::ANY_SUBCLASS); + } + + if (subclassIterator == subclassMap.end()) + { + LOG_ERROR("playerbots", "Unable to locate service for item class {} subclass {} item service", std::to_string(itemClass), std::to_string(itemSubClass)); + + return; + } + + const InspectorFactory& factoryFunction = subclassIterator->second; + + ItemActionStruct action = factoryFunction(botGUID, itemGUID); + + std::string itemAction = "NONE"; + + if (action.action == ItemActionEnum::DESTROY) + { + itemAction = "DESTROY"; + } + + if (action.action == ItemActionEnum::EQUIP) + { + itemAction = "EQUIP"; + } + + if (action.action == ItemActionEnum::SELL) + { + itemAction = "SELL"; + } + + LOG_ERROR("playerbots", "Determined action {} for item {}", itemAction, std::to_string(itemTemplateLowGUID)); + + LOG_ERROR("playerbots", "Done visiting item {}", std::to_string(itemTemplateLowGUID)); +} + +template +void ManageInventoryAction::determineItemAction(const uint32_t botLowGUID, const uint64_t itemLowGUID) +{ + InspectorT inspector(botLowGUID, itemLowGUID); + const ItemActionStruct action = inspector.determineItemAction(); + this->itemActions.insert({itemLowGUID, action}); +} diff --git a/src/strategy/actions/player/inventory/ManageInventoryAction.h b/src/strategy/actions/player/inventory/ManageInventoryAction.h new file mode 100644 index 00000000000..664496fab5d --- /dev/null +++ b/src/strategy/actions/player/inventory/ManageInventoryAction.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#pragma once + +#include +#include +#include + +#include "Bag.h" +#include "ItemTemplate.h" + +#include "Action.h" +#include "Log.h" +#include "PlayerbotAI.h" +#include "ItemActionStruct.h" +#include "AbstractItemInspector.h" +#include "ArmorItemInspector.h" +#include "ConsumableConsumableInspector.h" +#include "ConsumableElixirInspector.h" +#include "ConsumablePotionInspector.h" +#include "ContainerInspector.h" +#include "GemInspector.h" +#include "GenericItemInspector.h" +#include "GlyphInspector.h" +#include "ItemTemplate.h" +#include "KeyInspector.h" +#include "MiscellaneousItemInspector.h" +#include "MoneyInspector.h" +#include "PermanentItemInspector.h" +#include "ProjectItemInspector.h" +#include "QuestItemInspector.h" +#include "QuiverItemInspector.h" +#include "ReagentItemInspector.h" +#include "RecipeItemInspector.h" +#include "TradeGoodItemInspector.h" +#include "WeaponItemInspector.h" + + +class ManageInventoryAction : public Action +{ +public: + ManageInventoryAction(PlayerbotAI* botAI) : Action(botAI, "manage_inventory") {} + + static constexpr uint32_t ANY_SUBCLASS = std::numeric_limits::max(); + + bool isPossible() override + { + LOG_ERROR("playerbots", "Starting manage inventory isPossible evaluation"); + + const Player* const bot = botAI->GetBot(); + + if (bot == nullptr) + { + LOG_ERROR("playerbots", "Manage inventory impossible bot is nullptr"); + + return false; + } + + const std::string botName = bot->GetName(); + + if (!bot->IsInWorld()) + { + LOG_ERROR("playerbots", "Manage inventory impossible bot '{}' is non in world", botName); + + return false; + } + + const WorldSession* const session = bot->GetSession(); + + if (session == nullptr) + { + LOG_ERROR("playerbots", "Manage inventory impossible bot '{}' session is nullptr", botName); + + return false; + } + + if (session->isLogingOut()) + { + LOG_ERROR("playerbots", "Manage inventory impossible bot '{}' is logging out", botName); + + return false; + } + + return true; + } + + bool Execute(Event event) override; + +private: + + std::unordered_map itemActions = {}; + + void iterateBags(); + void iterateBag(const uint64_t bagGUID); + void processItem(const uint64_t itemGUID); + template + void determineItemAction(const uint32_t botLowGUID, const uint64_t itemLowGUID); +}; From 482147ab94399ad6dc617167a47c7956325c6e79 Mon Sep 17 00:00:00 2001 From: SmashingQuasar Date: Wed, 14 Jan 2026 12:51:12 +0100 Subject: [PATCH 3/8] fix: Resolved the undefined behaviour crash within the inventory management. --- src/Ai/Base/Strategy/NonCombatStrategy.cpp | 10 +- .../abstract/AbstractItemInspector.h | 45 ++++- .../definition/struct/ItemActionStruct.h | 3 +- .../item/inspector/armor/ArmorItemInspector.h | 44 +++-- .../elixir/ConsumableElixirInspector.h | 3 +- .../potion/ConsumablePotionInspector.h | 3 +- .../inspector/container/ContainerInspector.h | 23 +-- .../inspector/weapon/WeaponItemInspector.h | 14 +- .../facade/inventory/PlayerInventoryFacade.h | 111 +++++++++++- .../enum/PlayerInventoryFacadeResultEnum.h | 2 + .../inventory/ManageInventoryAction.cpp | 163 ++++++++++++++---- .../player/inventory/ManageInventoryAction.h | 2 +- 12 files changed, 333 insertions(+), 90 deletions(-) diff --git a/src/Ai/Base/Strategy/NonCombatStrategy.cpp b/src/Ai/Base/Strategy/NonCombatStrategy.cpp index 977343cc26d..983c3f76f97 100644 --- a/src/Ai/Base/Strategy/NonCombatStrategy.cpp +++ b/src/Ai/Base/Strategy/NonCombatStrategy.cpp @@ -12,6 +12,7 @@ #include "WorldBuffAction.h" #include "FishingAction.h" #include "EquipAction.h" +#include "ManageInventoryAction.h" void NonCombatStrategy::InitTriggers(std::vector& triggers) { @@ -23,7 +24,14 @@ void NonCombatStrategy::InitTriggers(std::vector& triggers) } ) ); - // triggers.push_back(new TriggerNode("seldom", { NextAction("manage inventory", 1.0f) })); + triggers.push_back( + new TriggerNode( + "seldom", + { + CreateNextAction(1.0f) + } + ) + ); triggers.push_back( new TriggerNode( "timer", diff --git a/src/domain/item/inspector/abstract/AbstractItemInspector.h b/src/domain/item/inspector/abstract/AbstractItemInspector.h index ff8c662848e..55dc9b9be4e 100644 --- a/src/domain/item/inspector/abstract/AbstractItemInspector.h +++ b/src/domain/item/inspector/abstract/AbstractItemInspector.h @@ -22,7 +22,7 @@ class AbstractItemInspector : public AbstractInspector return this->getDefaultItemAction(); }; - uint8_t getItemInventorySlot() const + uint8_t getBagSlot() const { const Item* const item = this->getCurrentItem(); @@ -62,6 +62,16 @@ class AbstractItemInspector : public AbstractInspector return itemTemplate->Class; } + uint8_t getCurrentItemInventoryType() const + { + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return 0; + + return itemTemplate->InventoryType; + } + uint32_t getCurrentItemSubclass() const { const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); @@ -92,14 +102,29 @@ class AbstractItemInspector : public AbstractInspector return item->GetState(); } - bool itemIsSellable() const + uint32_t getItemSellPrice() const { const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); if (itemTemplate == nullptr) - return false; + return 0; - return itemTemplate->SellPrice != 0; + return itemTemplate->SellPrice; + } + + uint32_t getItemCurrentCount() const + { + const Item* const item = this->getCurrentItem(); + + if (item == nullptr) + return 0; + + return item->GetCount(); + } + + bool itemIsSellable() const + { + return this->getItemSellPrice() != 0; } bool itemIsInWorld() const @@ -218,7 +243,8 @@ class AbstractItemInspector : public AbstractInspector { return { .action = ItemActionEnum::NONE, - .inventorySlot = 0, + .bagSlot = 0, + .containerSlot = 0, .equipmentSlot = 0 }; } @@ -232,7 +258,8 @@ class AbstractItemInspector : public AbstractInspector { return { .action = ItemActionEnum::DESTROY, - .inventorySlot = this->getItemInventorySlot(), + .bagSlot = this->getBagSlot(), + .containerSlot = 0, .equipmentSlot = 0 }; } @@ -244,13 +271,15 @@ class AbstractItemInspector : public AbstractInspector if (sellable) return { .action = ItemActionEnum::SELL, - .inventorySlot = this->getItemInventorySlot(), + .bagSlot = this->getBagSlot(), + .containerSlot = 0, .equipmentSlot = 0 }; return { .action = ItemActionEnum::DESTROY, - .inventorySlot = this->getItemInventorySlot(), + .bagSlot = this->getBagSlot(), + .containerSlot = 0, .equipmentSlot = 0 }; } diff --git a/src/domain/item/inspector/abstract/definition/struct/ItemActionStruct.h b/src/domain/item/inspector/abstract/definition/struct/ItemActionStruct.h index c26b7ab0570..a9486d226c4 100644 --- a/src/domain/item/inspector/abstract/definition/struct/ItemActionStruct.h +++ b/src/domain/item/inspector/abstract/definition/struct/ItemActionStruct.h @@ -7,6 +7,7 @@ struct ItemActionStruct { const ItemActionEnum action; - const uint8_t inventorySlot; + const uint8_t bagSlot; + const uint8_t containerSlot; const uint32_t equipmentSlot; }; diff --git a/src/domain/item/inspector/armor/ArmorItemInspector.h b/src/domain/item/inspector/armor/ArmorItemInspector.h index 051fb4dc3fa..07bb6538a57 100644 --- a/src/domain/item/inspector/armor/ArmorItemInspector.h +++ b/src/domain/item/inspector/armor/ArmorItemInspector.h @@ -29,7 +29,11 @@ class ArmorItemInspector : public AbstractItemInspector ItemActionStruct determineItemAction() const override { if (!this->isInspectable()) + { + LOG_ERROR("playerbots", "Item is not inspectable"); + return this->getDefaultItemAction(); + } if (this->isForbiddenItem()) return this->getForbiddenItemAction(); @@ -38,36 +42,42 @@ class ArmorItemInspector : public AbstractItemInspector Player* player = ObjectAccessor::FindPlayer(playerGUID); if (player == nullptr) + { + LOG_ERROR("playerbots", "player nullptr"); + return this->getDefaultItemAction(); + } Item* const item = this->getMutableCurrentItem(); if (item == nullptr) + { + LOG_ERROR("playerbots", "item nullptr"); + return this->getDefaultItemAction(); + } const bool canUseItem = player->CanUseItem(item); if (!canUseItem) - return this->getDefaultItemAction(); - - player = ObjectAccessor::FindPlayer(playerGUID); + { + LOG_ERROR("playerbots", "player can't use item"); - if (player == nullptr) return this->getDefaultItemAction(); + } - StatsWeightCalculator statisticsWeightCalculator = StatsWeightCalculator(player); + StatsWeightCalculator statisticsWeightCalculator(player); const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); if (itemTemplate == nullptr) - return this->getDefaultItemAction(); - - std::vector slots = InventoryService::GetInstance().getItemEquipmentSlots(itemTemplate); - const ItemTemplate* const refreshedItemTemplate = this->getCurrentItemTemplate(); + { + LOG_ERROR("playerbots", "item template nullptr"); - if (refreshedItemTemplate == nullptr) return this->getDefaultItemAction(); + } - const float newItemStatisticsWeight = statisticsWeightCalculator.CalculateItem(refreshedItemTemplate->ItemId); + std::vector slots = InventoryService::GetInstance().getItemEquipmentSlots(itemTemplate); + const float newItemStatisticsWeight = statisticsWeightCalculator.CalculateItem(itemTemplate->ItemId); statisticsWeightCalculator.Reset(); @@ -79,19 +89,24 @@ class ArmorItemInspector : public AbstractItemInspector if (player == nullptr) return this->getDefaultItemAction(); - const Item* const currentlyEquippedItem = player->GetItemByPos(equipmentSlot); + const Item* const currentlyEquippedItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, equipmentSlot); if (currentlyEquippedItem == nullptr) return { .action = ItemActionEnum::EQUIP, - .inventorySlot = this->getItemInventorySlot(), + .bagSlot = this->getBagSlot(), + .containerSlot = this->getItemSlot(), .equipmentSlot = equipmentSlot }; const ItemTemplate* const currentlyEquippedItemTemplate = currentlyEquippedItem->GetTemplate(); if (currentlyEquippedItemTemplate == nullptr) + { + LOG_ERROR("playerbots", "current item template nullptr"); + return this->getDefaultItemAction(); + } const float existingItemStatisticsWeight = statisticsWeightCalculator.CalculateItem(currentlyEquippedItemTemplate->ItemId); @@ -99,7 +114,8 @@ class ArmorItemInspector : public AbstractItemInspector { return { .action = ItemActionEnum::EQUIP, - .inventorySlot = this->getItemInventorySlot(), + .bagSlot = this->getBagSlot(), + .containerSlot = this->getItemSlot(), .equipmentSlot = equipmentSlot }; } diff --git a/src/domain/item/inspector/consumable/elixir/ConsumableElixirInspector.h b/src/domain/item/inspector/consumable/elixir/ConsumableElixirInspector.h index 4ffd23ae612..57395e764d0 100644 --- a/src/domain/item/inspector/consumable/elixir/ConsumableElixirInspector.h +++ b/src/domain/item/inspector/consumable/elixir/ConsumableElixirInspector.h @@ -55,7 +55,8 @@ class ConsumableElixirInspector : public AbstractConsumableInspector if (totalQuantity < maxStackSize) return { .action = ItemActionEnum::NONE, - .inventorySlot = 0, + .bagSlot = 0, + .containerSlot = 0, .equipmentSlot = 0 }; diff --git a/src/domain/item/inspector/consumable/potion/ConsumablePotionInspector.h b/src/domain/item/inspector/consumable/potion/ConsumablePotionInspector.h index 84362ec6d3f..09adab49a0c 100644 --- a/src/domain/item/inspector/consumable/potion/ConsumablePotionInspector.h +++ b/src/domain/item/inspector/consumable/potion/ConsumablePotionInspector.h @@ -51,7 +51,8 @@ class ConsumablePotionInspector : public AbstractConsumableInspector if (totalQuantity < maxStackSize) return { .action = ItemActionEnum::NONE, - .inventorySlot = 0, + .bagSlot = 0, + .containerSlot = 0, .equipmentSlot = 0 }; diff --git a/src/domain/item/inspector/container/ContainerInspector.h b/src/domain/item/inspector/container/ContainerInspector.h index d1b78bdbbff..1fd7b4bae07 100644 --- a/src/domain/item/inspector/container/ContainerInspector.h +++ b/src/domain/item/inspector/container/ContainerInspector.h @@ -39,13 +39,9 @@ class ContainerInspector : public AbstractItemInspector if (itemTemplate == nullptr) return this->getDestroyItemAction(); - const ObjectGuid playerGUID = ObjectGuid::Create(this->playerLowGUID); - Player* player = ObjectAccessor::FindPlayer(playerGUID); + const GlobalPlayerInspector playerInspector(this->playerLowGUID); - if (player == nullptr) - return this->getDefaultItemAction(); - - const uint8_t playerClass = player->getClass(); + const uint8_t playerClass = playerInspector.getCurrentPlayerClass(); const uint32_t inventoryType = itemTemplate->InventoryType; // @TODO: Add hunter quiver equipment decision (NOT process) here. @@ -66,18 +62,13 @@ class ContainerInspector : public AbstractItemInspector for (uint8_t i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) { - const ObjectGuid playerGUID = ObjectGuid::Create(this->playerLowGUID); - Player* player = ObjectAccessor::FindPlayer(playerGUID); - - if (player == nullptr) - return this->getDefaultItemAction(); - - const Item* const equippedItem = player->GetItemByPos(i); + const Item* const equippedItem = playerInspector.getItemByPosition(INVENTORY_SLOT_BAG_0, i); if (equippedItem == nullptr) return { .action = ItemActionEnum::EQUIP, - .inventorySlot = this->getItemInventorySlot(), + .bagSlot = this->getBagSlot(), + .containerSlot = this->getItemSlot(), .equipmentSlot = i }; @@ -91,10 +82,10 @@ class ContainerInspector : public AbstractItemInspector if (equippedBagSize < bagSize) return { .action = ItemActionEnum::EQUIP, - .inventorySlot = this->getItemInventorySlot(), + .bagSlot = this->getBagSlot(), + .containerSlot = this->getItemSlot(), .equipmentSlot = i }; - } return this->getSellAction(); diff --git a/src/domain/item/inspector/weapon/WeaponItemInspector.h b/src/domain/item/inspector/weapon/WeaponItemInspector.h index 44c2e9d7012..a1879440608 100644 --- a/src/domain/item/inspector/weapon/WeaponItemInspector.h +++ b/src/domain/item/inspector/weapon/WeaponItemInspector.h @@ -59,12 +59,8 @@ class WeaponItemInspector : public AbstractItemInspector return this->getDefaultItemAction(); std::vector slots = InventoryService::GetInstance().getItemEquipmentSlots(itemTemplate); - const ItemTemplate* const refreshedItemTemplate = this->getCurrentItemTemplate(); - if (refreshedItemTemplate == nullptr) - return this->getDefaultItemAction(); - - const float newItemStatisticsWeight = statisticsWeightCalculator.CalculateItem(refreshedItemTemplate->ItemId); + const float newItemStatisticsWeight = statisticsWeightCalculator.CalculateItem(itemTemplate->ItemId); statisticsWeightCalculator.Reset(); @@ -76,12 +72,13 @@ class WeaponItemInspector : public AbstractItemInspector if (player == nullptr) return this->getDefaultItemAction(); - const Item* const currentlyEquippedItem = player->GetItemByPos(equipmentSlot); + const Item* const currentlyEquippedItem = player->GetItemByPos(this->getBagSlot(), equipmentSlot); if (currentlyEquippedItem == nullptr) return { .action = ItemActionEnum::EQUIP, - .inventorySlot = this->getItemInventorySlot(), + .bagSlot = this->getBagSlot(), + .containerSlot = this->getItemSlot(), .equipmentSlot = equipmentSlot }; @@ -96,7 +93,8 @@ class WeaponItemInspector : public AbstractItemInspector { return { .action = ItemActionEnum::EQUIP, - .inventorySlot = this->getItemInventorySlot(), + .bagSlot = this->getBagSlot(), + .containerSlot = this->getItemSlot(), .equipmentSlot = equipmentSlot }; } diff --git a/src/domain/player/facade/inventory/PlayerInventoryFacade.h b/src/domain/player/facade/inventory/PlayerInventoryFacade.h index d3472cd5068..ac7747892bb 100644 --- a/src/domain/player/facade/inventory/PlayerInventoryFacade.h +++ b/src/domain/player/facade/inventory/PlayerInventoryFacade.h @@ -10,6 +10,9 @@ #include "SharedDefines.h" #include "Player.h" #include "WorldSession.h" +#include "SharedDefines.h" +#include "DBCStructure.h" +#include "AuctionHouseMgr.h" #include "AbstractPlayerFacade.h" #include "PlayerInventoryFacadeResultEnum.h" @@ -26,11 +29,49 @@ class PlayerInventoryFacade : public AbstractPlayerFacade AbstractPlayerFacade(playerGUID) {} - PlayerInventoryFacadeResultEnum destroyItem(uint64_t itemLowGUID, EquipmentSlots slot) + PlayerInventoryFacadeResultEnum equipItem(uint64_t itemLowGUID, uint8_t equipmentSlot) { - if (slot < INVENTORY_SLOT_BAG_START || slot > INVENTORY_SLOT_ITEM_END) + if (equipmentSlot < EQUIPMENT_SLOT_START || equipmentSlot > EQUIPMENT_SLOT_TABARD) return PlayerInventoryFacadeResultEnum::IMPOSSIBLE; + GlobalItemInspector itemInspector(this->playerGUID, itemLowGUID); + + const uint8_t inventoryType = itemInspector.getCurrentItemInventoryType(); + + if (inventoryType == INVTYPE_AMMO) + { + Player* const player = this->getCurrentPlayer(); + + if (player == nullptr) + return PlayerInventoryFacadeResultEnum::IMPOSSIBLE; + + player->SetAmmo(itemLowGUID); + + return PlayerInventoryFacadeResultEnum::OK; + } + + const uint32_t itemClass = itemInspector.getCurrentItemClass(); + + if (itemClass == ITEM_CLASS_WEAPON || itemClass == ITEM_CLASS_ARMOR || itemClass == ITEM_CLASS_CONTAINER) + { + Player* const player = this->getCurrentPlayer(); + + if (player == nullptr) + return PlayerInventoryFacadeResultEnum::IMPOSSIBLE; + + const uint16_t overlyCleverSrc = (itemInspector.getBagSlot() << 8) | itemInspector.getItemSlot(); + const uint16_t overlyCleverDest = (INVENTORY_SLOT_BAG_0 << 8) | equipmentSlot; + + player->SwapItem(overlyCleverSrc, overlyCleverDest); + + return PlayerInventoryFacadeResultEnum::OK; + } + + return PlayerInventoryFacadeResultEnum::IMPOSSIBLE; + } + + PlayerInventoryFacadeResultEnum destroyItem(uint64_t itemLowGUID) + { GlobalPlayerInspector playerInspector(this->playerGUID); GlobalItemInspector itemInspector(this->playerGUID, itemLowGUID); Player* const player = this->getCurrentPlayer(); @@ -38,8 +79,72 @@ class PlayerInventoryFacade : public AbstractPlayerFacade if (player == nullptr) return PlayerInventoryFacadeResultEnum::IMPOSSIBLE; - player->DestroyItem(itemInspector.getItemInventorySlot(), itemInspector.getItemSlot(), true); + Item* item = itemInspector.getMutableCurrentItem(); + + if (item == nullptr) + return PlayerInventoryFacadeResultEnum::IMPOSSIBLE; + + LOG_ERROR("playerbots", "destroying item"); + + uint32_t itemCount = itemInspector.getItemCurrentCount(); + + player->DestroyItemCount(item, itemCount, true); return PlayerInventoryFacadeResultEnum::OK; } + + PlayerInventoryFacadeResultEnum sellItem(uint64_t itemLowGUID) + { + LOG_ERROR("playerbots", "Selling item {}", std::to_string(itemLowGUID)); + + GlobalPlayerInspector playerInspector(this->playerGUID); + GlobalItemInspector itemInspector(this->playerGUID, itemLowGUID); + + const uint32_t sellPrice = itemInspector.getItemSellPrice(); + + LOG_ERROR("playerbots", "Item sell price {}", std::to_string(sellPrice)); + + if (sellPrice == 0) + return this->destroyItem(itemLowGUID); + + const uint32_t itemCount = itemInspector.getItemCurrentCount(); + + LOG_ERROR("playerbots", "Item count {}", std::to_string(itemCount)); + + Player* const player = this->getCurrentPlayer(); + + if (player == nullptr) + return PlayerInventoryFacadeResultEnum::IMPOSSIBLE; + + Item* item = itemInspector.getMutableCurrentItem(); + + if (item == nullptr) + return PlayerInventoryFacadeResultEnum::IMPOSSIBLE; + + const uint32_t totalItemStackValue = sellPrice * itemCount; + + LOG_ERROR("playerbots", "Total item stack value {}", std::to_string(totalItemStackValue)); + + LOG_ERROR("playerbots", "modifiying player money"); + player->ModifyMoney(totalItemStackValue); + // LOG_ERROR("playerbots", "destroying item after sell"); + // player->DestroyItemCount(itemInspector.getBagSlot(), itemInspector.getItemSlot(), true); + + + LOG_ERROR("playerbots", "removing item"); + player->RemoveItem(item->GetBagSlot(), item->GetSlot(), true); + LOG_ERROR("playerbots", "removing item from update queue"); + item->RemoveFromUpdateQueueOf(player); + LOG_ERROR("playerbots", "adding item to buy back slot"); + player->AddItemToBuyBackSlot(item, totalItemStackValue); + LOG_ERROR("playerbots", "Done selling item"); + + return PlayerInventoryFacadeResultEnum::OK; + } + + PlayerInventoryFacadeResultEnum auctionItem(uint64_t itemLowGUID) + { + const AuctionHouseMgr* const auctionHouseManager = AuctionHouseMgr::instance(); + // const AuctionHouseEntry* const auctionHouseEntry = auctionHouseManager->GetAuctionHouseEntryFromFactionTemplate(); + } }; diff --git a/src/domain/player/facade/inventory/definition/enum/PlayerInventoryFacadeResultEnum.h b/src/domain/player/facade/inventory/definition/enum/PlayerInventoryFacadeResultEnum.h index 384e00d8e6a..d60187392ab 100644 --- a/src/domain/player/facade/inventory/definition/enum/PlayerInventoryFacadeResultEnum.h +++ b/src/domain/player/facade/inventory/definition/enum/PlayerInventoryFacadeResultEnum.h @@ -1,3 +1,5 @@ +#pragma once + enum class PlayerInventoryFacadeResultEnum { OK = 0, diff --git a/src/strategy/actions/player/inventory/ManageInventoryAction.cpp b/src/strategy/actions/player/inventory/ManageInventoryAction.cpp index 1f1c60c638b..bcc43bf6e5f 100644 --- a/src/strategy/actions/player/inventory/ManageInventoryAction.cpp +++ b/src/strategy/actions/player/inventory/ManageInventoryAction.cpp @@ -22,6 +22,8 @@ #include "Event.h" #include "RandomPlayerbotMgr.h" #include "ItemActionStruct.h" +#include "PlayerInventoryFacadeResultEnum.h" +#include "PlayerInventoryFacade.h" using InspectorFactory = std::function; using SubclassMap = std::unordered_map; @@ -35,6 +37,8 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing ARMOR inspector"); + return ArmorItemInspector(botGUID, itemGUID).determineItemAction(); } } @@ -48,6 +52,8 @@ static const ClassMap inspectorFactories = { ITEM_SUBCLASS_CONSUMABLE, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing CONSUMABLE inspector"); + return ConsumableConsumableInspector(botGUID, itemGUID).determineItemAction(); } }, @@ -55,6 +61,8 @@ static const ClassMap inspectorFactories = { ITEM_SUBCLASS_ELIXIR, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing ELIXIR inspector"); + return ConsumableElixirInspector(botGUID, itemGUID).determineItemAction(); } }, @@ -62,6 +70,8 @@ static const ClassMap inspectorFactories = { ITEM_SUBCLASS_POTION, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing POTION inspector"); + return ConsumablePotionInspector(botGUID, itemGUID).determineItemAction(); } } @@ -75,6 +85,8 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing CONTAINER inspector"); + return ContainerInspector(botGUID, itemGUID).determineItemAction(); } } @@ -87,6 +99,8 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing GEM inspector"); + return GemInspector(botGUID, itemGUID).determineItemAction(); } } @@ -99,6 +113,8 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing GENERIC inspector"); + return GenericItemInspector(botGUID, itemGUID).determineItemAction(); } } @@ -111,6 +127,8 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing GLYPH inspector"); + return GlyphInspector(botGUID, itemGUID).determineItemAction(); } } @@ -123,6 +141,8 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing KEY inspector"); + return KeyInspector(botGUID, itemGUID).determineItemAction(); } } @@ -135,6 +155,8 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing MISC inspector"); + return MiscellaneousItemInspector(botGUID, itemGUID).determineItemAction(); } } @@ -147,6 +169,8 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing MONEY inspector"); + return MoneyInspector(botGUID, itemGUID).determineItemAction(); } } @@ -159,6 +183,8 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing PERMANENT inspector"); + return PermanentItemInspector(botGUID, itemGUID).determineItemAction(); } } @@ -171,6 +197,8 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing PROJECTILE inspector"); + return ProjectItemInspector(botGUID, itemGUID).determineItemAction(); } } @@ -183,6 +211,8 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing QUEST inspector"); + return QuestItemInspector(botGUID, itemGUID).determineItemAction(); } } @@ -195,6 +225,8 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing QUIVER inspector"); + return QuiverItemInspector(botGUID, itemGUID).determineItemAction(); } } @@ -207,6 +239,8 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing REAGENT inspector"); + return ReagentItemInspector(botGUID, itemGUID).determineItemAction(); } } @@ -219,6 +253,8 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing RECIPE inspector"); + return RecipeItemInspector(botGUID, itemGUID).determineItemAction(); } } @@ -231,6 +267,8 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing TRADE GOODS inspector"); + return TradeGoodItemInspector(botGUID, itemGUID).determineItemAction(); } } @@ -243,6 +281,8 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { + LOG_ERROR("playerbots", "executing WEAPON inspector"); + return WeaponItemInspector(botGUID, itemGUID).determineItemAction(); } } @@ -254,7 +294,7 @@ bool ManageInventoryAction::Execute(Event) { LOG_ERROR("playerbots", "starting inventory management action"); - std::unordered_map itemActions = {}; + this->itemActions.clear(); if (this->bot == nullptr) { @@ -274,34 +314,12 @@ bool ManageInventoryAction::Execute(Event) LOG_ERROR("playerbots", "done processing bags for {}", botName); - if (this->botAI != nullptr) - { - const Player* const master = botAI->GetMaster(); - - LOG_ERROR("playerbots", "fetched master for {}", botName); - - if (master != nullptr) - { - const std::string masterName = master->GetName(); - - LOG_ERROR("playerbots", "telling master ({}) of bot {}", masterName, botName); - - std::ostringstream out; + PlayerInventoryFacade playerInventoryFacade(this->bot->GetGUID().GetRawValue()); - out << "I am done sorting my inventory."; - - if (this->botAI != nullptr) - { - this->botAI->TellMaster(out); - } - - LOG_ERROR("playerbots", "told master ({}) of bot {}", masterName, botName); - } - } + bool shouldEquipUpgrade = false; for (const auto& [itemGUID, action] : this->itemActions) { - std::string actionName = "UNKNOWN_ACTION"; switch (action.action) @@ -320,22 +338,90 @@ bool ManageInventoryAction::Execute(Event) break; } - LOG_ERROR("playerbots", "Item {} action: {}, inventorySlot: {}, equipmentSlot: {}", std::to_string(itemGUID), actionName, std::to_string(action.inventorySlot), std::to_string(action.equipmentSlot)); + LOG_ERROR("playerbots", "Item {} action: {}, bagSlot: {}, containerSlot: {}, equipmentSlot: {}", std::to_string(itemGUID), actionName, std::to_string(action.bagSlot), std::to_string(action.containerSlot), std::to_string(action.equipmentSlot)); + + switch (action.action) + { + case ItemActionEnum::EQUIP: + { + // LOG_ERROR("playerbots", "equipping item through facade"); + + // const PlayerInventoryFacadeResultEnum result = playerInventoryFacade.equipItem(itemGUID, action.equipmentSlot); + + // LOG_ERROR("playerbots", "Equipped item {}, result: {}", std::to_string(itemGUID), result); + + shouldEquipUpgrade = true; + + break; + } + case ItemActionEnum::SELL: + { + LOG_ERROR("playerbots", "selling item through facade"); + const PlayerInventoryFacadeResultEnum result = playerInventoryFacade.sellItem(itemGUID); + + LOG_ERROR("playerbots", "Sold item {}, result: {}", std::to_string(itemGUID), result); + + break; + } + case ItemActionEnum::DESTROY: + { + LOG_ERROR("playerbots", "destroying item through facade"); + const PlayerInventoryFacadeResultEnum result = playerInventoryFacade.destroyItem(itemGUID); + + LOG_ERROR("playerbots", "Destroyed item {}, result: {}", std::to_string(itemGUID), result); + + break; + } + } + } + + if (shouldEquipUpgrade && this->botAI != nullptr) + { + LOG_ERROR("playerbots", "Equipping upgrades"); + + this->botAI->DoSpecificAction("equip upgrades"); + + LOG_ERROR("playerbots", "Done equipping upgrades"); } LOG_ERROR("playerbots", "Done processing inventory"); + if (this->botAI != nullptr) + { + const Player* const master = botAI->GetMaster(); + + LOG_ERROR("playerbots", "fetched master for {}", botName); + + if (master != nullptr) + { + const std::string masterName = master->GetName(); + + LOG_ERROR("playerbots", "telling master ({}) of bot {}", masterName, botName); + + std::ostringstream out; + + out << "I am done sorting my inventory."; + + if (this->botAI != nullptr) + { + this->botAI->TellMaster(out); + } + + LOG_ERROR("playerbots", "told master ({}) of bot {}", masterName, botName); + } + } + return true; // if (this->bot != nullptr) // { - // bot->SaveToDB(false, false); + // this->bot->SaveToDB(false, false); // return true; // } // LOG_ERROR("playerbots", "ManageInventoryAction::Execute can't save, bot is nullptr"); - // return false; + // return true; } void ManageInventoryAction::iterateBags() @@ -380,14 +466,14 @@ void ManageInventoryAction::iterateBags() return; } - const Item* const possibleBag = playerInspector.getItemByPosition(INVENTORY_SLOT_BAG_0, slot); + // const Item* const possibleBag = playerInspector.getItemByPosition(INVENTORY_SLOT_BAG_0, slot); - if (possibleBag == nullptr) - continue; + // if (possibleBag == nullptr) + // continue; - const uint64_t bagLowGUID = possibleBag->GetGUID().GetRawValue(); + // const uint64_t bagLowGUID = possibleBag->GetGUID().GetRawValue(); - this->iterateBag(bagLowGUID); + this->iterateBag(slot); } if (this->bot == nullptr) @@ -400,10 +486,11 @@ void ManageInventoryAction::iterateBags() LOG_ERROR("playerbots", "done processing bags for {}", this->bot->GetName()); } -void ManageInventoryAction::iterateBag(const uint64_t bagGUID) +void ManageInventoryAction::iterateBag(const uint32_t bagSlot) { const GlobalPlayerInspector playerInspector(this->bot->GetGUID().GetRawValue()); - const Item* const possibleBag = playerInspector.getItemByGUID(bagGUID); + // const Item* const possibleBag = playerInspector.getItemByGUID(bagGUID); + const Item* const possibleBag = playerInspector.getItemByPosition(INVENTORY_SLOT_BAG_0, bagSlot); if (possibleBag == nullptr) return; @@ -430,7 +517,8 @@ void ManageInventoryAction::iterateBag(const uint64_t bagGUID) return; } - Item* item = bag->GetItemByPos(slot); + // Item* item = bag->GetItemByPos(slot); + Item* item = playerInspector.getItemByPosition(bagSlot, slot); if (item == nullptr) { @@ -532,6 +620,7 @@ void ManageInventoryAction::processItem(const uint64_t itemGUID) const InspectorFactory& factoryFunction = subclassIterator->second; ItemActionStruct action = factoryFunction(botGUID, itemGUID); + this->itemActions.insert({itemGUID, action}); std::string itemAction = "NONE"; @@ -558,6 +647,8 @@ void ManageInventoryAction::processItem(const uint64_t itemGUID) template void ManageInventoryAction::determineItemAction(const uint32_t botLowGUID, const uint64_t itemLowGUID) { + LOG_ERROR("playerbots", "Determining action for item {}", std::to_string(itemLowGUID)); + InspectorT inspector(botLowGUID, itemLowGUID); const ItemActionStruct action = inspector.determineItemAction(); this->itemActions.insert({itemLowGUID, action}); diff --git a/src/strategy/actions/player/inventory/ManageInventoryAction.h b/src/strategy/actions/player/inventory/ManageInventoryAction.h index 664496fab5d..cf15ca18c8f 100644 --- a/src/strategy/actions/player/inventory/ManageInventoryAction.h +++ b/src/strategy/actions/player/inventory/ManageInventoryAction.h @@ -94,7 +94,7 @@ class ManageInventoryAction : public Action std::unordered_map itemActions = {}; void iterateBags(); - void iterateBag(const uint64_t bagGUID); + void iterateBag(const uint32_t bagSlot); void processItem(const uint64_t itemGUID); template void determineItemAction(const uint32_t botLowGUID, const uint64_t itemLowGUID); From 5aee6871f29c0571fe750a8f430e8d7ef23cd48d Mon Sep 17 00:00:00 2001 From: SmashingQuasar Date: Fri, 16 Jan 2026 15:43:05 +0100 Subject: [PATCH 4/8] chore: Logs clean up after debugging. --- .../item/inspector/armor/ArmorItemInspector.h | 12 +- .../facade/inventory/PlayerInventoryFacade.h | 32 ++-- .../inventory/ManageInventoryAction.cpp | 147 ++++++++---------- .../player/inventory/ManageInventoryAction.h | 10 +- 4 files changed, 93 insertions(+), 108 deletions(-) diff --git a/src/domain/item/inspector/armor/ArmorItemInspector.h b/src/domain/item/inspector/armor/ArmorItemInspector.h index 07bb6538a57..7aeba431d7e 100644 --- a/src/domain/item/inspector/armor/ArmorItemInspector.h +++ b/src/domain/item/inspector/armor/ArmorItemInspector.h @@ -30,7 +30,7 @@ class ArmorItemInspector : public AbstractItemInspector { if (!this->isInspectable()) { - LOG_ERROR("playerbots", "Item is not inspectable"); + LOG_DEBUG("playerbots.armor.inspector", "Item is not inspectable"); return this->getDefaultItemAction(); } @@ -43,7 +43,7 @@ class ArmorItemInspector : public AbstractItemInspector if (player == nullptr) { - LOG_ERROR("playerbots", "player nullptr"); + LOG_DEBUG("playerbots.armor.inspector", "player nullptr"); return this->getDefaultItemAction(); } @@ -52,7 +52,7 @@ class ArmorItemInspector : public AbstractItemInspector if (item == nullptr) { - LOG_ERROR("playerbots", "item nullptr"); + LOG_DEBUG("playerbots.armor.inspector", "item nullptr"); return this->getDefaultItemAction(); } @@ -61,7 +61,7 @@ class ArmorItemInspector : public AbstractItemInspector if (!canUseItem) { - LOG_ERROR("playerbots", "player can't use item"); + LOG_DEBUG("playerbots.armor.inspector", "player can't use item"); return this->getDefaultItemAction(); } @@ -71,7 +71,7 @@ class ArmorItemInspector : public AbstractItemInspector if (itemTemplate == nullptr) { - LOG_ERROR("playerbots", "item template nullptr"); + LOG_DEBUG("playerbots.armor.inspector", "item template nullptr"); return this->getDefaultItemAction(); } @@ -103,7 +103,7 @@ class ArmorItemInspector : public AbstractItemInspector if (currentlyEquippedItemTemplate == nullptr) { - LOG_ERROR("playerbots", "current item template nullptr"); + LOG_DEBUG("playerbots.armor.inspector", "current item template nullptr"); return this->getDefaultItemAction(); } diff --git a/src/domain/player/facade/inventory/PlayerInventoryFacade.h b/src/domain/player/facade/inventory/PlayerInventoryFacade.h index ac7747892bb..f13329654dd 100644 --- a/src/domain/player/facade/inventory/PlayerInventoryFacade.h +++ b/src/domain/player/facade/inventory/PlayerInventoryFacade.h @@ -84,7 +84,7 @@ class PlayerInventoryFacade : public AbstractPlayerFacade if (item == nullptr) return PlayerInventoryFacadeResultEnum::IMPOSSIBLE; - LOG_ERROR("playerbots", "destroying item"); + LOG_DEBUG("playerbots.player.facade", "destroying item"); uint32_t itemCount = itemInspector.getItemCurrentCount(); @@ -95,21 +95,21 @@ class PlayerInventoryFacade : public AbstractPlayerFacade PlayerInventoryFacadeResultEnum sellItem(uint64_t itemLowGUID) { - LOG_ERROR("playerbots", "Selling item {}", std::to_string(itemLowGUID)); + LOG_DEBUG("playerbots.player.facade", "Selling item {}", std::to_string(itemLowGUID)); GlobalPlayerInspector playerInspector(this->playerGUID); GlobalItemInspector itemInspector(this->playerGUID, itemLowGUID); const uint32_t sellPrice = itemInspector.getItemSellPrice(); - LOG_ERROR("playerbots", "Item sell price {}", std::to_string(sellPrice)); + LOG_DEBUG("playerbots.player.facade", "Item sell price {}", std::to_string(sellPrice)); if (sellPrice == 0) return this->destroyItem(itemLowGUID); const uint32_t itemCount = itemInspector.getItemCurrentCount(); - LOG_ERROR("playerbots", "Item count {}", std::to_string(itemCount)); + LOG_DEBUG("playerbots.player.facade", "Item count {}", std::to_string(itemCount)); Player* const player = this->getCurrentPlayer(); @@ -123,28 +123,28 @@ class PlayerInventoryFacade : public AbstractPlayerFacade const uint32_t totalItemStackValue = sellPrice * itemCount; - LOG_ERROR("playerbots", "Total item stack value {}", std::to_string(totalItemStackValue)); + LOG_DEBUG("playerbots.player.facade", "Total item stack value {}", std::to_string(totalItemStackValue)); - LOG_ERROR("playerbots", "modifiying player money"); + LOG_DEBUG("playerbots.player.facade", "modifiying player money"); player->ModifyMoney(totalItemStackValue); - // LOG_ERROR("playerbots", "destroying item after sell"); + // LOG_DEBUG("playerbots.player.facade", "destroying item after sell"); // player->DestroyItemCount(itemInspector.getBagSlot(), itemInspector.getItemSlot(), true); - LOG_ERROR("playerbots", "removing item"); + LOG_DEBUG("playerbots.player.facade", "removing item"); player->RemoveItem(item->GetBagSlot(), item->GetSlot(), true); - LOG_ERROR("playerbots", "removing item from update queue"); + LOG_DEBUG("playerbots.player.facade", "removing item from update queue"); item->RemoveFromUpdateQueueOf(player); - LOG_ERROR("playerbots", "adding item to buy back slot"); + LOG_DEBUG("playerbots.player.facade", "adding item to buy back slot"); player->AddItemToBuyBackSlot(item, totalItemStackValue); - LOG_ERROR("playerbots", "Done selling item"); + LOG_DEBUG("playerbots.player.facade", "Done selling item"); return PlayerInventoryFacadeResultEnum::OK; } - PlayerInventoryFacadeResultEnum auctionItem(uint64_t itemLowGUID) - { - const AuctionHouseMgr* const auctionHouseManager = AuctionHouseMgr::instance(); - // const AuctionHouseEntry* const auctionHouseEntry = auctionHouseManager->GetAuctionHouseEntryFromFactionTemplate(); - } + // PlayerInventoryFacadeResultEnum auctionItem(uint64_t itemLowGUID) + // { + // const AuctionHouseMgr* const auctionHouseManager = AuctionHouseMgr::instance(); + // // const AuctionHouseEntry* const auctionHouseEntry = auctionHouseManager->GetAuctionHouseEntryFromFactionTemplate(); + // } }; diff --git a/src/strategy/actions/player/inventory/ManageInventoryAction.cpp b/src/strategy/actions/player/inventory/ManageInventoryAction.cpp index bcc43bf6e5f..b8d54045bfe 100644 --- a/src/strategy/actions/player/inventory/ManageInventoryAction.cpp +++ b/src/strategy/actions/player/inventory/ManageInventoryAction.cpp @@ -10,6 +10,8 @@ #include #include +#include "CreateNextAction.h" +#include "EquipAction.h" #include "ItemActionEnum.h" #include "Log.h" #include "Player.h" @@ -37,7 +39,7 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing ARMOR inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing ARMOR inspector"); return ArmorItemInspector(botGUID, itemGUID).determineItemAction(); } @@ -52,7 +54,7 @@ static const ClassMap inspectorFactories = { ITEM_SUBCLASS_CONSUMABLE, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing CONSUMABLE inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing CONSUMABLE inspector"); return ConsumableConsumableInspector(botGUID, itemGUID).determineItemAction(); } @@ -61,7 +63,7 @@ static const ClassMap inspectorFactories = { ITEM_SUBCLASS_ELIXIR, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing ELIXIR inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing ELIXIR inspector"); return ConsumableElixirInspector(botGUID, itemGUID).determineItemAction(); } @@ -70,7 +72,7 @@ static const ClassMap inspectorFactories = { ITEM_SUBCLASS_POTION, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing POTION inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing POTION inspector"); return ConsumablePotionInspector(botGUID, itemGUID).determineItemAction(); } @@ -85,7 +87,7 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing CONTAINER inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing CONTAINER inspector"); return ContainerInspector(botGUID, itemGUID).determineItemAction(); } @@ -99,7 +101,7 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing GEM inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing GEM inspector"); return GemInspector(botGUID, itemGUID).determineItemAction(); } @@ -113,7 +115,7 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing GENERIC inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing GENERIC inspector"); return GenericItemInspector(botGUID, itemGUID).determineItemAction(); } @@ -127,7 +129,7 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing GLYPH inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing GLYPH inspector"); return GlyphInspector(botGUID, itemGUID).determineItemAction(); } @@ -141,7 +143,7 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing KEY inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing KEY inspector"); return KeyInspector(botGUID, itemGUID).determineItemAction(); } @@ -155,7 +157,7 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing MISC inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing MISC inspector"); return MiscellaneousItemInspector(botGUID, itemGUID).determineItemAction(); } @@ -169,7 +171,7 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing MONEY inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing MONEY inspector"); return MoneyInspector(botGUID, itemGUID).determineItemAction(); } @@ -183,7 +185,7 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing PERMANENT inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing PERMANENT inspector"); return PermanentItemInspector(botGUID, itemGUID).determineItemAction(); } @@ -197,7 +199,7 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing PROJECTILE inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing PROJECTILE inspector"); return ProjectItemInspector(botGUID, itemGUID).determineItemAction(); } @@ -211,7 +213,7 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing QUEST inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing QUEST inspector"); return QuestItemInspector(botGUID, itemGUID).determineItemAction(); } @@ -225,7 +227,7 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing QUIVER inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing QUIVER inspector"); return QuiverItemInspector(botGUID, itemGUID).determineItemAction(); } @@ -239,7 +241,7 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing REAGENT inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing REAGENT inspector"); return ReagentItemInspector(botGUID, itemGUID).determineItemAction(); } @@ -253,7 +255,7 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing RECIPE inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing RECIPE inspector"); return RecipeItemInspector(botGUID, itemGUID).determineItemAction(); } @@ -267,7 +269,7 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing TRADE GOODS inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing TRADE GOODS inspector"); return TradeGoodItemInspector(botGUID, itemGUID).determineItemAction(); } @@ -281,7 +283,7 @@ static const ClassMap inspectorFactories = { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) { - LOG_ERROR("playerbots", "executing WEAPON inspector"); + LOG_DEBUG("playerbots.action.manage_inventory", "executing WEAPON inspector"); return WeaponItemInspector(botGUID, itemGUID).determineItemAction(); } @@ -292,27 +294,27 @@ static const ClassMap inspectorFactories = { bool ManageInventoryAction::Execute(Event) { - LOG_ERROR("playerbots", "starting inventory management action"); + LOG_DEBUG("playerbots.action.manage_inventory", "starting inventory management action"); this->itemActions.clear(); if (this->bot == nullptr) { - LOG_ERROR("playerbots", "ManageInventoryAction::Execute bot is nullptr"); + LOG_DEBUG("playerbots.action.manage_inventory", "ManageInventoryAction::Execute bot is nullptr"); return false; } const std::string botName = this->bot->GetName(); - LOG_ERROR("playerbots", "managing inventory for {}", botName); + LOG_DEBUG("playerbots.action.manage_inventory", "managing inventory for {}", botName); if (!RandomPlayerbotMgr::instance().IsRandomBot(bot)) return true; this->iterateBags(); - LOG_ERROR("playerbots", "done processing bags for {}", botName); + LOG_DEBUG("playerbots.action.manage_inventory", "done processing bags for {}", botName); PlayerInventoryFacade playerInventoryFacade(this->bot->GetGUID().GetRawValue()); @@ -338,17 +340,17 @@ bool ManageInventoryAction::Execute(Event) break; } - LOG_ERROR("playerbots", "Item {} action: {}, bagSlot: {}, containerSlot: {}, equipmentSlot: {}", std::to_string(itemGUID), actionName, std::to_string(action.bagSlot), std::to_string(action.containerSlot), std::to_string(action.equipmentSlot)); + LOG_DEBUG("playerbots.action.manage_inventory", "Item {} action: {}, bagSlot: {}, containerSlot: {}, equipmentSlot: {}", std::to_string(itemGUID), actionName, std::to_string(action.bagSlot), std::to_string(action.containerSlot), std::to_string(action.equipmentSlot)); switch (action.action) { case ItemActionEnum::EQUIP: { - // LOG_ERROR("playerbots", "equipping item through facade"); + // LOG_DEBUG("playerbots.action.manage_inventory", "equipping item through facade"); // const PlayerInventoryFacadeResultEnum result = playerInventoryFacade.equipItem(itemGUID, action.equipmentSlot); - // LOG_ERROR("playerbots", "Equipped item {}, result: {}", std::to_string(itemGUID), result); + // LOG_DEBUG("playerbots.action.manage_inventory", "Equipped item {}, result: {}", std::to_string(itemGUID), result); shouldEquipUpgrade = true; @@ -356,47 +358,49 @@ bool ManageInventoryAction::Execute(Event) } case ItemActionEnum::SELL: { - LOG_ERROR("playerbots", "selling item through facade"); + LOG_DEBUG("playerbots.action.manage_inventory", "selling item through facade"); const PlayerInventoryFacadeResultEnum result = playerInventoryFacade.sellItem(itemGUID); - LOG_ERROR("playerbots", "Sold item {}, result: {}", std::to_string(itemGUID), result); + LOG_DEBUG("playerbots.action.manage_inventory", "Sold item {}, result: {}", std::to_string(itemGUID), result); break; } case ItemActionEnum::DESTROY: { - LOG_ERROR("playerbots", "destroying item through facade"); + LOG_DEBUG("playerbots.action.manage_inventory", "destroying item through facade"); const PlayerInventoryFacadeResultEnum result = playerInventoryFacade.destroyItem(itemGUID); - LOG_ERROR("playerbots", "Destroyed item {}, result: {}", std::to_string(itemGUID), result); + LOG_DEBUG("playerbots.action.manage_inventory", "Destroyed item {}, result: {}", std::to_string(itemGUID), result); break; } + case ItemActionEnum::NONE: + break; } } if (shouldEquipUpgrade && this->botAI != nullptr) { - LOG_ERROR("playerbots", "Equipping upgrades"); + LOG_DEBUG("playerbots.action.manage_inventory", "Equipping upgrades"); - this->botAI->DoSpecificAction("equip upgrades"); + this->botAI->DoSpecificAction(CreateNextAction(1.0f).factory); - LOG_ERROR("playerbots", "Done equipping upgrades"); + LOG_DEBUG("playerbots.action.manage_inventory", "Done equipping upgrades"); } - LOG_ERROR("playerbots", "Done processing inventory"); + LOG_DEBUG("playerbots.action.manage_inventory", "Done processing inventory"); if (this->botAI != nullptr) { const Player* const master = botAI->GetMaster(); - LOG_ERROR("playerbots", "fetched master for {}", botName); + LOG_DEBUG("playerbots.action.manage_inventory", "fetched master for {}", botName); if (master != nullptr) { const std::string masterName = master->GetName(); - LOG_ERROR("playerbots", "telling master ({}) of bot {}", masterName, botName); + LOG_DEBUG("playerbots.action.manage_inventory", "telling master ({}) of bot {}", masterName, botName); std::ostringstream out; @@ -407,7 +411,7 @@ bool ManageInventoryAction::Execute(Event) this->botAI->TellMaster(out); } - LOG_ERROR("playerbots", "told master ({}) of bot {}", masterName, botName); + LOG_DEBUG("playerbots.action.manage_inventory", "told master ({}) of bot {}", masterName, botName); } } @@ -419,18 +423,18 @@ bool ManageInventoryAction::Execute(Event) // return true; // } - // LOG_ERROR("playerbots", "ManageInventoryAction::Execute can't save, bot is nullptr"); + // LOG_DEBUG("playerbots.action.manage_inventory", "ManageInventoryAction::Execute can't save, bot is nullptr"); // return true; } void ManageInventoryAction::iterateBags() { - LOG_ERROR("playerbots", "Processing main bag"); + LOG_DEBUG("playerbots.action.manage_inventory", "Processing main bag"); if (this->bot == nullptr) { - LOG_ERROR("playerbots", "ManageInventoryAction::iterateBags bot became nullptr"); + LOG_DEBUG("playerbots.action.manage_inventory", "ManageInventoryAction::iterateBags bot became nullptr"); return; } @@ -439,13 +443,13 @@ void ManageInventoryAction::iterateBags() for (uint8_t slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++) { - LOG_ERROR("playerbots", "Processing item in main bag slot {}", std::to_string(slot)); + LOG_DEBUG("playerbots.action.manage_inventory", "Processing item in main bag slot {}", std::to_string(slot)); Item* item = playerInspector.getItemByPosition(INVENTORY_SLOT_BAG_0, slot); if (item == nullptr) { - LOG_ERROR("playerbots", "Item in main bag slot {} did not exist", std::to_string(slot)); + LOG_DEBUG("playerbots.action.manage_inventory", "Item in main bag slot {} did not exist", std::to_string(slot)); continue; } @@ -457,11 +461,11 @@ void ManageInventoryAction::iterateBags() for (uint8_t slot = INVENTORY_SLOT_BAG_START; slot < INVENTORY_SLOT_BAG_END; ++slot) { - LOG_ERROR("playerbots", "Processing bag in slot {}", std::to_string(slot)); + LOG_DEBUG("playerbots.action.manage_inventory", "Processing bag in slot {}", std::to_string(slot)); if (this->bot == nullptr) { - LOG_ERROR("playerbots", "ManageInventoryAction::iterateBags bot became nullptr"); + LOG_DEBUG("playerbots.action.manage_inventory", "ManageInventoryAction::iterateBags bot became nullptr"); return; } @@ -478,12 +482,12 @@ void ManageInventoryAction::iterateBags() if (this->bot == nullptr) { - LOG_ERROR("playerbots", "ManageInventoryAction::iterateBags bot became nullptr"); + LOG_DEBUG("playerbots.action.manage_inventory", "ManageInventoryAction::iterateBags bot became nullptr"); return; } - LOG_ERROR("playerbots", "done processing bags for {}", this->bot->GetName()); + LOG_DEBUG("playerbots.action.manage_inventory", "done processing bags for {}", this->bot->GetName()); } void ManageInventoryAction::iterateBag(const uint32_t bagSlot) @@ -499,7 +503,7 @@ void ManageInventoryAction::iterateBag(const uint32_t bagSlot) if (bag == nullptr) { - LOG_ERROR("playerbots", "Bag did not exist"); + LOG_DEBUG("playerbots.action.manage_inventory", "Bag did not exist"); return; } @@ -508,11 +512,11 @@ void ManageInventoryAction::iterateBag(const uint32_t bagSlot) for (uint8_t slot = 0; slot < size; ++slot) { - LOG_ERROR("playerbots", "Processing item {} in bag", std::to_string(slot)); + LOG_DEBUG("playerbots.action.manage_inventory", "Processing item {} in bag", std::to_string(slot)); if (bag == nullptr) { - LOG_ERROR("playerbots", "ManageInventoryAction::iterateBag bag became nullptr"); + LOG_DEBUG("playerbots.action.manage_inventory", "ManageInventoryAction::iterateBag bag became nullptr"); return; } @@ -522,7 +526,7 @@ void ManageInventoryAction::iterateBag(const uint32_t bagSlot) if (item == nullptr) { - LOG_ERROR("playerbots", "Item {} in bag did not exist", std::to_string(slot)); + LOG_DEBUG("playerbots.action.manage_inventory", "Item {} in bag did not exist", std::to_string(slot)); continue; } @@ -532,14 +536,14 @@ void ManageInventoryAction::iterateBag(const uint32_t bagSlot) this->processItem(itemGUID); } - LOG_ERROR("playerbots", "done processing bag {}", std::to_string(bag->GetSlot())); + LOG_DEBUG("playerbots.action.manage_inventory", "done processing bag {}", std::to_string(bag->GetSlot())); } void ManageInventoryAction::processItem(const uint64_t itemGUID) { if (this->bot == nullptr) { - LOG_ERROR("playerbots", "ManageInventoryAction::processItem bot became nullptr"); + LOG_DEBUG("playerbots.action.manage_inventory", "ManageInventoryAction::processItem bot became nullptr"); return; } @@ -552,21 +556,21 @@ void ManageInventoryAction::processItem(const uint64_t itemGUID) if (itemState == ITEM_REMOVED) { - LOG_ERROR("playerbots", "Item {} (template {}) was already flagged for removal.", itemGUID, itemTemplateLowGUID); + LOG_DEBUG("playerbots.action.manage_inventory", "Item {} (template {}) was already flagged for removal.", itemGUID, itemTemplateLowGUID); return; } if (itemState == ITEM_CHANGED) { - LOG_ERROR("playerbots", "Item {} (template {}) was already modified.", itemGUID, itemTemplateLowGUID); + LOG_DEBUG("playerbots.action.manage_inventory", "Item {} (template {}) was already modified.", itemGUID, itemTemplateLowGUID); return; } if (!itemInspector.itemIsInWorld()) { - LOG_ERROR("playerbots", "Item {} (template {}) is not in world.", itemGUID, itemTemplateLowGUID); + LOG_DEBUG("playerbots.action.manage_inventory", "Item {} (template {}) is not in world.", itemGUID, itemTemplateLowGUID); return; } @@ -575,14 +579,14 @@ void ManageInventoryAction::processItem(const uint64_t itemGUID) if (itemSlot == NULL_SLOT) { - LOG_ERROR("playerbots", "Item {} (template {}) is null slot.", itemGUID, itemTemplateLowGUID); + LOG_DEBUG("playerbots.action.manage_inventory", "Item {} (template {}) is null slot.", itemGUID, itemTemplateLowGUID); return; } if (itemInspector.itemIsInUnsafeContainer()) { - LOG_ERROR("playerbots", "Item {} (template {}) is in unsafe container", itemGUID, itemTemplateLowGUID); + LOG_DEBUG("playerbots.action.manage_inventory", "Item {} (template {}) is in unsafe container", itemGUID, itemTemplateLowGUID); return; } @@ -590,13 +594,13 @@ void ManageInventoryAction::processItem(const uint64_t itemGUID) const uint32_t itemClass = itemInspector.getCurrentItemClass(); const uint32_t itemSubClass = itemInspector.getCurrentItemSubclass(); - LOG_ERROR("playerbots", "processing item {}", std::to_string(itemTemplateLowGUID)); + LOG_DEBUG("playerbots.action.manage_inventory", "processing item {}", std::to_string(itemTemplateLowGUID)); ClassMap::const_iterator classIterator = inspectorFactories.find(itemClass); if (classIterator == inspectorFactories.end()) { - LOG_ERROR("playerbots", "Unable to locate service for item class {} item service", std::to_string(itemClass)); + LOG_DEBUG("playerbots.action.manage_inventory", "Unable to locate service for item class {} item service", std::to_string(itemClass)); return; } @@ -612,7 +616,7 @@ void ManageInventoryAction::processItem(const uint64_t itemGUID) if (subclassIterator == subclassMap.end()) { - LOG_ERROR("playerbots", "Unable to locate service for item class {} subclass {} item service", std::to_string(itemClass), std::to_string(itemSubClass)); + LOG_DEBUG("playerbots.action.manage_inventory", "Unable to locate service for item class {} subclass {} item service", std::to_string(itemClass), std::to_string(itemSubClass)); return; } @@ -622,32 +626,13 @@ void ManageInventoryAction::processItem(const uint64_t itemGUID) ItemActionStruct action = factoryFunction(botGUID, itemGUID); this->itemActions.insert({itemGUID, action}); - std::string itemAction = "NONE"; - - if (action.action == ItemActionEnum::DESTROY) - { - itemAction = "DESTROY"; - } - - if (action.action == ItemActionEnum::EQUIP) - { - itemAction = "EQUIP"; - } - - if (action.action == ItemActionEnum::SELL) - { - itemAction = "SELL"; - } - - LOG_ERROR("playerbots", "Determined action {} for item {}", itemAction, std::to_string(itemTemplateLowGUID)); - - LOG_ERROR("playerbots", "Done visiting item {}", std::to_string(itemTemplateLowGUID)); + LOG_DEBUG("playerbots.action.manage_inventory", "Done visiting item {}", std::to_string(itemTemplateLowGUID)); } template void ManageInventoryAction::determineItemAction(const uint32_t botLowGUID, const uint64_t itemLowGUID) { - LOG_ERROR("playerbots", "Determining action for item {}", std::to_string(itemLowGUID)); + LOG_DEBUG("playerbots.action.manage_inventory", "Determining action for item {}", std::to_string(itemLowGUID)); InspectorT inspector(botLowGUID, itemLowGUID); const ItemActionStruct action = inspector.determineItemAction(); diff --git a/src/strategy/actions/player/inventory/ManageInventoryAction.h b/src/strategy/actions/player/inventory/ManageInventoryAction.h index cf15ca18c8f..0a88cf2f016 100644 --- a/src/strategy/actions/player/inventory/ManageInventoryAction.h +++ b/src/strategy/actions/player/inventory/ManageInventoryAction.h @@ -48,13 +48,13 @@ class ManageInventoryAction : public Action bool isPossible() override { - LOG_ERROR("playerbots", "Starting manage inventory isPossible evaluation"); + LOG_DEBUG("playerbots.action.manage_inventory", "Starting manage inventory isPossible evaluation"); const Player* const bot = botAI->GetBot(); if (bot == nullptr) { - LOG_ERROR("playerbots", "Manage inventory impossible bot is nullptr"); + LOG_DEBUG("playerbots.action.manage_inventory", "Manage inventory impossible bot is nullptr"); return false; } @@ -63,7 +63,7 @@ class ManageInventoryAction : public Action if (!bot->IsInWorld()) { - LOG_ERROR("playerbots", "Manage inventory impossible bot '{}' is non in world", botName); + LOG_DEBUG("playerbots.action.manage_inventory", "Manage inventory impossible bot '{}' is non in world", botName); return false; } @@ -72,14 +72,14 @@ class ManageInventoryAction : public Action if (session == nullptr) { - LOG_ERROR("playerbots", "Manage inventory impossible bot '{}' session is nullptr", botName); + LOG_DEBUG("playerbots.action.manage_inventory", "Manage inventory impossible bot '{}' session is nullptr", botName); return false; } if (session->isLogingOut()) { - LOG_ERROR("playerbots", "Manage inventory impossible bot '{}' is logging out", botName); + LOG_DEBUG("playerbots.action.manage_inventory", "Manage inventory impossible bot '{}' is logging out", botName); return false; } From 3de6aea323c7c6f299a02a1a9d1118579140c21e Mon Sep 17 00:00:00 2001 From: SmashingQuasar Date: Tue, 27 Jan 2026 16:36:02 +0100 Subject: [PATCH 5/8] fix: Resolved an issue with the inventory management system where an enum was wrongfully evaluated as a boolean. --- .../item/inspector/armor/ArmorItemInspector.h | 18 +-- .../consumable/GlobalConsumableInspector.h | 55 +++++++ .../inspector/weapon/WeaponItemInspector.h | 42 ++++-- .../inventory/ManageInventoryAction.cpp | 136 +++++++++++++++++- .../player/inventory/ManageInventoryAction.h | 27 +--- 5 files changed, 229 insertions(+), 49 deletions(-) create mode 100644 src/domain/item/inspector/consumable/GlobalConsumableInspector.h diff --git a/src/domain/item/inspector/armor/ArmorItemInspector.h b/src/domain/item/inspector/armor/ArmorItemInspector.h index 7aeba431d7e..544a5f58145 100644 --- a/src/domain/item/inspector/armor/ArmorItemInspector.h +++ b/src/domain/item/inspector/armor/ArmorItemInspector.h @@ -2,6 +2,7 @@ #include +#include "Item.h" #include "ItemTemplate.h" #include "StatsWeightCalculator.h" @@ -57,13 +58,13 @@ class ArmorItemInspector : public AbstractItemInspector return this->getDefaultItemAction(); } - const bool canUseItem = player->CanUseItem(item); + const InventoryResult canUseItem = player->CanUseItem(item); - if (!canUseItem) + if (canUseItem != EQUIP_ERR_OK) { LOG_DEBUG("playerbots.armor.inspector", "player can't use item"); - return this->getDefaultItemAction(); + return this->getSellAction(); } StatsWeightCalculator statisticsWeightCalculator(player); @@ -84,20 +85,19 @@ class ArmorItemInspector : public AbstractItemInspector for (uint8_t i = 0; i < slots.size(); ++i) { const uint32_t equipmentSlot = slots[i]; - player = ObjectAccessor::FindPlayer(playerGUID); - - if (player == nullptr) - return this->getDefaultItemAction(); - const Item* const currentlyEquippedItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, equipmentSlot); if (currentlyEquippedItem == nullptr) + { + LOG_DEBUG("playerbots.armor.inspector", "current item is nullptr"); + return { .action = ItemActionEnum::EQUIP, .bagSlot = this->getBagSlot(), .containerSlot = this->getItemSlot(), .equipmentSlot = equipmentSlot }; + } const ItemTemplate* const currentlyEquippedItemTemplate = currentlyEquippedItem->GetTemplate(); @@ -112,6 +112,8 @@ class ArmorItemInspector : public AbstractItemInspector if (existingItemStatisticsWeight < newItemStatisticsWeight) { + LOG_DEBUG("playerbots.armor.inspector", "Current item is worse than item"); + return { .action = ItemActionEnum::EQUIP, .bagSlot = this->getBagSlot(), diff --git a/src/domain/item/inspector/consumable/GlobalConsumableInspector.h b/src/domain/item/inspector/consumable/GlobalConsumableInspector.h new file mode 100644 index 00000000000..dd0ff972894 --- /dev/null +++ b/src/domain/item/inspector/consumable/GlobalConsumableInspector.h @@ -0,0 +1,55 @@ +#pragma once + +#include + +#include "ItemTemplate.h" + +#include "AbstractConsumableInspector.h" +#include "ItemActionStruct.h" + +class GlobalConsumableInspector : public AbstractConsumableInspector +{ +public: + GlobalConsumableInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractConsumableInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const + { + const uint8_t itemSubclass = this->getCurrentItemSubclass(); + + return AbstractConsumableInspector::isInspectable() && itemSubclass == ITEM_SUBCLASS_CONSUMABLE; + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + + ItemActionStruct determineItemAction() const + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + return this->getSellAction(); + } + +protected: + + std::unordered_set getForbiddenItemsGUIDs() const + { + return {}; + } + +}; diff --git a/src/domain/item/inspector/weapon/WeaponItemInspector.h b/src/domain/item/inspector/weapon/WeaponItemInspector.h index a1879440608..dd5b057261d 100644 --- a/src/domain/item/inspector/weapon/WeaponItemInspector.h +++ b/src/domain/item/inspector/weapon/WeaponItemInspector.h @@ -31,32 +31,56 @@ class WeaponItemInspector : public AbstractItemInspector ItemActionStruct determineItemAction() const override { if (!this->isInspectable()) + { + LOG_DEBUG("playerbots", "Item is not inspectable."); + return this->getDefaultItemAction(); + } if (this->isForbiddenItem()) + { + LOG_DEBUG("playerbots", "Item is forbidden"); + return this->getForbiddenItemAction(); + } const ObjectGuid playerGUID = ObjectGuid::Create(this->playerLowGUID); Player* player = ObjectAccessor::FindPlayer(playerGUID); if (player == nullptr) + { + LOG_DEBUG("playerbots", "Player is nullptr"); + return this->getDefaultItemAction(); + } Item* currentItem = this->getMutableCurrentItem(); if (currentItem == nullptr) + { + LOG_DEBUG("playerbots", "new item had no template"); + return this->getDefaultItemAction(); + } - const bool canUseItem = player->CanUseItem(currentItem); + const InventoryResult canUseItem = player->CanUseItem(currentItem); - if (!canUseItem) - return this->getDefaultItemAction(); + if (canUseItem != EQUIP_ERR_OK) + { + LOG_DEBUG("playerbots", "item could not be used, selling"); + + return this->getSellAction(); + } StatsWeightCalculator statisticsWeightCalculator(player); const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); if (itemTemplate == nullptr) + { + LOG_DEBUG("playerbots", "Item had no template"); + return this->getDefaultItemAction(); + } std::vector slots = InventoryService::GetInstance().getItemEquipmentSlots(itemTemplate); @@ -67,20 +91,20 @@ class WeaponItemInspector : public AbstractItemInspector for (uint8_t i = 0; i < slots.size(); ++i) { const uint32_t equipmentSlot = slots[i]; - player = ObjectAccessor::FindPlayer(playerGUID); - if (player == nullptr) - return this->getDefaultItemAction(); - - const Item* const currentlyEquippedItem = player->GetItemByPos(this->getBagSlot(), equipmentSlot); + const Item* const currentlyEquippedItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, equipmentSlot); if (currentlyEquippedItem == nullptr) + { + LOG_DEBUG("playerbots.inspector.weapon", "No current item, equipping new"); + return { .action = ItemActionEnum::EQUIP, .bagSlot = this->getBagSlot(), .containerSlot = this->getItemSlot(), .equipmentSlot = equipmentSlot }; + } const ItemTemplate* const currentlyEquippedItemTemplate = currentlyEquippedItem->GetTemplate(); @@ -91,6 +115,8 @@ class WeaponItemInspector : public AbstractItemInspector if (existingItemStatisticsWeight < newItemStatisticsWeight) { + LOG_DEBUG("playerbots.inspector.weapon", "New item is better than old one."); + return { .action = ItemActionEnum::EQUIP, .bagSlot = this->getBagSlot(), diff --git a/src/strategy/actions/player/inventory/ManageInventoryAction.cpp b/src/strategy/actions/player/inventory/ManageInventoryAction.cpp index b8d54045bfe..1977b144cd9 100644 --- a/src/strategy/actions/player/inventory/ManageInventoryAction.cpp +++ b/src/strategy/actions/player/inventory/ManageInventoryAction.cpp @@ -18,6 +18,7 @@ #include "ChatHelper.h" #include "ItemTemplate.h" +#include "ItemActionEnum.h" #include "GlobalPlayerInspector.h" #include "GlobalItemInspector.h" #include "PlayerbotAI.h" @@ -26,11 +27,52 @@ #include "ItemActionStruct.h" #include "PlayerInventoryFacadeResultEnum.h" #include "PlayerInventoryFacade.h" +#include "GlobalConsumableInspector.h" +#include "ArmorItemInspector.h" +#include "ConsumableConsumableInspector.h" +#include "ConsumableElixirInspector.h" +#include "ConsumablePotionInspector.h" +#include "ContainerInspector.h" +#include "GemInspector.h" +#include "GenericItemInspector.h" +#include "GlyphInspector.h" +#include "ItemTemplate.h" +#include "KeyInspector.h" +#include "MiscellaneousItemInspector.h" +#include "MoneyInspector.h" +#include "PermanentItemInspector.h" +#include "ProjectItemInspector.h" +#include "QuestItemInspector.h" +#include "QuiverItemInspector.h" +#include "ReagentItemInspector.h" +#include "RecipeItemInspector.h" +#include "TradeGoodItemInspector.h" +#include "WeaponItemInspector.h" using InspectorFactory = std::function; using SubclassMap = std::unordered_map; using ClassMap = std::unordered_map; +static const std::vector> arrowsIdsPerLevel = { + { 75, 41586 }, // Terrorshaft Arrow + { 65, 28056 }, // Blackflight Arrow + { 61, 28053 }, // Wicked Arrow => Requires level 55 but is a TBC item so set to level 61 + { 40, 11285 }, // Jagged Arrow + { 25, 3030 }, // Razor Arrow + { 10, 2515 }, // Sharp Arrow + { 0, 2512 } // Rough Arrow +}; + +static const std::vector> bulletsIdsPerLevel = { + { 75, 41584 }, // Frostbite Bullets + { 65, 28061 }, // Ironbite Shell + { 61, 28060 }, // Impact Shot => Requires level 55 but is a TBC item so set to level 61 + { 40, 11284 }, // Accurate Slugs + { 25, 3033 }, // Solid Shot + { 10, 2519 }, // Heavy Shot + { 0, 2516 } // Light Shot +}; + static const ClassMap inspectorFactories = { { ITEM_CLASS_ARMOR, @@ -76,6 +118,15 @@ static const ClassMap inspectorFactories = { return ConsumablePotionInspector(botGUID, itemGUID).determineItemAction(); } + }, + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing generic consumable inspector"); + + return GlobalConsumableInspector(botGUID, itemGUID).determineItemAction(); + } } } } @@ -352,6 +403,15 @@ bool ManageInventoryAction::Execute(Event) // LOG_DEBUG("playerbots.action.manage_inventory", "Equipped item {}, result: {}", std::to_string(itemGUID), result); + Item* item = this->bot->GetItemByPos(action.bagSlot, action.containerSlot); + + if (item == nullptr) + continue; + + const ItemTemplate* proto = item->GetTemplate(); + + LOG_DEBUG("playerbots.action.manage_inventory", "Item {} was supposed to be equipped.", proto->ItemId); + shouldEquipUpgrade = true; break; @@ -388,6 +448,8 @@ bool ManageInventoryAction::Execute(Event) LOG_DEBUG("playerbots.action.manage_inventory", "Done equipping upgrades"); } + this->refillConsumables(); + LOG_DEBUG("playerbots.action.manage_inventory", "Done processing inventory"); if (this->botAI != nullptr) @@ -415,6 +477,7 @@ bool ManageInventoryAction::Execute(Event) } } + return true; // if (this->bot != nullptr) // { @@ -561,13 +624,6 @@ void ManageInventoryAction::processItem(const uint64_t itemGUID) return; } - if (itemState == ITEM_CHANGED) - { - LOG_DEBUG("playerbots.action.manage_inventory", "Item {} (template {}) was already modified.", itemGUID, itemTemplateLowGUID); - - return; - } - if (!itemInspector.itemIsInWorld()) { LOG_DEBUG("playerbots.action.manage_inventory", "Item {} (template {}) is not in world.", itemGUID, itemTemplateLowGUID); @@ -629,6 +685,72 @@ void ManageInventoryAction::processItem(const uint64_t itemGUID) LOG_DEBUG("playerbots.action.manage_inventory", "Done visiting item {}", std::to_string(itemTemplateLowGUID)); } +void ManageInventoryAction::refillConsumables() +{ + if (this->bot->IsClass(CLASS_HUNTER)) + { + Item* rangedWeapon = this->bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED); + + if (rangedWeapon == nullptr) + return; + + const ItemTemplate* weaponTemplate = rangedWeapon->GetTemplate(); + + if (weaponTemplate == nullptr) + return; + + if (weaponTemplate->SubClass == ITEM_SUBCLASS_WEAPON_THROWN) + return; + + const uint8_t playerLevel = this->bot->GetLevel(); + + if (weaponTemplate->SubClass == ITEM_SUBCLASS_WEAPON_GUN) + { + for (std::pair bulletPair : bulletsIdsPerLevel) + { + if (playerLevel > bulletPair.first) + { + Item* item = Item::CreateItem(bulletPair.second, 1); + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + const bool hasEnoughAmmunition = this->bot->HasItemCount(bulletPair.second, itemTemplate->GetMaxStackSize()); + + if (!hasEnoughAmmunition) + { + this->bot->AddItem(bulletPair.second, itemTemplate->GetMaxStackSize()); + } + + delete item; + + return; + } + } + } + + for (std::pair arrowPair : arrowsIdsPerLevel) + { + if (playerLevel > arrowPair.first) + { + Item* item = Item::CreateItem(arrowPair.second, 1); + + const ItemTemplate* itemTemplate = item->GetTemplate(); + + const bool hasEnoughAmmunition = this->bot->HasItemCount(arrowPair.second, itemTemplate->GetMaxStackSize()); + + if (!hasEnoughAmmunition) + { + this->bot->AddItem(arrowPair.second, itemTemplate->GetMaxStackSize()); + } + + delete item; + + return; + } + } + } +} + template void ManageInventoryAction::determineItemAction(const uint32_t botLowGUID, const uint64_t itemLowGUID) { diff --git a/src/strategy/actions/player/inventory/ManageInventoryAction.h b/src/strategy/actions/player/inventory/ManageInventoryAction.h index 0a88cf2f016..f9106d63407 100644 --- a/src/strategy/actions/player/inventory/ManageInventoryAction.h +++ b/src/strategy/actions/player/inventory/ManageInventoryAction.h @@ -6,38 +6,12 @@ #pragma once #include -#include #include -#include "Bag.h" -#include "ItemTemplate.h" - #include "Action.h" #include "Log.h" #include "PlayerbotAI.h" #include "ItemActionStruct.h" -#include "AbstractItemInspector.h" -#include "ArmorItemInspector.h" -#include "ConsumableConsumableInspector.h" -#include "ConsumableElixirInspector.h" -#include "ConsumablePotionInspector.h" -#include "ContainerInspector.h" -#include "GemInspector.h" -#include "GenericItemInspector.h" -#include "GlyphInspector.h" -#include "ItemTemplate.h" -#include "KeyInspector.h" -#include "MiscellaneousItemInspector.h" -#include "MoneyInspector.h" -#include "PermanentItemInspector.h" -#include "ProjectItemInspector.h" -#include "QuestItemInspector.h" -#include "QuiverItemInspector.h" -#include "ReagentItemInspector.h" -#include "RecipeItemInspector.h" -#include "TradeGoodItemInspector.h" -#include "WeaponItemInspector.h" - class ManageInventoryAction : public Action { @@ -98,4 +72,5 @@ class ManageInventoryAction : public Action void processItem(const uint64_t itemGUID); template void determineItemAction(const uint32_t botLowGUID, const uint64_t itemLowGUID); + void refillConsumables(); }; From 3faac8867ebef990e4c7645f7363f856f9b35a82 Mon Sep 17 00:00:00 2001 From: SmashingQuasar Date: Sun, 8 Feb 2026 15:15:48 +0100 Subject: [PATCH 6/8] fix: Resolved all errors. --- src/Bot/Engine/Engine.cpp | 7 ------- .../inspector/consumable/GlobalConsumableInspector.h | 4 ++-- .../consumable/ConsumableConsumableInspector.h | 2 -- .../consumable/elixir/ConsumableElixirInspector.h | 2 -- .../player/facade/inventory/PlayerInventoryFacade.h | 10 ---------- .../player/inspector/global/GlobalPlayerInspector.h | 2 ++ 6 files changed, 4 insertions(+), 23 deletions(-) diff --git a/src/Bot/Engine/Engine.cpp b/src/Bot/Engine/Engine.cpp index a58ef6c3989..2759485a6b8 100644 --- a/src/Bot/Engine/Engine.cpp +++ b/src/Bot/Engine/Engine.cpp @@ -579,13 +579,6 @@ bool Engine::listenAndExecute(Action& action, Event event) botAI->TellMasterNoFacing(out); } - const std::string& actionName = action.getName(); - - if (actionName == "manage inventory") - { - LOG_ERROR("playerbots", "Pre override result"); - } - actionExecuted = actionExecutionListeners.overrideResult(action, actionExecuted, event); actionExecutionListeners.after(action, actionExecuted, event); return actionExecuted; diff --git a/src/domain/item/inspector/consumable/GlobalConsumableInspector.h b/src/domain/item/inspector/consumable/GlobalConsumableInspector.h index dd0ff972894..809ea3fa428 100644 --- a/src/domain/item/inspector/consumable/GlobalConsumableInspector.h +++ b/src/domain/item/inspector/consumable/GlobalConsumableInspector.h @@ -16,7 +16,7 @@ class GlobalConsumableInspector : public AbstractConsumableInspector ) : AbstractConsumableInspector(playerLowGUID, itemLowGUID) {} - bool isInspectable() const + bool isInspectable() const override { const uint8_t itemSubclass = this->getCurrentItemSubclass(); @@ -34,7 +34,7 @@ class GlobalConsumableInspector : public AbstractConsumableInspector return forbiddenItems.contains(itemTemplate->ItemId); } - ItemActionStruct determineItemAction() const + ItemActionStruct determineItemAction() const override { if (!this->isInspectable()) return this->getDefaultItemAction(); diff --git a/src/domain/item/inspector/consumable/consumable/ConsumableConsumableInspector.h b/src/domain/item/inspector/consumable/consumable/ConsumableConsumableInspector.h index 115e6937778..101db76fc0f 100644 --- a/src/domain/item/inspector/consumable/consumable/ConsumableConsumableInspector.h +++ b/src/domain/item/inspector/consumable/consumable/ConsumableConsumableInspector.h @@ -7,8 +7,6 @@ #include "AbstractConsumableInspector.h" #include "ItemActionStruct.h" -#include "ItemActionEnum.h" -#include "GlobalPlayerInspector.h" class ConsumableConsumableInspector : public AbstractConsumableInspector { diff --git a/src/domain/item/inspector/consumable/elixir/ConsumableElixirInspector.h b/src/domain/item/inspector/consumable/elixir/ConsumableElixirInspector.h index 57395e764d0..89a03135175 100644 --- a/src/domain/item/inspector/consumable/elixir/ConsumableElixirInspector.h +++ b/src/domain/item/inspector/consumable/elixir/ConsumableElixirInspector.h @@ -2,13 +2,11 @@ #include -#include "SharedDefines.h" #include "ItemTemplate.h" #include "AbstractConsumableInspector.h" #include "ItemActionStruct.h" #include "ItemActionEnum.h" -#include "GlobalPlayerInspector.h" class ConsumableElixirInspector : public AbstractConsumableInspector { diff --git a/src/domain/player/facade/inventory/PlayerInventoryFacade.h b/src/domain/player/facade/inventory/PlayerInventoryFacade.h index f13329654dd..5f424ffe6ad 100644 --- a/src/domain/player/facade/inventory/PlayerInventoryFacade.h +++ b/src/domain/player/facade/inventory/PlayerInventoryFacade.h @@ -1,23 +1,13 @@ #pragma once #include -#include - -#include "Packet.h" -#include "ItemPackets.h" -#include "ObjectGuid.h" -#include "ObjectAccessor.h" -#include "SharedDefines.h" #include "Player.h" #include "WorldSession.h" -#include "SharedDefines.h" -#include "DBCStructure.h" #include "AuctionHouseMgr.h" #include "AbstractPlayerFacade.h" #include "PlayerInventoryFacadeResultEnum.h" #include "GlobalPlayerInspector.h" -#include "InventoryService.h" #include "GlobalItemInspector.h" class PlayerInventoryFacade : public AbstractPlayerFacade diff --git a/src/domain/player/inspector/global/GlobalPlayerInspector.h b/src/domain/player/inspector/global/GlobalPlayerInspector.h index f99fc8bd8cf..1cc7bfaa640 100644 --- a/src/domain/player/inspector/global/GlobalPlayerInspector.h +++ b/src/domain/player/inspector/global/GlobalPlayerInspector.h @@ -349,5 +349,7 @@ class GlobalPlayerInspector : public AbstractPlayerInspector return STAT_STAMINA; } } + + return STAT_STAMINA; } }; From 15e234b9de8ae3cfd9602c524e40985a9cb43545 Mon Sep 17 00:00:00 2001 From: SmashingQuasar Date: Tue, 3 Mar 2026 13:23:03 +0100 Subject: [PATCH 7/8] feat: Added ZulGurub support to inventory management. --- .../consumable/food/ConsumableFoodInspector.h | 81 +++++++++++++++ .../item/inspector/quest/QuestItemInspector.h | 99 ++++++++++++++++++- .../quest/definition/enum/QuestItemEnum.h | 52 ++++++++++ .../inventory/ManageInventoryAction.cpp | 10 ++ 4 files changed, 240 insertions(+), 2 deletions(-) create mode 100644 src/domain/item/inspector/consumable/food/ConsumableFoodInspector.h create mode 100644 src/domain/item/inspector/quest/definition/enum/QuestItemEnum.h diff --git a/src/domain/item/inspector/consumable/food/ConsumableFoodInspector.h b/src/domain/item/inspector/consumable/food/ConsumableFoodInspector.h new file mode 100644 index 00000000000..8650135accf --- /dev/null +++ b/src/domain/item/inspector/consumable/food/ConsumableFoodInspector.h @@ -0,0 +1,81 @@ +#pragma once + +#include + +#include "ItemTemplate.h" + +#include "AbstractConsumableInspector.h" +#include "ItemActionStruct.h" +#include "PlayerbotAI.h" +#include "PlayerbotAIConfig.h" +#include "PlayerbotMgr.h" + +class ConsumableFoodInspector : public AbstractConsumableInspector +{ +public: + ConsumableFoodInspector( + uint64_t playerLowGUID, + uint64_t itemLowGUID + ) : AbstractConsumableInspector(playerLowGUID, itemLowGUID) + {} + + bool isInspectable() const + { + const uint8_t itemSubclass = this->getCurrentItemSubclass(); + + return AbstractConsumableInspector::isInspectable() && itemSubclass == ITEM_SUBCLASS_FOOD; + } + + ItemActionStruct determineItemAction() const + { + if (!this->isInspectable()) + return this->getDefaultItemAction(); + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return this->getDefaultItemAction(); + + const ObjectGuid playerGUID = ObjectGuid::Create(this->playerLowGUID); + Player* player = ObjectAccessor::FindPlayer(playerGUID); + + if (player == nullptr) + return this->getDefaultItemAction(); + + PlayerbotAI* const playerbotAI = PlayerbotsMgr::instance().GetPlayerbotAI(player); + + if (playerbotAI == nullptr) + return this->getDefaultItemAction(); + + if (playerbotAI->HasCheat(BotCheatMask::food)) + { + return this->getSellAction(); + } + + return this->getSellAction(); + } + + bool isForbiddenItem() const + { + const std::unordered_set forbiddenItems = this->getForbiddenItemsGUIDs(); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return false; + + return forbiddenItems.contains(itemTemplate->ItemId); + } + +protected: + + const std::unordered_set getForbiddenItemsGUIDs() const + { + return { + + }; + } + +}; diff --git a/src/domain/item/inspector/quest/QuestItemInspector.h b/src/domain/item/inspector/quest/QuestItemInspector.h index 551d8144a2a..ef163e22b19 100644 --- a/src/domain/item/inspector/quest/QuestItemInspector.h +++ b/src/domain/item/inspector/quest/QuestItemInspector.h @@ -2,12 +2,11 @@ #include -#include "SharedDefines.h" #include "ItemTemplate.h" #include "AbstractItemInspector.h" #include "ItemActionStruct.h" -#include "ItemActionEnum.h" +#include "definition/enum/QuestItemEnum.h" class QuestItemInspector : public AbstractItemInspector { @@ -47,6 +46,16 @@ class QuestItemInspector : public AbstractItemInspector if (player->HasQuestForItem(itemTemplate->ItemId)) return this->getKeepItemAction(); + if ( + this->isZulGurubCurrency(itemTemplate->ItemId) + || this->isZulGurubToken(itemTemplate->ItemId) + || this->isZulGurubDoll(itemTemplate->ItemId) + || itemTemplate->ItemId == uint32_t(QuestItemEnum::PRIMAL_HAKKARI_IDOL) + ) + { + return this->getKeepItemAction(); + } + return this->getDestroyItemAction(); } @@ -63,6 +72,92 @@ class QuestItemInspector : public AbstractItemInspector protected: + // @TODO: Move this to a dedicated class. + static constexpr std::array ZulGurubCurrencies = { + QuestItemEnum::ZULIAN_COIN, + QuestItemEnum::RAZZASHI_COIN, + QuestItemEnum::HAKKARI_COIN, + QuestItemEnum::GURUBASHI_COIN, + QuestItemEnum::VILEBREW_COIN, + QuestItemEnum::WITHERBARK_COIN, + QuestItemEnum::SANDFURY_COIN, + QuestItemEnum::SKULLSPLITTER_COIN, + QuestItemEnum::BLOODSCALP_COIN, + + QuestItemEnum::RED_HAKKARI_BIJOU, + QuestItemEnum::BLUE_HAKKARI_BIJOU, + QuestItemEnum::YELLOW_HAKKARI_BIJOU, + QuestItemEnum::ORANGE_HAKKARI_BIJOU, + QuestItemEnum::GREEN_HAKKARI_BIJOU, + QuestItemEnum::PURPLE_HAKKARI_BIJOU, + QuestItemEnum::BRONZE_HAKKARI_BIJOU, + QuestItemEnum::SILVER_HAKKARI_BIJOU, + QuestItemEnum::GOLD_HAKKARI_BIJOU + }; + + static constexpr std::array ZulGurubTokens = { + QuestItemEnum::PRIMAL_HAKKARI_BINDINGS, + QuestItemEnum::PRIMAL_HAKKARI_ARMSPLINT, + QuestItemEnum::PRIMAL_HAKKARI_STANCHION, + QuestItemEnum::PRIMAL_HAKKARI_GIRDLE, + QuestItemEnum::PRIMAL_HAKKARI_SASH, + QuestItemEnum::PRIMAL_HAKKARI_SHAWL, + QuestItemEnum::PRIMAL_HAKKARI_TABARD, + QuestItemEnum::PRIMAL_HAKKARI_KOSSACK, + QuestItemEnum::PRIMAL_HAKKARI_AEGIS + }; + + static constexpr std::array ZulGurubDolls = { + QuestItemEnum::PUNCTURED_VOODOO_DOLL_WARRIOR, + QuestItemEnum::PUNCTURED_VOODOO_DOLL_ROGUE, + QuestItemEnum::PUNCTURED_VOODOO_DOLL_PALADIN, + QuestItemEnum::PUNCTURED_VOODOO_DOLL_HUNTER, + QuestItemEnum::PUNCTURED_VOODOO_DOLL_SHAMAN, + QuestItemEnum::PUNCTURED_VOODOO_DOLL_MAGE, + QuestItemEnum::PUNCTURED_VOODOO_DOLL_WARLOCK, + QuestItemEnum::PUNCTURED_VOODOO_DOLL_PRIEST, + QuestItemEnum::PUNCTURED_VOODOO_DOLL_DRUID + }; + + [[nodiscard]] bool isZulGurubCurrency(const uint32_t itemTemplateId) const noexcept + { + for (const QuestItemEnum zulGurubCurrency : ZulGurubCurrencies) + { + if (itemTemplateId == uint32_t(zulGurubCurrency)) + { + return true; + } + } + + return false; + } + + [[nodiscard]] bool isZulGurubToken(const uint32_t itemTemplateId) const noexcept + { + for (const QuestItemEnum zulGurubToken : ZulGurubTokens) + { + if (itemTemplateId == uint32_t(zulGurubToken)) + { + return true; + } + } + + return false; + } + + [[nodiscard]] bool isZulGurubDoll(const uint32_t itemTemplateId) const noexcept + { + for (const QuestItemEnum zulGurubDoll : ZulGurubDolls) + { + if (itemTemplateId == uint32_t(zulGurubDoll)) + { + return true; + } + } + + return false; + } + const std::unordered_set getForbiddenItemsGUIDs() const { return {}; diff --git a/src/domain/item/inspector/quest/definition/enum/QuestItemEnum.h b/src/domain/item/inspector/quest/definition/enum/QuestItemEnum.h new file mode 100644 index 00000000000..2948b8baff2 --- /dev/null +++ b/src/domain/item/inspector/quest/definition/enum/QuestItemEnum.h @@ -0,0 +1,52 @@ +#pragma once + +#include + +enum class QuestItemEnum : uint32_t +{ + // Zul'Gurub currency + ZULIAN_COIN = 19698, + RAZZASHI_COIN = 19699, + HAKKARI_COIN = 19700, + GURUBASHI_COIN = 19701, + VILEBREW_COIN = 19702, + WITHERBARK_COIN = 19703, + SANDFURY_COIN = 19704, + SKULLSPLITTER_COIN = 19705, + BLOODSCALP_COIN = 19706, + + RED_HAKKARI_BIJOU = 19707, + BLUE_HAKKARI_BIJOU = 19708, + YELLOW_HAKKARI_BIJOU = 19709, + ORANGE_HAKKARI_BIJOU = 19710, + GREEN_HAKKARI_BIJOU = 19711, + PURPLE_HAKKARI_BIJOU = 19712, + BRONZE_HAKKARI_BIJOU = 19713, + SILVER_HAKKARI_BIJOU = 19714, + GOLD_HAKKARI_BIJOU = 19715, + + // Zul'Gurub token + PRIMAL_HAKKARI_BINDINGS = 19716, + PRIMAL_HAKKARI_ARMSPLINT = 19717, + PRIMAL_HAKKARI_STANCHION = 19718, + PRIMAL_HAKKARI_GIRDLE = 19719, + PRIMAL_HAKKARI_SASH = 19720, + PRIMAL_HAKKARI_SHAWL = 19721, + PRIMAL_HAKKARI_TABARD = 19722, + PRIMAL_HAKKARI_KOSSACK = 19723, + PRIMAL_HAKKARI_AEGIS = 19724, + + // Dolls + PUNCTURED_VOODOO_DOLL_WARRIOR = 19813, + PUNCTURED_VOODOO_DOLL_ROGUE = 19814, + PUNCTURED_VOODOO_DOLL_PALADIN = 19815, + PUNCTURED_VOODOO_DOLL_HUNTER = 19816, + PUNCTURED_VOODOO_DOLL_SHAMAN = 19817, + PUNCTURED_VOODOO_DOLL_MAGE = 19818, + PUNCTURED_VOODOO_DOLL_WARLOCK = 19819, + PUNCTURED_VOODOO_DOLL_PRIEST = 19820, + PUNCTURED_VOODOO_DOLL_DRUID = 19821, + + // Idols + PRIMAL_HAKKARI_IDOL = 22637 +}; diff --git a/src/strategy/actions/player/inventory/ManageInventoryAction.cpp b/src/strategy/actions/player/inventory/ManageInventoryAction.cpp index 1977b144cd9..1b2491d1064 100644 --- a/src/strategy/actions/player/inventory/ManageInventoryAction.cpp +++ b/src/strategy/actions/player/inventory/ManageInventoryAction.cpp @@ -10,6 +10,7 @@ #include #include +#include "ConsumableFoodInspector.h" #include "CreateNextAction.h" #include "EquipAction.h" #include "ItemActionEnum.h" @@ -119,6 +120,15 @@ static const ClassMap inspectorFactories = { return ConsumablePotionInspector(botGUID, itemGUID).determineItemAction(); } }, + { + ITEM_SUBCLASS_FOOD, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing FOOD inspector"); + + return ConsumableFoodInspector(botGUID, itemGUID).determineItemAction(); + } + }, { ManageInventoryAction::ANY_SUBCLASS, [](const uint32_t botGUID, const uint64_t itemGUID) From f8fa910cb211f2846bc4bb5f2614e611efa24cc7 Mon Sep 17 00:00:00 2001 From: SmashingQuasar Date: Sun, 8 Mar 2026 13:01:01 +0100 Subject: [PATCH 8/8] feat: Added miscellaneous items. --- .../misc/definition/enum/item-misc.enum.h | 8 ++++ .../definition/enum/item-misc-reagent.enum.h | 0 .../enum/item-projectile-arrow.enum.h | 41 ++++++++++++++++ .../enum/item-projectile-bullet.enum.h | 47 +++++++++++++++++++ .../enum/item-reagent-reagent.enum.h | 13 +++++ 5 files changed, 109 insertions(+) create mode 100644 src/domain/item/misc/definition/enum/item-misc.enum.h create mode 100644 src/domain/item/misc/reagent/definition/enum/item-misc-reagent.enum.h create mode 100644 src/domain/item/projectile/arrow/definition/enum/item-projectile-arrow.enum.h create mode 100644 src/domain/item/projectile/bullet/definition/enum/item-projectile-bullet.enum.h create mode 100644 src/domain/item/reagent/reagent/definition/enum/item-reagent-reagent.enum.h diff --git a/src/domain/item/misc/definition/enum/item-misc.enum.h b/src/domain/item/misc/definition/enum/item-misc.enum.h new file mode 100644 index 00000000000..b29061e21ba --- /dev/null +++ b/src/domain/item/misc/definition/enum/item-misc.enum.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +enum class ItemMiscEnum : uint32_t +{ + +}; diff --git a/src/domain/item/misc/reagent/definition/enum/item-misc-reagent.enum.h b/src/domain/item/misc/reagent/definition/enum/item-misc-reagent.enum.h new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/domain/item/projectile/arrow/definition/enum/item-projectile-arrow.enum.h b/src/domain/item/projectile/arrow/definition/enum/item-projectile-arrow.enum.h new file mode 100644 index 00000000000..7b2f1f98920 --- /dev/null +++ b/src/domain/item/projectile/arrow/definition/enum/item-projectile-arrow.enum.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +enum class ItemProjectileArrowEnum : uint32_t +{ + TEST_ARROW = 2103, + ROUGH_ARROW = 2512, + DEPRICATED_SHARP_ARROW = 2514, + SHARP_ARROW = 2515, + DEPRECATED_STANDARD_ARROW = 2517, + DEPRICATED_WHIPWOOD_ARROW = 3029, + RAZOR_ARROW = 3030, + DEPRICATED_RAZOR_ARROW = 3031, + NPC_EQUIP_3043 = 3043, + FEATHERED_ARROW = 3464, + PRECISION_ARROW = 9399, + EXPLOSIVE_ARROW = 10579, + JAGGED_ARROW = 11285, + DOOMSHOT = 12654, + THORIUM_HEADED_ARROW = 18042, + MONSTER_FIRE_ARROW = 19082, + ICE_THREADED_ARROW = 19316, + WARDEN_S_ARROW = 24412, + SCOUT_S_ARROW = 24417, + WICKED_ARROW = 28053, + BLACKFLIGHT_ARROW = 28056, + NPC_EQUIP_29907 = 29907, + NETHER_SPIKE = 30319, + HALAANI_RAZORSHAFT = 30611, + TIMELESS_ARROW = 31737, + WARDEN_S_ARROW_2 = 31949, + THE_MACHO_GNOME_S_ARROW = 32760, + ADAMANTITE_STINGER = 33803, + MYSTERIOUS_ARROW = 34581, + NPC_EQUIP_37309 = 37309, + SARONITE_RAZORHEADS = 41165, + TERRORSHAFT_ARROW = 41586, + NPC_EQUIP_46854 = 46854, + ICEBLADE_ARROW = 52021, +}; diff --git a/src/domain/item/projectile/bullet/definition/enum/item-projectile-bullet.enum.h b/src/domain/item/projectile/bullet/definition/enum/item-projectile-bullet.enum.h new file mode 100644 index 00000000000..421995c2b2b --- /dev/null +++ b/src/domain/item/projectile/bullet/definition/enum/item-projectile-bullet.enum.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +enum class ItemProjectileBulletEnum : uint32_t +{ + DEPRECATED_STANDARD_SHOT = 2104, + DEPRECATED_IRON_SHOT = 2513, + LIGHT_SHOT = 2516, + DEPRECATED_SOLID_SHOT = 2518, + HEAVY_SHOT = 2519, + DEPRECATED_IMPACT_SHOT = 3032, + SOLID_SHOT = 3033, + GLYPHED_BREASTPLATE = 3034, + EXPLODING_SHOT = 3465, + FLASH_PELLET = 4960, + SMOOTH_PEBBLE = 5568, + CRAFTED_LIGHT_SHOT = 8067, + CRAFTED_HEAVY_SHOT = 8068, + CRAFTED_SOLID_SHOT = 8069, + HI_IMPACT_MITHRIL_SLUGS = 10512, + MITHRIL_GYRO_SHOT = 10513, + ACCURATE_SLUGS = 11284, + ROCKSHARD_PELLETS = 11630, + MINIATURE_CANNON_BALLS = 13377, + THORIUM_SHELLS = 15997, + NPC_EQUIP_19285 = 19285, + FAST_TEST_AMMO = 19286, + ICE_THREADED_BULLET = 19317, + FEL_IRON_SHELLS = 23772, + ADAMANTITE_SHELLS = 23773, + IMPACT_SHOT = 28060, + IRONBITE_SHELL = 28061, + HUNTER_120_EPIC_TEST_BULLETS = 29885, + HALAANI_GRIMSHOT = 30612, + TIMELESS_SHELL = 31735, + THE_SARGE_S_BULLET = 32761, + NPC_EQUIP_32880 = 32880, + NPC_EQUIP_32881 = 32881, + HELLFIRE_SHOT = 32882, + FELBANE_SLUGS = 32883, + NPC_EQUIP_34534 = 34534, + MYSTERIOUS_SHELL = 34582, + MAMMOTH_CUTTERS = 41164, + FROSTBITE_BULLETS = 41584, + SHATTER_ROUNDS = 52020, +}; diff --git a/src/domain/item/reagent/reagent/definition/enum/item-reagent-reagent.enum.h b/src/domain/item/reagent/reagent/definition/enum/item-reagent-reagent.enum.h new file mode 100644 index 00000000000..5fce8f8d2cc --- /dev/null +++ b/src/domain/item/reagent/reagent/definition/enum/item-reagent-reagent.enum.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +enum class ItemReagentReagentEnum : uint32_t +{ + FLASH_POWDER = 5140, + WORTHLESS_BLINDING_POWDER = 5530, + ANKH = 17030, + NPC_EQUIP_22453 = 22453, + NPC_EQUIP_22454 = 22454, + NPC_EQUIP_22455 = 22455, +};