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/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/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..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) { @@ -22,6 +23,14 @@ void NonCombatStrategy::InitTriggers(std::vector& triggers) CreateNextAction(1.0f) } ) + ); + triggers.push_back( + new TriggerNode( + "seldom", + { + CreateNextAction(1.0f) + } + ) ); triggers.push_back( new TriggerNode( diff --git a/src/Bot/Factory/PlayerbotFactory.cpp b/src/Bot/Factory/PlayerbotFactory.cpp index ca2b074ce21..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(); @@ -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(); } @@ -497,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() @@ -1573,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) @@ -1990,6 +1873,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) } } +// Unused method. bool PlayerbotFactory::IsDesiredReplacement(Item* item) { if (!item) @@ -2001,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; } @@ -2025,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) @@ -2273,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"); @@ -2351,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); @@ -2480,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) @@ -2926,6 +2682,8 @@ void PlayerbotFactory::InitInstanceQuests() void PlayerbotFactory::ClearInventory() { + LOG_ERROR("playerbots", "Clearing inventory for {}", bot->GetName()); + DestroyItemsVisitor visitor(bot); IterateItems(&visitor); } @@ -3970,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 729090b5f06..e87c986fbca 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 (PlayerbotAIConfig::instance().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(PlayerbotAIConfig::instance().minRandomBotRandomizeTime, PlayerbotAIConfig::instance().maxRandomBotRandomizeTime); + + ScheduleRandomize(botId, time); + + return true; +} + +bool RandomPlayerbotMgr::ProcessBotTeleportation(Player* bot) { + if (PlayerbotAIConfig::instance().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(PlayerbotAIConfig::instance().minRandomBotTeleportInterval, + PlayerbotAIConfig::instance().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,30 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot) void RandomPlayerbotMgr::Randomize(Player* bot) { + uint8 level = bot->GetLevel(); + + LOG_ERROR("playerbots", "randomizing bot {}", bot->GetName()); + 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/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 6326eabade5..23ba096314f 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 (RandomPlayerbotMgr::instance().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 (RandomPlayerbotMgr::instance().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/domain/item/inspector/abstract/AbstractItemInspector.h b/src/domain/item/inspector/abstract/AbstractItemInspector.h new file mode 100644 index 00000000000..55dc9b9be4e --- /dev/null +++ b/src/domain/item/inspector/abstract/AbstractItemInspector.h @@ -0,0 +1,290 @@ +#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 getBagSlot() 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; + } + + 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(); + + 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(); + } + + uint32_t getItemSellPrice() const + { + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + return 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 + { + 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, + .bagSlot = 0, + .containerSlot = 0, + .equipmentSlot = 0 + }; + } + + ItemActionStruct getForbiddenItemAction() const + { + return this->getDestroyItemAction(); + } + + ItemActionStruct getDestroyItemAction() const + { + return { + .action = ItemActionEnum::DESTROY, + .bagSlot = this->getBagSlot(), + .containerSlot = 0, + .equipmentSlot = 0 + }; + } + + ItemActionStruct getSellAction() const + { + const bool sellable = this->itemIsSellable(); + + if (sellable) + return { + .action = ItemActionEnum::SELL, + .bagSlot = this->getBagSlot(), + .containerSlot = 0, + .equipmentSlot = 0 + }; + + return { + .action = ItemActionEnum::DESTROY, + .bagSlot = this->getBagSlot(), + .containerSlot = 0, + .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..a9486d226c4 --- /dev/null +++ b/src/domain/item/inspector/abstract/definition/struct/ItemActionStruct.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#include "ItemActionEnum.h" + +struct ItemActionStruct +{ + const ItemActionEnum action; + 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 new file mode 100644 index 00000000000..544a5f58145 --- /dev/null +++ b/src/domain/item/inspector/armor/ArmorItemInspector.h @@ -0,0 +1,147 @@ +#pragma once + +#include + +#include "Item.h" +#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()) + { + LOG_DEBUG("playerbots.armor.inspector", "Item is not inspectable"); + + return this->getDefaultItemAction(); + } + + if (this->isForbiddenItem()) + return this->getForbiddenItemAction(); + + const ObjectGuid playerGUID = ObjectGuid::Create(this->playerLowGUID); + Player* player = ObjectAccessor::FindPlayer(playerGUID); + + if (player == nullptr) + { + LOG_DEBUG("playerbots.armor.inspector", "player nullptr"); + + return this->getDefaultItemAction(); + } + + Item* const item = this->getMutableCurrentItem(); + + if (item == nullptr) + { + LOG_DEBUG("playerbots.armor.inspector", "item nullptr"); + + return this->getDefaultItemAction(); + } + + const InventoryResult canUseItem = player->CanUseItem(item); + + if (canUseItem != EQUIP_ERR_OK) + { + LOG_DEBUG("playerbots.armor.inspector", "player can't use item"); + + return this->getSellAction(); + } + + StatsWeightCalculator statisticsWeightCalculator(player); + const ItemTemplate* const itemTemplate = this->getCurrentItemTemplate(); + + if (itemTemplate == nullptr) + { + LOG_DEBUG("playerbots.armor.inspector", "item template nullptr"); + + return this->getDefaultItemAction(); + } + + std::vector slots = InventoryService::GetInstance().getItemEquipmentSlots(itemTemplate); + const float newItemStatisticsWeight = statisticsWeightCalculator.CalculateItem(itemTemplate->ItemId); + + statisticsWeightCalculator.Reset(); + + for (uint8_t i = 0; i < slots.size(); ++i) + { + const uint32_t equipmentSlot = slots[i]; + 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(); + + if (currentlyEquippedItemTemplate == nullptr) + { + LOG_DEBUG("playerbots.armor.inspector", "current item template nullptr"); + + return this->getDefaultItemAction(); + } + + const float existingItemStatisticsWeight = statisticsWeightCalculator.CalculateItem(currentlyEquippedItemTemplate->ItemId); + + if (existingItemStatisticsWeight < newItemStatisticsWeight) + { + LOG_DEBUG("playerbots.armor.inspector", "Current item is worse than item"); + + return { + .action = ItemActionEnum::EQUIP, + .bagSlot = this->getBagSlot(), + .containerSlot = this->getItemSlot(), + .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/GlobalConsumableInspector.h b/src/domain/item/inspector/consumable/GlobalConsumableInspector.h new file mode 100644 index 00000000000..809ea3fa428 --- /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 override + { + 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 override + { + 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/consumable/consumable/ConsumableConsumableInspector.h b/src/domain/item/inspector/consumable/consumable/ConsumableConsumableInspector.h new file mode 100644 index 00000000000..101db76fc0f --- /dev/null +++ b/src/domain/item/inspector/consumable/consumable/ConsumableConsumableInspector.h @@ -0,0 +1,958 @@ +#pragma once + +#include + +#include "SharedDefines.h" +#include "ItemTemplate.h" + +#include "AbstractConsumableInspector.h" +#include "ItemActionStruct.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..89a03135175 --- /dev/null +++ b/src/domain/item/inspector/consumable/elixir/ConsumableElixirInspector.h @@ -0,0 +1,82 @@ +#pragma once + +#include + +#include "ItemTemplate.h" + +#include "AbstractConsumableInspector.h" +#include "ItemActionStruct.h" +#include "ItemActionEnum.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, + .bagSlot = 0, + .containerSlot = 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/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/consumable/potion/ConsumablePotionInspector.h b/src/domain/item/inspector/consumable/potion/ConsumablePotionInspector.h new file mode 100644 index 00000000000..09adab49a0c --- /dev/null +++ b/src/domain/item/inspector/consumable/potion/ConsumablePotionInspector.h @@ -0,0 +1,98 @@ +#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, + .bagSlot = 0, + .containerSlot = 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..1fd7b4bae07 --- /dev/null +++ b/src/domain/item/inspector/container/ContainerInspector.h @@ -0,0 +1,113 @@ +#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 GlobalPlayerInspector playerInspector(this->playerLowGUID); + + const uint8_t playerClass = playerInspector.getCurrentPlayerClass(); + 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 Item* const equippedItem = playerInspector.getItemByPosition(INVENTORY_SLOT_BAG_0, i); + + if (equippedItem == nullptr) + return { + .action = ItemActionEnum::EQUIP, + .bagSlot = this->getBagSlot(), + .containerSlot = this->getItemSlot(), + .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, + .bagSlot = this->getBagSlot(), + .containerSlot = this->getItemSlot(), + .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..ef163e22b19 --- /dev/null +++ b/src/domain/item/inspector/quest/QuestItemInspector.h @@ -0,0 +1,165 @@ +#pragma once + +#include + +#include "ItemTemplate.h" + +#include "AbstractItemInspector.h" +#include "ItemActionStruct.h" +#include "definition/enum/QuestItemEnum.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(); + + 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(); + } + + 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: + + // @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/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..dd5b057261d --- /dev/null +++ b/src/domain/item/inspector/weapon/WeaponItemInspector.h @@ -0,0 +1,150 @@ +#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()) + { + 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 InventoryResult canUseItem = player->CanUseItem(currentItem); + + 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); + + const float newItemStatisticsWeight = statisticsWeightCalculator.CalculateItem(itemTemplate->ItemId); + + statisticsWeightCalculator.Reset(); + + for (uint8_t i = 0; i < slots.size(); ++i) + { + const uint32_t equipmentSlot = slots[i]; + + 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(); + + if (currentlyEquippedItemTemplate == nullptr) + return this->getDefaultItemAction(); + + const float existingItemStatisticsWeight = statisticsWeightCalculator.CalculateItem(currentlyEquippedItemTemplate->ItemId); + + if (existingItemStatisticsWeight < newItemStatisticsWeight) + { + LOG_DEBUG("playerbots.inspector.weapon", "New item is better than old one."); + + return { + .action = ItemActionEnum::EQUIP, + .bagSlot = this->getBagSlot(), + .containerSlot = this->getItemSlot(), + .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/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, +}; 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..5f424ffe6ad --- /dev/null +++ b/src/domain/player/facade/inventory/PlayerInventoryFacade.h @@ -0,0 +1,140 @@ +#pragma once + +#include +#include "Player.h" +#include "WorldSession.h" +#include "AuctionHouseMgr.h" + +#include "AbstractPlayerFacade.h" +#include "PlayerInventoryFacadeResultEnum.h" +#include "GlobalPlayerInspector.h" +#include "GlobalItemInspector.h" + +class PlayerInventoryFacade : public AbstractPlayerFacade +{ +public: + PlayerInventoryFacade( + uint64_t playerGUID + ) : + AbstractPlayerFacade(playerGUID) + {} + + PlayerInventoryFacadeResultEnum equipItem(uint64_t itemLowGUID, uint8_t equipmentSlot) + { + 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(); + + if (player == nullptr) + return PlayerInventoryFacadeResultEnum::IMPOSSIBLE; + + Item* item = itemInspector.getMutableCurrentItem(); + + if (item == nullptr) + return PlayerInventoryFacadeResultEnum::IMPOSSIBLE; + + LOG_DEBUG("playerbots.player.facade", "destroying item"); + + uint32_t itemCount = itemInspector.getItemCurrentCount(); + + player->DestroyItemCount(item, itemCount, true); + + return PlayerInventoryFacadeResultEnum::OK; + } + + PlayerInventoryFacadeResultEnum sellItem(uint64_t 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_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_DEBUG("playerbots.player.facade", "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_DEBUG("playerbots.player.facade", "Total item stack value {}", std::to_string(totalItemStackValue)); + + LOG_DEBUG("playerbots.player.facade", "modifiying player money"); + player->ModifyMoney(totalItemStackValue); + // LOG_DEBUG("playerbots.player.facade", "destroying item after sell"); + // player->DestroyItemCount(itemInspector.getBagSlot(), itemInspector.getItemSlot(), true); + + + LOG_DEBUG("playerbots.player.facade", "removing item"); + player->RemoveItem(item->GetBagSlot(), item->GetSlot(), true); + LOG_DEBUG("playerbots.player.facade", "removing item from update queue"); + item->RemoveFromUpdateQueueOf(player); + LOG_DEBUG("playerbots.player.facade", "adding item to buy back slot"); + player->AddItemToBuyBackSlot(item, totalItemStackValue); + 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(); + // } +}; 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..d60187392ab --- /dev/null +++ b/src/domain/player/facade/inventory/definition/enum/PlayerInventoryFacadeResultEnum.h @@ -0,0 +1,8 @@ +#pragma once + +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..1cc7bfaa640 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,247 @@ 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; + } + } + + return STAT_STAMINA; + } }; 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..fc709b0e80a --- /dev/null +++ b/src/repository/PlayerGuildRepository.cpp @@ -0,0 +1,76 @@ +#include +#include +#include "PlayerGuildRepository.h" +#include "PlayerbotAIConfig.h" +#include "QueryResult.h" +#include "UInt32VectorToString.h" +#include "DatabaseEnv.h" +#include "Log.h" + +std::unordered_set PlayerGuildRepository::GetPlayerGuildsIds() +{ + std::string randomBotsAccountsIdsString = UInt32VectorToString(PlayerbotAIConfig::instance().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(PlayerbotAIConfig::instance().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..068ab8e1c63 --- /dev/null +++ b/src/repository/PlayerGuildRepository.h @@ -0,0 +1,30 @@ +#ifndef PLAYER_GUILD_REPOSITORY_H +#define PLAYER_GUILD_REPOSITORY_H + +#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/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..1b2491d1064 --- /dev/null +++ b/src/strategy/actions/player/inventory/ManageInventoryAction.cpp @@ -0,0 +1,772 @@ +/* + * 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 "ConsumableFoodInspector.h" +#include "CreateNextAction.h" +#include "EquipAction.h" +#include "ItemActionEnum.h" +#include "Log.h" +#include "Player.h" +#include "ChatHelper.h" +#include "ItemTemplate.h" + +#include "ItemActionEnum.h" +#include "GlobalPlayerInspector.h" +#include "GlobalItemInspector.h" +#include "PlayerbotAI.h" +#include "Event.h" +#include "RandomPlayerbotMgr.h" +#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, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing ARMOR inspector"); + + return ArmorItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_CONSUMABLE, + { + { + { + ITEM_SUBCLASS_CONSUMABLE, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing CONSUMABLE inspector"); + + return ConsumableConsumableInspector(botGUID, itemGUID).determineItemAction(); + } + }, + { + ITEM_SUBCLASS_ELIXIR, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing ELIXIR inspector"); + + return ConsumableElixirInspector(botGUID, itemGUID).determineItemAction(); + } + }, + { + ITEM_SUBCLASS_POTION, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing POTION inspector"); + + 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) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing generic consumable inspector"); + + return GlobalConsumableInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + } + }, + { + ITEM_CLASS_CONTAINER, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing CONTAINER inspector"); + + return ContainerInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_GEM, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing GEM inspector"); + + return GemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_GENERIC, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing GENERIC inspector"); + + return GenericItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_GLYPH, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing GLYPH inspector"); + + return GlyphInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_KEY, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing KEY inspector"); + + return KeyInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_MISC, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing MISC inspector"); + + return MiscellaneousItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_MONEY, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing MONEY inspector"); + + return MoneyInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_PERMANENT, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing PERMANENT inspector"); + + return PermanentItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_PROJECTILE, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing PROJECTILE inspector"); + + return ProjectItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_QUEST, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing QUEST inspector"); + + return QuestItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_QUIVER, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing QUIVER inspector"); + + return QuiverItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_REAGENT, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing REAGENT inspector"); + + return ReagentItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_RECIPE, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing RECIPE inspector"); + + return RecipeItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_TRADE_GOODS, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing TRADE GOODS inspector"); + + return TradeGoodItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, + { + ITEM_CLASS_WEAPON, + { + { + ManageInventoryAction::ANY_SUBCLASS, + [](const uint32_t botGUID, const uint64_t itemGUID) + { + LOG_DEBUG("playerbots.action.manage_inventory", "executing WEAPON inspector"); + + return WeaponItemInspector(botGUID, itemGUID).determineItemAction(); + } + } + } + }, +}; + +bool ManageInventoryAction::Execute(Event) +{ + LOG_DEBUG("playerbots.action.manage_inventory", "starting inventory management action"); + + this->itemActions.clear(); + + if (this->bot == nullptr) + { + LOG_DEBUG("playerbots.action.manage_inventory", "ManageInventoryAction::Execute bot is nullptr"); + + return false; + } + + const std::string botName = this->bot->GetName(); + + LOG_DEBUG("playerbots.action.manage_inventory", "managing inventory for {}", botName); + + if (!RandomPlayerbotMgr::instance().IsRandomBot(bot)) + return true; + + this->iterateBags(); + + LOG_DEBUG("playerbots.action.manage_inventory", "done processing bags for {}", botName); + + PlayerInventoryFacade playerInventoryFacade(this->bot->GetGUID().GetRawValue()); + + bool shouldEquipUpgrade = false; + + 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_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_DEBUG("playerbots.action.manage_inventory", "equipping item through facade"); + + // const PlayerInventoryFacadeResultEnum result = playerInventoryFacade.equipItem(itemGUID, action.equipmentSlot); + + // 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; + } + case ItemActionEnum::SELL: + { + LOG_DEBUG("playerbots.action.manage_inventory", "selling item through facade"); + const PlayerInventoryFacadeResultEnum result = playerInventoryFacade.sellItem(itemGUID); + + LOG_DEBUG("playerbots.action.manage_inventory", "Sold item {}, result: {}", std::to_string(itemGUID), result); + + break; + } + case ItemActionEnum::DESTROY: + { + LOG_DEBUG("playerbots.action.manage_inventory", "destroying item through facade"); + const PlayerInventoryFacadeResultEnum result = playerInventoryFacade.destroyItem(itemGUID); + + 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_DEBUG("playerbots.action.manage_inventory", "Equipping upgrades"); + + this->botAI->DoSpecificAction(CreateNextAction(1.0f).factory); + + LOG_DEBUG("playerbots.action.manage_inventory", "Done equipping upgrades"); + } + + this->refillConsumables(); + + LOG_DEBUG("playerbots.action.manage_inventory", "Done processing inventory"); + + if (this->botAI != nullptr) + { + const Player* const master = botAI->GetMaster(); + + LOG_DEBUG("playerbots.action.manage_inventory", "fetched master for {}", botName); + + if (master != nullptr) + { + const std::string masterName = master->GetName(); + + LOG_DEBUG("playerbots.action.manage_inventory", "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_DEBUG("playerbots.action.manage_inventory", "told master ({}) of bot {}", masterName, botName); + } + } + + + return true; + // if (this->bot != nullptr) + // { + // this->bot->SaveToDB(false, false); + + // return true; + // } + + // LOG_DEBUG("playerbots.action.manage_inventory", "ManageInventoryAction::Execute can't save, bot is nullptr"); + + // return true; +} + +void ManageInventoryAction::iterateBags() +{ + LOG_DEBUG("playerbots.action.manage_inventory", "Processing main bag"); + + if (this->bot == nullptr) + { + LOG_DEBUG("playerbots.action.manage_inventory", "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_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_DEBUG("playerbots.action.manage_inventory", "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_DEBUG("playerbots.action.manage_inventory", "Processing bag in slot {}", std::to_string(slot)); + + if (this->bot == nullptr) + { + LOG_DEBUG("playerbots.action.manage_inventory", "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(slot); + } + + if (this->bot == nullptr) + { + LOG_DEBUG("playerbots.action.manage_inventory", "ManageInventoryAction::iterateBags bot became nullptr"); + + return; + } + + LOG_DEBUG("playerbots.action.manage_inventory", "done processing bags for {}", this->bot->GetName()); +} + +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.getItemByPosition(INVENTORY_SLOT_BAG_0, bagSlot); + + if (possibleBag == nullptr) + return; + + const Bag* bag = possibleBag->ToBag(); + + if (bag == nullptr) + { + LOG_DEBUG("playerbots.action.manage_inventory", "Bag did not exist"); + + return; + } + + const uint8_t size = bag->GetBagSize(); + + for (uint8_t slot = 0; slot < size; ++slot) + { + LOG_DEBUG("playerbots.action.manage_inventory", "Processing item {} in bag", std::to_string(slot)); + + if (bag == nullptr) + { + LOG_DEBUG("playerbots.action.manage_inventory", "ManageInventoryAction::iterateBag bag became nullptr"); + + return; + } + + // Item* item = bag->GetItemByPos(slot); + Item* item = playerInspector.getItemByPosition(bagSlot, slot); + + if (item == nullptr) + { + LOG_DEBUG("playerbots.action.manage_inventory", "Item {} in bag did not exist", std::to_string(slot)); + + continue; + } + + const uint64_t itemGUID = item->GetGUID().GetRawValue(); + + this->processItem(itemGUID); + } + + 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_DEBUG("playerbots.action.manage_inventory", "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_DEBUG("playerbots.action.manage_inventory", "Item {} (template {}) was already flagged for removal.", itemGUID, itemTemplateLowGUID); + + return; + } + + if (!itemInspector.itemIsInWorld()) + { + LOG_DEBUG("playerbots.action.manage_inventory", "Item {} (template {}) is not in world.", itemGUID, itemTemplateLowGUID); + + return; + } + + const uint8_t itemSlot = itemInspector.getItemSlot(); + + if (itemSlot == NULL_SLOT) + { + LOG_DEBUG("playerbots.action.manage_inventory", "Item {} (template {}) is null slot.", itemGUID, itemTemplateLowGUID); + + return; + } + + if (itemInspector.itemIsInUnsafeContainer()) + { + LOG_DEBUG("playerbots.action.manage_inventory", "Item {} (template {}) is in unsafe container", itemGUID, itemTemplateLowGUID); + + return; + } + + const uint32_t itemClass = itemInspector.getCurrentItemClass(); + const uint32_t itemSubClass = itemInspector.getCurrentItemSubclass(); + + LOG_DEBUG("playerbots.action.manage_inventory", "processing item {}", std::to_string(itemTemplateLowGUID)); + + ClassMap::const_iterator classIterator = inspectorFactories.find(itemClass); + + if (classIterator == inspectorFactories.end()) + { + LOG_DEBUG("playerbots.action.manage_inventory", "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_DEBUG("playerbots.action.manage_inventory", "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); + this->itemActions.insert({itemGUID, action}); + + 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) +{ + LOG_DEBUG("playerbots.action.manage_inventory", "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 new file mode 100644 index 00000000000..f9106d63407 --- /dev/null +++ b/src/strategy/actions/player/inventory/ManageInventoryAction.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 + +#include "Action.h" +#include "Log.h" +#include "PlayerbotAI.h" +#include "ItemActionStruct.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_DEBUG("playerbots.action.manage_inventory", "Starting manage inventory isPossible evaluation"); + + const Player* const bot = botAI->GetBot(); + + if (bot == nullptr) + { + LOG_DEBUG("playerbots.action.manage_inventory", "Manage inventory impossible bot is nullptr"); + + return false; + } + + const std::string botName = bot->GetName(); + + if (!bot->IsInWorld()) + { + LOG_DEBUG("playerbots.action.manage_inventory", "Manage inventory impossible bot '{}' is non in world", botName); + + return false; + } + + const WorldSession* const session = bot->GetSession(); + + if (session == nullptr) + { + LOG_DEBUG("playerbots.action.manage_inventory", "Manage inventory impossible bot '{}' session is nullptr", botName); + + return false; + } + + if (session->isLogingOut()) + { + LOG_DEBUG("playerbots.action.manage_inventory", "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 uint32_t bagSlot); + void processItem(const uint64_t itemGUID); + template + void determineItemAction(const uint32_t botLowGUID, const uint64_t itemLowGUID); + void refillConsumables(); +}; 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