From 6406c6acf03eaa1c8dc56980ebd13c18f8919c0b Mon Sep 17 00:00:00 2001 From: TerraByte Date: Tue, 15 Apr 2025 22:12:36 -0500 Subject: [PATCH] Update aoe_loot.cpp --- mod-aoe-loot/src/aoe_loot.cpp | 330 +++++++++++++++++++++++++--------- 1 file changed, 244 insertions(+), 86 deletions(-) diff --git a/mod-aoe-loot/src/aoe_loot.cpp b/mod-aoe-loot/src/aoe_loot.cpp index cfded64..d78d96b 100644 --- a/mod-aoe-loot/src/aoe_loot.cpp +++ b/mod-aoe-loot/src/aoe_loot.cpp @@ -19,8 +19,9 @@ using namespace Acore::ChatCommands; +using namespace WorldPackets; -bool AOELootServer::CanPacketReceive(WorldSession* session, WorldPacket& packet) +bool AoeLootServer::CanPacketReceive(WorldSession* session, WorldPacket& packet) { if (packet.GetOpcode() == CMSG_LOOT) { @@ -55,34 +56,211 @@ ChatCommandTable AoeLootCommandScript::GetCommands() const return playerAoeLootCommandTable; } +// Custom function to handle gold looting without distance checks +bool AoeLootCommandScript::ProcessLootMoney(Player* player, Creature* creature) +{ + if (!player || !creature) + return false; + + Loot* loot = &creature->loot; + if (!loot || loot->gold == 0) + return false; + + // Store gold amount before we modify it + uint32 goldAmount = loot->gold; + + // Handle group money distribution if needed + bool shareMoney = true; // Share by default for creature corpses + + if (shareMoney && player->GetGroup()) + { + Group* group = player->GetGroup(); + + std::vector playersNear; + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member) + continue; + + // For AOE looting, we don't do a distance check - just include all group members + playersNear.push_back(member); + } + + uint32 goldPerPlayer = uint32((loot->gold) / (playersNear.size())); + + for (std::vector::const_iterator i = playersNear.begin(); i != playersNear.end(); ++i) + { + (*i)->ModifyMoney(goldPerPlayer); + (*i)->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, goldPerPlayer); + + WorldPacket data(SMSG_LOOT_MONEY_NOTIFY, 4 + 1); + data << uint32(goldPerPlayer); + data << uint8(playersNear.size() > 1 ? 0 : 1); // 0 is "Your share is..." and 1 is "You loot..." + (*i)->GetSession()->SendPacket(&data); + } + } + else + { + // No group - give all gold to the player + player->ModifyMoney(loot->gold); + player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, loot->gold); + + WorldPacket data(SMSG_LOOT_MONEY_NOTIFY, 4 + 1); + data << uint32(loot->gold); + data << uint8(1); // "You loot..." + player->GetSession()->SendPacket(&data); + } + + // Mark the money as looted + loot->gold = 0; + loot->NotifyMoneyRemoved(); + + // Delete container if empty + if (loot->isLooted()) + { + // The loot release will be handled in the main function + } + + return true; +} + +bool AoeLootCommandScript::ProcessLootSlot(Player* player, ObjectGuid lguid, uint8 lootSlot) +{ + if (!player) + return false; + + Loot* loot = nullptr; + + // Get the loot object based on the GUID type. + if (lguid.IsGameObject()) + { + GameObject* go = player->GetMap()->GetGameObject(lguid); + + // Use increased distance for AOE looting + float aoeDistance = sConfigMgr->GetOption("AOELoot.Range", 55.0f); + + // Modified distance check + if (!go || ((go->GetOwnerGUID() != player->GetGUID() && + go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && + !go->IsWithinDistInMap(player, aoeDistance))) + { + // We're outside of range - silently fail when batch processing + return false; + } + + loot = &go->loot; + } + else if (lguid.IsItem()) + { + Item* pItem = player->GetItemByGuid(lguid); + if (!pItem) + return false; + + loot = &pItem->loot; + } + else if (lguid.IsCorpse()) + { + Corpse* bones = ObjectAccessor::GetCorpse(*player, lguid); + if (!bones) + return false; + + loot = &bones->loot; + } + else + { + Creature* creature = player->GetMap()->GetCreature(lguid); + + if (creature && creature->IsAlive()) + { + bool isPickpocketing = creature && creature->IsAlive() && (player->IsClass(CLASS_ROGUE, CLASS_CONTEXT_ABILITY) && creature->loot.loot_type == LOOT_PICKPOCKETING); + // Use regular interaction distance for pickpocketing + bool lootAllowed = creature && creature->IsAlive() == isPickpocketing; + // Modified distance check with appropriate range + if (!lootAllowed || !creature->IsWithinDistInMap(player, INTERACTION_DISTANCE)) + { + // We're outside of range - silently fail when batch processing + return false; + } + } + loot = &creature->loot; + } + + if (!loot) + return false; + + // Check if the item is already looted + QuestItem* qitem = nullptr; + QuestItem* ffaitem = nullptr; + QuestItem* conditem = nullptr; + LootItem* item = loot->LootItemInSlot(lootSlot, player, &qitem, &ffaitem, &conditem); + if (!item) + { + if (sConfigMgr->GetOption("AOELoot.Debug", true)) + { + LOG_DEBUG("module.aoe_loot", "No valid loot item found in slot {}", lootSlot); + } + return false; + } + + // Now call StoreLootItem with the validated slot + InventoryResult msg; + + // Use StoreLootItem directly + player->StoreLootItem(lootSlot, loot, msg); + if (msg == EQUIP_ERR_OK ) + { + // Successfully looted the item + loot->items[lootSlot].is_looted = true; + loot->unlootedCount--; + + // Notify the player about the looted item + if (sConfigMgr->GetOption("AOELoot.Debug", true)) + { + LOG_DEBUG("module.aoe_loot", "Successfully looted item (ID: {}) x{} from {}", item->itemid, static_cast(item->count), lguid.ToString()); + } + } + // Handle mail fallback for items + if (msg != EQUIP_ERR_OK && lguid.IsItem() && loot->loot_type != LOOT_CORPSE) + { + item->is_looted = true; + loot->NotifyItemRemoved(item->itemIndex); + loot->unlootedCount--; + + player->SendItemRetrievalMail(item->itemid, item->count); + } + + // Success + return true; +} + bool AoeLootCommandScript::HandleStartAoeLootCommand(ChatHandler* handler, Optional /*args*/) { if (!sConfigMgr->GetOption("AOELoot.Enable", true)) - return true; + return true; Player* player = handler->GetSession()->GetPlayer(); + if (!player) - return true; + return true; float range = sConfigMgr->GetOption("AOELoot.Range", 55.0); std::list nearbyCorpses; player->GetDeadCreatureListInGrid(nearbyCorpses, range); - uint32 totalItemsLooted = 0; - uint32 totalCopperLooted = 0; - uint32 totalCorpsesLooted = 0; - if (sConfigMgr->GetOption("AOELoot.Debug", true)) { LOG_DEBUG("module.aoe_loot", "AOE Loot: Found {} nearby corpses within range {}.", nearbyCorpses.size(), range); handler->PSendSysMessage(fmt::format("AOE Loot: Found {} nearby corpses within range {}.", nearbyCorpses.size(), range)); } + // Filter valid corpses first + std::list validCorpses; // Process each corpse one by one for (auto* creature : nearbyCorpses) { - if (!player || !creature || !player->IsInWorld()) + if (!player || !creature) { continue; } @@ -98,128 +276,108 @@ bool AoeLootCommandScript::HandleStartAoeLootCommand(ChatHandler* handler, Optio { if (sConfigMgr->GetOption("AOELoot.Debug", true)) { - LOG_DEBUG("module.aoe_loot", "AOE Loot: Skipping creature {} - not lootable", creature->GetGUID().ToString()); - handler->PSendSysMessage(fmt::format("AOE Loot: Skipping creature {} - not lootable", creature->GetGUID().ToString())); + LOG_DEBUG("module.aoe_loot", "AOE Loot: Skipping creature {} - ***NOT LOOTABLE***", creature->GetGUID().ToString()); + handler->PSendSysMessage(fmt::format("AOE Loot: Skipping creature {} - ***NOT LOOTABLE***", creature->GetGUID().ToString())); } continue; } // Additional permission check - if (player->GetMap()->Instanceable()) - { - if(!player->isAllowedToLoot(creature)) - { - if (sConfigMgr->GetOption("AOELoot.Debug", true)) - { - LOG_DEBUG("module.aoe_loot", "AOE Loot: Skipping creature {} - player not allowed to loot", creature->GetGUID().ToString()); - handler->PSendSysMessage(fmt::format("AOE Loot: Skipping creature {} - player not allowed to loot", creature->GetGUID().ToString())); - } - continue; - } - } - - // Double-check distance to prevent exploits - if (player->GetDistance(creature) > range) + if (player->GetMap()->Instanceable() && !player->isAllowedToLoot(creature)) { if (sConfigMgr->GetOption("AOELoot.Debug", true)) { - LOG_DEBUG("module.aoe_loot", "AOE Loot: Skipping creature {} - out of range", creature->GetGUID().ToString()); - handler->PSendSysMessage(fmt::format("AOE Loot: Skipping creature {} - out of range", creature->GetGUID().ToString())); + LOG_DEBUG("module.aoe_loot", "AOE Loot: Skipping creature {} - ***NOT YOUR LOOT***", creature->GetGUID().ToString()); + handler->PSendSysMessage(fmt::format("AOE Loot: Skipping creature {} - ***NOT YOUR LOOT***", creature->GetGUID().ToString())); } continue; } - - // Set this corpse as the current loot target - player->SetLootGUID(creature->GetGUID()); - + // Add to valid list of creatures to loot + validCorpses.push_back(creature); + + } + if (sConfigMgr->GetOption("AOELoot.Debug", true)) + { + LOG_DEBUG("module.aoe_loot", "AOE Loot: Found {} VALID nearby corpses within range {}.", validCorpses.size(), range); + handler->PSendSysMessage(fmt::format("AOE Loot: Found {} VALID nearby corpses within range {}.", validCorpses.size(), range)); + } + // Process all corpses silently and quickly + for (auto* creature : validCorpses) + { Loot* loot = &creature->loot; - + if (!loot) { continue; } - - // Process regular items - for (uint8 lootSlot = 0; lootSlot < loot->items.size(); ++lootSlot) - { - - // Store the loot item in the player's inventory - InventoryResult msg; - player->StoreLootItem(lootSlot, loot, msg); - - // Debug - if (sConfigMgr->GetOption("AOELoot.Debug", true)) - { - totalItemsLooted++; - - LOG_DEBUG("module.aoe_loot", "AOE Loot: Successfully looted item from slot {} (ID: {}) from {}", lootSlot, loot->items[lootSlot].itemid, creature->GetGUID().ToString()); - handler->PSendSysMessage(fmt::format("AOE Loot: Successfully looted item from slot {} (ID: {})", lootSlot, loot->items[lootSlot].itemid)); - } - } // Process quest items if (!loot->quest_items.empty()) { - // Calculate starting slot for quest items - uint8 firstQuestSlot = loot->items.size(); - // Try to loot each potential quest slot for (uint8 i = 0; i < loot->quest_items.size(); ++i) { - uint8 lootSlot = firstQuestSlot + i; - - // Store the loot item in the player's inventory - InventoryResult msg; - player->StoreLootItem(lootSlot, loot, msg); + uint8 lootSlot = loot->items.size() + i; + // Store the loot item in the player's inventory + AoeLootCommandScript::ProcessLootSlot(player, creature->GetGUID(), lootSlot); // Debug logging if (sConfigMgr->GetOption("AOELoot.Debug", true)) - { - totalItemsLooted++; - - LOG_DEBUG("module.aoe_loot", "AOE Loot: Successfully looted quest item from slot {}", lootSlot); - handler->PSendSysMessage(fmt::format("AOE Loot: Successfully looted quest item from slot {}", lootSlot)); - + { + LOG_DEBUG("module.aoe_loot", "AOE Loot: looted QUEST ITEM in slot {}", lootSlot); + handler->PSendSysMessage(fmt::format("AOE Loot: looted QUEST ITEM in slot {}", lootSlot)); } } } + + // Process regular items + for (uint8 lootSlot = 0; lootSlot < loot->items.size(); ++lootSlot) + { + player->SetLootGUID(creature->GetGUID()); + // Store the loot item in the player's inventory + AoeLootCommandScript::ProcessLootSlot(player, creature->GetGUID(), lootSlot); + + // Debug + if (sConfigMgr->GetOption("AOELoot.Debug", true)) + { + LOG_DEBUG("module.aoe_loot", "AOE Loot: looted item from slot {} (ID: {}) from {}", lootSlot, loot->items[lootSlot].itemid, creature->GetGUID().ToString()); + handler->PSendSysMessage(fmt::format("AOE Loot: looted item from slot {} (ID: {})", lootSlot, loot->items[lootSlot].itemid)); + } + } // Handle money with direct packet if (loot->gold > 0) { - WorldPacket moneyPacket(CMSG_LOOT_MONEY, 0); - player->GetSession()->HandleLootMoneyOpcode(moneyPacket); + // Store the gold amount before it gets cleared by HandleLootMoneyOpcode + uint32 goldAmount = loot->gold; + AoeLootCommandScript::ProcessLootMoney(player, creature); - // Debug - if (sConfigMgr->GetOption("AOELoot.Debug", true)) - { - totalCopperLooted += loot->gold; - LOG_DEBUG("module.aoe_loot", "AOE Loot: Looted {} copper from {}", loot->gold, creature->GetGUID().ToString()); - handler->PSendSysMessage(fmt::format("AOE Loot: Looted {} copper from {}", loot->gold, creature->GetGUID().ToString())); - } + + // Debug + if (sConfigMgr->GetOption("AOELoot.Debug", true)) + { + LOG_DEBUG("module.aoe_loot", "AOE Loot: Looted {} copper from {}", goldAmount, creature->GetGUID().ToString()); + handler->PSendSysMessage(fmt::format("AOE Loot: Looted {} copper from {}", goldAmount, creature->GetGUID().ToString())); + } } // Check if the corpse is now fully looted if (loot->isLooted()) { + WorldPacket releaseData(CMSG_LOOT_RELEASE, 8); + releaseData << creature->GetGUID(); + player->GetSession()->HandleLootReleaseOpcode(releaseData); // Cleanup the loot object from the corpse - creature->AllLootRemovedFromCorpse(); - creature->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE); - } - - - if (sConfigMgr->GetOption("AOELoot.Debug", true)) - { - LOG_DEBUG("module.aoe_loot", "AOE Loot: Summary - Looted {} items, {} gold from {} corpses", totalItemsLooted, totalCopperLooted, totalCorpsesLooted); - handler->PSendSysMessage(fmt::format("AOE Loot: Looted {} items and {} gold from {} corpses", totalItemsLooted, totalCopperLooted, totalCorpsesLooted)); + //creature->AllLootRemovedFromCorpse(); + //creature->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE); + // Force update the world state to reflect the loot being gone } } return true; } - // Copied from sudlud's "mod-aoe-loot" module. -void AOELootPlayer::OnPlayerLogin(Player* player) +void AoeLootPlayer::OnPlayerLogin(Player* player) { if (sConfigMgr->GetOption("AOELoot.Enable", true)) { @@ -232,7 +390,7 @@ void AOELootPlayer::OnPlayerLogin(Player* player) void AddSC_AoeLoot() { - new AOELootPlayer(); - new AOELootServer(); + new AoeLootPlayer(); + new AoeLootServer(); new AoeLootCommandScript(); }