ItemUsageValue: Handle items that create required items (#1091)

* Updated IsItemUsefulForQuest to handle items that create required quest items

* Debug output

* Removed unneeded botAI->

* Debug output in Calculate

* -> not .

* Comprehensive debug output for quest usage

* Corrected logic for lootable items when sync quest is enabled

* #include "LootObjectStack.h"

* Corrected call to IsItem

* Removed debug statements

* Reimplement ItemCount >= RequiredItemCount

* Modified CanLoot to loot all items when loot source is an item in their bags

* Set loot target when sending Open packet
This commit is contained in:
avirar
2025-03-25 00:47:25 +11:00
committed by GitHub
parent 47ffe37519
commit c83cf0706a
3 changed files with 91 additions and 11 deletions

View File

@@ -4,6 +4,9 @@
#include "WorldPacket.h"
#include "Player.h"
#include "ObjectMgr.h"
#include "LootObjectStack.h"
#include "AiObjectContext.h"
bool OpenItemAction::Execute(Event event)
{
bool foundOpenable = false;
@@ -27,6 +30,11 @@ void OpenItemAction::OpenItem(Item* item, uint8 bag, uint8 slot)
packet << bag << slot;
bot->GetSession()->HandleOpenItemOpcode(packet);
// Store the item GUID as the loot target
LootObject lootObject;
lootObject.guid = item->GetGUID();
botAI->GetAiObjectContext()->GetValue<LootObject>("loot target")->Set(lootObject);
std::ostringstream out;
out << "Opened item: " << item->GetTemplate()->Name1;
botAI->TellMaster(out.str());

View File

@@ -8,6 +8,7 @@
#include "AiFactory.h"
#include "ChatHelper.h"
#include "GuildTaskMgr.h"
#include "LootObjectStack.h"
#include "PlayerbotAIConfig.h"
#include "PlayerbotFactory.h"
#include "Playerbots.h"
@@ -112,12 +113,38 @@ ItemUsage ItemUsageValue::Calculate()
return ITEM_USAGE_DISENCHANT;
}
// While sync is on, do not loot quest items that are also Useful for master. Master
if (!botAI->GetMaster() || !sPlayerbotAIConfig->syncQuestWithPlayer ||
!IsItemUsefulForQuest(botAI->GetMaster(), proto))
if (IsItemUsefulForQuest(bot, proto))
return ITEM_USAGE_QUEST;
Player* master = botAI->GetMaster();
bool isSelfBot = (master == bot);
bool botNeedsItemForQuest = IsItemUsefulForQuest(bot, proto);
bool masterNeedsItemForQuest = master && sPlayerbotAIConfig->syncQuestWithPlayer && IsItemUsefulForQuest(master, proto);
// Identify the source of loot
LootObject lootObject = AI_VALUE(LootObject, "loot target");
// Get GUID of loot source
ObjectGuid lootGuid = lootObject.guid;
// Check if loot source is an item
bool isLootFromItem = lootGuid.IsItem();
// If the loot is from an item in the bots bags, ignore syncQuestWithPlayer
if (isLootFromItem && botNeedsItemForQuest)
{
return ITEM_USAGE_QUEST;
}
// If the bot is NOT acting alone and the master needs this quest item, defer to the master
if (!isSelfBot && masterNeedsItemForQuest)
{
return ITEM_USAGE_NONE;
}
// If the bot itself needs the item for a quest, allow looting
if (botNeedsItemForQuest)
{
return ITEM_USAGE_QUEST;
}
if (proto->Class == ITEM_CLASS_PROJECTILE && bot->CanUseItem(proto) == EQUIP_ERR_OK)
{
if (bot->getClass() == CLASS_HUNTER || bot->getClass() == CLASS_ROGUE || bot->getClass() == CLASS_WARRIOR)
@@ -464,6 +491,10 @@ uint32 ItemUsageValue::GetSmallestBagSize()
bool ItemUsageValue::IsItemUsefulForQuest(Player* player, ItemTemplate const* proto)
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
if (!botAI)
return false;
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
{
uint32 entry = player->GetQuestSlotQuestId(slot);
@@ -471,20 +502,52 @@ bool ItemUsageValue::IsItemUsefulForQuest(Player* player, ItemTemplate const* pr
if (!quest)
continue;
// Check if the item itself is needed for the quest
for (uint8 i = 0; i < 4; i++)
{
if (quest->RequiredItemId[i] != proto->ItemId)
if (quest->RequiredItemId[i] == proto->ItemId)
{
if (AI_VALUE2(uint32, "item count", proto->Name1) >= quest->RequiredItemCount[i])
continue;
return true; // Item is directly required for a quest
}
}
// Check if the item has spells that create a required quest item
for (uint8 i = 0; i < MAX_ITEM_SPELLS; i++)
{
uint32 spellId = proto->Spells[i].SpellId;
if (!spellId)
continue;
if (GET_PLAYERBOT_AI(player) &&
AI_VALUE2(uint32, "item count", proto->Name1) >= quest->RequiredItemCount[i])
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo)
continue;
return true;
for (uint8 effectIndex = 0; effectIndex < MAX_SPELL_EFFECTS; effectIndex++)
{
if (spellInfo->Effects[effectIndex].Effect == SPELL_EFFECT_CREATE_ITEM)
{
uint32 createdItemId = spellInfo->Effects[effectIndex].ItemType;
// Check if the created item is required for a quest
for (uint8 j = 0; j < 4; j++)
{
if (quest->RequiredItemId[j] == createdItemId)
{
if (AI_VALUE2(uint32, "item count", createdItemId) >= quest->RequiredItemCount[j])
continue;
return true; // Item is useful because it creates a required quest item
}
}
}
}
}
}
return false;
return false; // Item is not useful for any active quests
}
bool ItemUsageValue::IsItemNeededForSkill(ItemTemplate const* proto)

View File

@@ -15,6 +15,15 @@ class NormalLootStrategy : public LootStrategy
public:
bool CanLoot(ItemTemplate const* proto, AiObjectContext* context) override
{
// Identify the source of loot, loot it if the source is an item in the bots inventory
LootObject lootObject = AI_VALUE(LootObject, "loot target");
ObjectGuid lootGuid = lootObject.guid;
if (lootGuid.IsItem())
{
return true;
}
// Otherwise, continue with the normal loot logic
std::ostringstream out;
out << proto->ItemId;
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", out.str());