mirror of
https://github.com/brighton-chi/mod-aoe-loot.git
synced 2026-01-13 00:58:34 +00:00
try to fix quest item issues
This commit is contained in:
266
src/aoe_loot.cpp
266
src/aoe_loot.cpp
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user