try to fix quest item issues

This commit is contained in:
crow
2025-08-28 21:57:06 -05:00
parent 801b0d1fe6
commit b3d29d861e

View File

@@ -31,6 +31,104 @@ bool IsAoeLootModuleEnabled()
return sConfigMgr->GetOption<bool>("AoeLoot.EnableMod", true);
}
bool IsPlayerEligibleForQuestItem(Player* player, LootItem const& item)
{
ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(item.itemid);
LOG_INFO("module.aoe_loot", "[AOE LOOT] Eligibility check: player={} item={} class={} startQuest={} maxCount={} questStatus={} hasItem={} flags={} isQuestStarter={}",
player->GetName(),
item.itemid,
itemTemplate ? itemTemplate->Class : -1,
itemTemplate ? itemTemplate->StartQuest : 0,
itemTemplate ? itemTemplate->MaxCount : 0,
itemTemplate ? player->GetQuestStatus(itemTemplate->StartQuest) : -1,
itemTemplate ? player->HasItemCount(item.itemid, itemTemplate->MaxCount) : false,
itemTemplate ? itemTemplate->Flags : 0,
itemTemplate ? itemTemplate->StartQuest != 0 : false);
if (!itemTemplate || itemTemplate->Class != ITEM_CLASS_QUEST)
{
LOG_INFO("module.aoe_loot", "[AOE LOOT] Not a quest item or missing template: item={}", item.itemid);
return false;
}
// For quest starter items
if (itemTemplate->StartQuest)
{
uint32 prevQuestId = 0;
if (Quest const* startQuest = sObjectMgr->GetQuestTemplate(itemTemplate->StartQuest))
prevQuestId = startQuest->GetPrevQuestId();
LOG_INFO("module.aoe_loot", "[AOE LOOT] Quest starter item: player={} item={} questStatus={} hasItem={} prevQuestId={} prevQuestReward={}",
player->GetName(),
item.itemid,
player->GetQuestStatus(itemTemplate->StartQuest),
player->HasItemCount(item.itemid, itemTemplate->MaxCount),
prevQuestId,
prevQuestId ? player->GetQuestRewardStatus(prevQuestId) : 1);
// Already completed the quest
if (player->GetQuestStatus(itemTemplate->StartQuest) != QUEST_STATUS_NONE)
return false;
// Already has the item and it's unique
if (itemTemplate->MaxCount && player->HasItemCount(item.itemid, itemTemplate->MaxCount))
return false;
// Has not completed prerequisite quest
if (prevQuestId && !player->GetQuestRewardStatus(prevQuestId))
return false;
return true;
}
// For normal quest items (objectives)
std::vector<uint32> questIds;
for (auto const& questPair : sObjectMgr->GetQuestTemplates())
{
Quest const* quest = questPair.second;
if (!quest)
continue;
for (uint32 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
{
if (quest->RequiredItemId[i] == item.itemid)
{
questIds.push_back(quest->GetQuestId());
break;
}
}
}
LOG_INFO("module.aoe_loot", "Objective quest item: player={} item={} foundQuests={}", player->GetName(), item.itemid, questIds.size());
for (uint32 questId : questIds)
{
LOG_INFO("module.aoe_loot", "Checking quest status: player={} item={} questId={} status={}", player->GetName(), item.itemid, questId, player->GetQuestStatus(questId));
if (player->GetQuestStatus(questId) == QUEST_STATUS_INCOMPLETE)
{
uint32 requiredCount = 1;
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
if (quest)
{
for (uint32 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
{
if (quest->RequiredItemId[i] == item.itemid)
{
requiredCount = quest->RequiredItemCount[i];
break;
}
}
}
LOG_INFO("module.aoe_loot", "Item count check: player={} item={} have={} required={}", player->GetName(), item.itemid, player->GetItemCount(item.itemid, true), requiredCount);
if (player->GetItemCount(item.itemid, true) < requiredCount)
return true;
}
}
LOG_INFO("module.aoe_loot", "Player not eligible for quest item: player={} item={}", player->GetName(), item.itemid);
return false;
}
// Helper function to get configured loot method from config value
LootMethod GetLootMethodFromConfig(uint32 configValue)
{
@@ -495,9 +593,59 @@ bool AoeLootCommandScript::ProcessSingleLootSlot(Player* player, ObjectGuid lgui
loot = &creature->loot;
}
sScriptMgr->OnPlayerAfterCreatureLoot(player);
if (!loot)
// Handle quest item slots
if (lootSlot >= loot->items.size())
{
// Calculate quest and FFA quest item slot
uint8 questItemOffset = loot->items.size();
const QuestItemMap& questItems = loot->GetPlayerQuestItems();
auto q_itr = questItems.find(player->GetGUID());
if (q_itr != questItems.end())
{
const QuestItemList* qlist = q_itr->second;
uint8 questCount = qlist->size();
if (lootSlot < questItemOffset + questCount)
{
uint8 questIndex = lootSlot - questItemOffset;
const QuestItem& qitem = (*qlist)[questIndex];
if (!qitem.is_looted)
{
if (qitem.index < loot->quest_items.size()) {
LootItem& li = loot->quest_items[qitem.index];
player->AddItem(li.itemid, li.count);
const_cast<QuestItem&>(qitem).is_looted = true;
LOG_INFO("module.aoe_loot", "[AOE LOOT] Player looted quest item: player={} item={} count={} slot={}", player->GetName(), li.itemid, uint32(li.count), uint32(questIndex));
return true;
}
}
}
}
// FFA quest items
const QuestItemMap& ffaItems = loot->GetPlayerFFAItems();
auto ffa_itr = ffaItems.find(player->GetGUID());
if (ffa_itr != ffaItems.end())
{
const QuestItemList* flist = ffa_itr->second;
uint8 ffaOffset = questItemOffset + (q_itr != questItems.end() ? q_itr->second->size() : 0);
uint8 ffaCount = flist->size();
if (lootSlot < ffaOffset + ffaCount)
{
uint8 ffaIndex = lootSlot - ffaOffset;
const QuestItem& fitem = (*flist)[ffaIndex];
if (!fitem.is_looted)
{
if (fitem.index < loot->quest_items.size()) {
LootItem& li = loot->quest_items[fitem.index];
player->AddItem(li.itemid, li.count);
const_cast<QuestItem&>(fitem).is_looted = true;
LOG_INFO("module.aoe_loot", "[AOE LOOT] Player looted FFA quest item: player={} item={} count={} slot={}", player->GetName(), li.itemid, uint32(li.count), uint32(ffaIndex));
return true;
}
}
}
}
return false;
}
// --- Begin standard loot logic for solo/group ---
Group* group = player->GetGroup();
@@ -511,12 +659,6 @@ bool AoeLootCommandScript::ProcessSingleLootSlot(Player* player, ObjectGuid lgui
LootMethod lootMethod = GROUP_LOOT;
uint8 groupLootThreshold = 2; // Use group's configured threshold, fallback to Uncommon
// Bounds check for loot slot
if (lootSlot >= loot->items.size())
{
return false;
}
isFFA = loot->items[lootSlot].freeforall;
if (group)
@@ -526,27 +668,18 @@ bool AoeLootCommandScript::ProcessSingleLootSlot(Player* player, ObjectGuid lgui
isMasterLooter = (lootMethod == MASTER_LOOT);
isRoundRobin = (lootMethod == ROUND_ROBIN);
isGroupLoot = (lootMethod == GROUP_LOOT || lootMethod == NEED_BEFORE_GREED);
// Bounds check before accessing loot item
if (lootSlot < loot->items.size())
{
ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(loot->items[lootSlot].itemid);
isThreshold = (itemTemplate && itemTemplate->Quality >= groupLootThreshold);
}
ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(loot->items[lootSlot].itemid);
isThreshold = (itemTemplate && itemTemplate->Quality >= groupLootThreshold);
}
// If in group and item meets group loot threshold, trigger group roll
if (group && isGroupLoot && isThreshold && !isFFA && !isMasterLooter)
{
// Use the existing game's SendLootStartRoll - it doesn't have distance checks
if (lootSlot < loot->items.size() && !loot->items[lootSlot].is_blocked)
if (!loot->items[lootSlot].is_blocked)
{
// Create Roll object using the constructor: Roll(ObjectGuid _guid, LootItem const &li)
Roll roll(lguid, loot->items[lootSlot]);
roll.itemSlot = lootSlot;
roll.setLoot(loot);
// Initialize player votes for group members
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
{
Player* member = itr->GetSource();
@@ -556,15 +689,12 @@ bool AoeLootCommandScript::ProcessSingleLootSlot(Player* player, ObjectGuid lgui
roll.totalPlayersRolling++;
}
}
// Call SendLootStartRoll using the group's configured roll timeout
group->SendLootStartRoll(60, player->GetMapId(), roll);
}
return true;
}
else if (group && isMasterLooter && !isFFA)
{
// Master looter: only master looter can loot
if (group->GetMasterLooterGuid() != player->GetGUID())
{
player->SendLootError(lguid, LOOT_ERROR_MASTER_OTHER);
@@ -573,12 +703,31 @@ bool AoeLootCommandScript::ProcessSingleLootSlot(Player* player, ObjectGuid lgui
}
else if (group && isRoundRobin && loot->roundRobinPlayer && loot->roundRobinPlayer != player->GetGUID())
{
// Round robin: only designated player can loot
return false;
}
// If not blocked by group loot, proceed to loot the item
sScriptMgr->OnPlayerAfterCreatureLoot(player);
if (!loot)
return false;
bool isLootedBefore = loot->items[lootSlot].is_looted;
LOG_INFO("module.aoe_loot", "Before StoreLootItem: player={} item={} slot={} itemCount={} is_looted={}",
player->GetName(),
loot->items[lootSlot].itemid,
lootSlot,
player->GetItemCount(loot->items[lootSlot].itemid, true),
isLootedBefore);
lootItem = player->StoreLootItem(lootSlot, loot, msg);
bool isLootedAfter = loot->items[lootSlot].is_looted;
LOG_INFO("module.aoe_loot", "After StoreLootItem: player={} item={} slot={} itemCount={} is_looted={} result={}",
player->GetName(),
loot->items[lootSlot].itemid,
lootSlot,
player->GetItemCount(loot->items[lootSlot].itemid, true),
isLootedAfter,
msg);
if (msg != EQUIP_ERR_OK && lguid.IsItem() && loot->loot_type != LOOT_CORPSE)
{
lootItem->is_looted = true;
@@ -587,7 +736,6 @@ bool AoeLootCommandScript::ProcessSingleLootSlot(Player* player, ObjectGuid lgui
player->SendItemRetrievalMail(lootItem->itemid, lootItem->count);
}
// If player is removing the last LootItem, delete the empty container
if (loot->isLooted() && lguid.IsItem())
{
ReleaseAndCleanupLoot(lguid, player, loot);
@@ -597,10 +745,10 @@ bool AoeLootCommandScript::ProcessSingleLootSlot(Player* player, ObjectGuid lgui
bool AoeLootCommandScript::TriggerAoeLootCommand(ChatHandler* handler, Optional<std::string> /*args*/)
{
if (!IsAoeLootModuleEnabled())
return true;
Player* player = handler->GetSession()->GetPlayer();
LOG_INFO("module.aoe_loot", "[AOE LOOT] TriggerAoeLootCommand: player={} starting AoE loot", player ? player->GetName() : "null");
if (!IsAoeLootModuleEnabled())
return true;
if (!player)
return true;
@@ -677,13 +825,70 @@ bool AoeLootCommandScript::TriggerAoeLootCommand(ChatHandler* handler, Optional<
}
player->SetLootGUID(lguid);
LOG_INFO("module.aoe_loot", "[AOE LOOT] SetLootGUID: player={} corpse={} loot window should open", player->GetName(), creature->GetName());
// Process all loot items
// Process all normal loot items
for (uint8 lootSlot = 0; lootSlot < loot->items.size(); ++lootSlot)
{
ProcessSingleLootSlot(player, lguid, lootSlot);
}
// Explicitly process per-player quest items
const QuestItemMap& questItems = loot->GetPlayerQuestItems();
auto q_itr = questItems.find(player->GetGUID());
if (q_itr != questItems.end())
{
const QuestItemList* qlist = q_itr->second;
for (uint8 i = 0; i < qlist->size(); ++i)
{
// Quest item slots follow after normal loot slots
uint8 questSlot = loot->items.size() + i;
ProcessSingleLootSlot(player, lguid, questSlot);
LOG_INFO("module.aoe_loot", "[AOE LOOT] Processed quest item in slot {}", questSlot);
}
}
// Explicitly process per-player FFA quest items
const QuestItemMap& ffaItems = loot->GetPlayerFFAItems();
auto ffa_itr = ffaItems.find(player->GetGUID());
if (ffa_itr != ffaItems.end())
{
const QuestItemList* flist = ffa_itr->second;
for (uint8 i = 0; i < flist->size(); ++i)
{
// FFA item slots follow after normal loot slots and quest item slots
uint8 ffaSlot = loot->items.size() + (q_itr != questItems.end() ? q_itr->second->size() : 0) + i;
ProcessSingleLootSlot(player, lguid, ffaSlot);
LOG_INFO("module.aoe_loot", "[AOE LOOT] Processed FFA quest item in slot {}", ffaSlot);
}
}
// After processing all slots, log how many lootable items remain
size_t lootableCount = 0;
for (const auto& item : loot->items)
{
if (!item.is_looted)
++lootableCount;
}
// Also count quest items and FFA quest items
if (q_itr != questItems.end())
{
for (const auto& item : *q_itr->second)
{
if (!item.is_looted)
++lootableCount;
}
}
if (ffa_itr != ffaItems.end())
{
for (const auto& item : *ffa_itr->second)
{
if (!item.is_looted)
++lootableCount;
}
}
LOG_INFO("module.aoe_loot", "[AOE LOOT] After processing: player={} corpse={} lootable_items={}", player->GetName(), creature->GetName(), lootableCount);
// Handle money
if (loot->gold > 0)
{
@@ -694,6 +899,7 @@ bool AoeLootCommandScript::TriggerAoeLootCommand(ChatHandler* handler, Optional<
if (loot->isLooted())
{
ReleaseAndCleanupLoot(lguid, player, loot);
LOG_INFO("module.aoe_loot", "[AOE LOOT] ReleaseAndCleanupLoot: player={} corpse={} loot released", player->GetName(), creature->GetName());
}
}
return true;