diff --git a/src/aoe_loot.cpp b/src/aoe_loot.cpp index 5ae4774..dda65a3 100644 --- a/src/aoe_loot.cpp +++ b/src/aoe_loot.cpp @@ -16,87 +16,176 @@ */ #include "aoe_loot.h" +#include +#include void AOELootPlayer::OnPlayerLogin(Player* player) { + if (!player) + return; + if (sConfigMgr->GetOption("AOELoot.Enable", true)) { if (sConfigMgr->GetOption("AOELoot.Message", true)) - ChatHandler(player->GetSession()).PSendSysMessage(AOE_ACORE_STRING_MESSAGE); + { + if (WorldSession* session = player->GetSession()) + ChatHandler(session).PSendSysMessage(AOE_ACORE_STRING_MESSAGE); + } } } bool AOELootServer::CanPacketReceive(WorldSession* session, WorldPacket& packet) { - if (packet.GetOpcode() == CMSG_LOOT) - { - Player* player = session->GetPlayer(); + // Only handle loot packets + if (packet.GetOpcode() != CMSG_LOOT) + return true; - if (!sConfigMgr->GetOption("AOELoot.Enable", true)) - return true; + // Basic validation checks + if (!session) + return true; - if (player->GetGroup() && !sConfigMgr->GetOption("AOELoot.Group", true)) - return true; + Player* player = session->GetPlayer(); + if (!player) + return true; - float range = sConfigMgr->GetOption("AOELoot.Range", 55.0); + // Check if module is enabled + if (!sConfigMgr->GetOption("AOELoot.Enable", true)) + return true; - std::list lootcreature; player->GetDeadCreatureListInGrid(lootcreature, range); + // Check group settings + if (player->GetGroup() && !sConfigMgr->GetOption("AOELoot.Group", true)) + return true; - ObjectGuid guid; packet >> guid; + // Get configured loot range + float range = sConfigMgr->GetOption("AOELoot.Range", 30.0f); - Loot* mainloot = &(player->GetMap()->GetCreature(guid))->loot; + // Limit range to reasonable values + if (range < 5.0f) + range = 5.0f; - for (auto itr = lootcreature.begin(); itr != lootcreature.end(); ++itr) + if (range > 100.0f) + range = 100.0f; + + // Read target GUID from packet + ObjectGuid targetGuid; + packet >> targetGuid; + + if (!targetGuid) + return true; + + // Get target creature + Creature* mainCreature = player->GetMap()->GetCreature(targetGuid); + if (!mainCreature) + return true; + + // Check if main creature has loot + if (!mainCreature->HasDynamicFlag(UNIT_DYNFLAG_LOOTABLE)) + return true; + + // Get nearby corpses + std::list nearbyCorpses; + player->GetDeadCreatureListInGrid(nearbyCorpses, range); + + // Remove invalid corpses and main target + nearbyCorpses.remove_if([&](Creature* c) { - Creature* creature = *itr; + return !c || + c->GetGUID() == targetGuid || + !c->HasDynamicFlag(UNIT_DYNFLAG_LOOTABLE) || + !player->isAllowedToLoot(c); + }); - // Prevent infiny add items - if (creature->GetGUID() == guid) - continue; - - // Prevent steal loot - if (!player->GetMap()->Instanceable()) - if (!player->isAllowedToLoot(creature)) - continue; - - // Max 15 items per creature - if (mainloot->items.size() + mainloot->quest_items.size() > 15) - break; - - Loot* loot = &(*itr)->loot; - - // FILL QITEMS - if (!loot->quest_items.empty()) - { - mainloot->items.insert(mainloot->items.end(), loot->quest_items.begin(), loot->quest_items.end()); - loot->quest_items.clear(); - } - - // FILL GOLD - if (loot->gold != 0) - { - mainloot->gold += loot->gold; - loot->gold = 0; - } - - // FILL ITEMS - if (!loot->items.empty()) - { - mainloot->items.insert(mainloot->items.end(), loot->items.begin(), loot->items.end()); - loot->items.clear(); - } - - // Set flag for skinning - if (loot->items.empty() && loot->quest_items.empty()) - { - creature->AllLootRemovedFromCorpse(); - creature->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE); - } - } - - player->SendLoot(guid, LOOT_CORPSE); + // If no other corpses, process normally + if (nearbyCorpses.empty()) + { + player->SendLoot(targetGuid, LOOT_CORPSE); return false; } - return true; + // Get main loot + Loot* mainLoot = &mainCreature->loot; + + // Limit number of corpses to process + size_t const maxCorpses = 10; // set to 10 to improve stability + size_t processedCorpses = 0; + + // Track total gold to merge + uint32 totalGold = mainLoot->gold; + + // Collect all items to merge (don't modify main loot directly) + std::vector itemsToAdd; + std::vector questItemsToAdd; + + for (Creature* creature : nearbyCorpses) + { + if (processedCorpses >= maxCorpses) + break; + + if (!creature) + continue; + + Loot* loot = &creature->loot; + + // Skip already looted corpses + if (loot->isLooted()) + continue; + + // Collect gold + if (loot->gold > 0) + { + // Prevent overflow + if (totalGold < (std::numeric_limits::max() - loot->gold)) + totalGold += loot->gold; + } + + // Collect regular items + for (size_t i = 0; i < loot->items.size(); ++i) + { + // Check if there's still space + if ((mainLoot->items.size() + itemsToAdd.size() + mainLoot->quest_items.size() + questItemsToAdd.size()) >= 15) + break; + + itemsToAdd.push_back(loot->items[i]); + } + + // Collect quest items + for (size_t i = 0; i < loot->quest_items.size(); ++i) + { + // Check if there's still space + if ((mainLoot->items.size() + itemsToAdd.size() + mainLoot->quest_items.size() + questItemsToAdd.size()) >= 15) + break; + + questItemsToAdd.push_back(loot->quest_items[i]); + } + + // Clear source loot (but don't modify vector directly) + loot->clear(); + creature->AllLootRemovedFromCorpse(); + creature->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE); + + processedCorpses++; + } + + // Now safely add collected items to main loot + // Update gold + mainLoot->gold = totalGold; + + // Add regular items + for (const auto& item : itemsToAdd) + { + if (mainLoot->items.size() < 15) + mainLoot->items.push_back(item); + } + + // Add quest items + for (const auto& item : questItemsToAdd) + { + if (mainLoot->quest_items.size() < 15) + mainLoot->quest_items.push_back(item); + } + + // Send merged loot window + player->SendLoot(targetGuid, LOOT_CORPSE); + + return false; } diff --git a/src/aoe_loot.h b/src/aoe_loot.h index a3bc300..0e73987 100644 --- a/src/aoe_loot.h +++ b/src/aoe_loot.h @@ -23,6 +23,16 @@ #include "Chat.h" #include "Player.h" #include "ScriptedGossip.h" +#include "Group.h" +#include "LootMgr.h" +#include "Creature.h" +#include "Log.h" +#include +#include +#include + + // Maximum loot items count +constexpr size_t MAX_LOOT_ITEMS = 16; enum AoeLootString { @@ -33,7 +43,7 @@ enum AoeLootString class AOELootPlayer : public PlayerScript { public: - AOELootPlayer() : PlayerScript("AOELootPlayer") { } + AOELootPlayer() : PlayerScript("AOELootPlayer") {} void OnPlayerLogin(Player* player) override; }; @@ -41,9 +51,53 @@ public: class AOELootServer : public ServerScript { public: - AOELootServer() : ServerScript("AOELootServer") { } + AOELootServer() : ServerScript("AOELootServer") {} bool CanPacketReceive(WorldSession* session, WorldPacket& packet) override; + +private: + // Helper function - Check if loot is valid + bool IsLootValid(Loot* loot) const; + + // Check if loot can be merged + bool CanMergeLoot(Player* player, Creature* creature) const; + + // Safely get item count + size_t GetSafeItemCount(Loot* loot) const; + + // Safely merge loot items + bool SafeMergeLootItems(Loot* mainLoot, Loot* sourceLoot, size_t& remainingSlots); +}; + +// Configuration options structure (optional, for better config management) +struct AOELootConfig +{ + bool enabled = true; + bool messageOnLogin = true; + bool allowInGroup = true; + float range = 55.0f; + uint32 maxCorpses = 20; + + static AOELootConfig* instance() + { + static AOELootConfig instance; + return &instance; + } + + void Load() + { + enabled = sConfigMgr->GetOption("AOELoot.Enable", true); + messageOnLogin = sConfigMgr->GetOption("AOELoot.Message", true); + allowInGroup = sConfigMgr->GetOption("AOELoot.Group", true); + range = sConfigMgr->GetOption("AOELoot.Range", 55.0f); + maxCorpses = sConfigMgr->GetOption("AOELoot.MaxCorpses", 20); + + // Validate configuration values + if (range < 5.0f) range = 5.0f; + if (range > 100.0f) range = 100.0f; + if (maxCorpses < 1) maxCorpses = 1; + if (maxCorpses > 50) maxCorpses = 50; + } }; void AddSC_AoeLoot()