diff --git a/.vs/ProjectSettings.json b/.vs/ProjectSettings.json new file mode 100644 index 0000000..0cf5ea5 --- /dev/null +++ b/.vs/ProjectSettings.json @@ -0,0 +1,3 @@ +{ + "CurrentProjectSetting": "No Configurations" +} \ No newline at end of file diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json new file mode 100644 index 0000000..0904703 --- /dev/null +++ b/.vs/VSWorkspaceState.json @@ -0,0 +1,9 @@ +{ + "ExpandedNodes": [ + "", + "\\conf", + "\\src" + ], + "SelectedNode": "\\src\\aoe_loot.h", + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/.vs/mod-aoe-loot/CopilotIndices/17.14.995.13737/CodeChunks.db b/.vs/mod-aoe-loot/CopilotIndices/17.14.995.13737/CodeChunks.db new file mode 100644 index 0000000..19f4363 Binary files /dev/null and b/.vs/mod-aoe-loot/CopilotIndices/17.14.995.13737/CodeChunks.db differ diff --git a/.vs/mod-aoe-loot/CopilotIndices/17.14.995.13737/SemanticSymbols.db b/.vs/mod-aoe-loot/CopilotIndices/17.14.995.13737/SemanticSymbols.db new file mode 100644 index 0000000..23c4a42 Binary files /dev/null and b/.vs/mod-aoe-loot/CopilotIndices/17.14.995.13737/SemanticSymbols.db differ diff --git a/.vs/mod-aoe-loot/FileContentIndex/b87e705f-f51a-4f0f-9c3c-34b3a77d1f47.vsidx b/.vs/mod-aoe-loot/FileContentIndex/b87e705f-f51a-4f0f-9c3c-34b3a77d1f47.vsidx new file mode 100644 index 0000000..d9f2168 Binary files /dev/null and b/.vs/mod-aoe-loot/FileContentIndex/b87e705f-f51a-4f0f-9c3c-34b3a77d1f47.vsidx differ diff --git a/.vs/mod-aoe-loot/copilot-chat/980cb370/sessions/0ce407e5-99fb-4dc1-9926-afc73349bc9e b/.vs/mod-aoe-loot/copilot-chat/980cb370/sessions/0ce407e5-99fb-4dc1-9926-afc73349bc9e new file mode 100644 index 0000000..202fc16 Binary files /dev/null and b/.vs/mod-aoe-loot/copilot-chat/980cb370/sessions/0ce407e5-99fb-4dc1-9926-afc73349bc9e differ diff --git a/.vs/mod-aoe-loot/v17/.wsuo b/.vs/mod-aoe-loot/v17/.wsuo new file mode 100644 index 0000000..b8fbac8 Binary files /dev/null and b/.vs/mod-aoe-loot/v17/.wsuo differ diff --git a/.vs/mod-aoe-loot/v17/Browse.VC.db b/.vs/mod-aoe-loot/v17/Browse.VC.db new file mode 100644 index 0000000..7079ba1 Binary files /dev/null and b/.vs/mod-aoe-loot/v17/Browse.VC.db differ diff --git a/.vs/mod-aoe-loot/v17/DocumentLayout.json b/.vs/mod-aoe-loot/v17/DocumentLayout.json new file mode 100644 index 0000000..76450c1 --- /dev/null +++ b/.vs/mod-aoe-loot/v17/DocumentLayout.json @@ -0,0 +1,71 @@ +{ + "Version": 1, + "WorkspaceRootPath": "C:\\Azerothcore (WotLK \u002B Playerbots)\\azerothcore-wotlk\\modules\\mod-aoe-loot\\", + "Documents": [ + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Azerothcore (WotLK \u002B Playerbots)\\azerothcore-wotlk\\modules\\mod-aoe-loot\\src\\aoe_loot.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:src\\aoe_loot.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Azerothcore (WotLK \u002B Playerbots)\\azerothcore-wotlk\\modules\\mod-aoe-loot\\conf\\mod_aoe_loot.conf.dist||{8B382828-6202-11D1-8870-0000F87579D2}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:conf\\mod_aoe_loot.conf.dist||{8B382828-6202-11D1-8870-0000F87579D2}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Azerothcore (WotLK \u002B Playerbots)\\azerothcore-wotlk\\modules\\mod-aoe-loot\\src\\aoe_loot.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:src\\aoe_loot.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}" + } + ], + "DocumentGroupContainers": [ + { + "Orientation": 0, + "VerticalTabListWidth": 256, + "DocumentGroups": [ + { + "DockedWidth": 200, + "SelectedChildIndex": 2, + "Children": [ + { + "$type": "Document", + "DocumentIndex": 2, + "Title": "aoe_loot.h", + "DocumentMoniker": "C:\\Azerothcore (WotLK \u002B Playerbots)\\azerothcore-wotlk\\modules\\mod-aoe-loot\\src\\aoe_loot.h", + "RelativeDocumentMoniker": "src\\aoe_loot.h", + "ToolTip": "C:\\Azerothcore (WotLK \u002B Playerbots)\\azerothcore-wotlk\\modules\\mod-aoe-loot\\src\\aoe_loot.h", + "RelativeToolTip": "src\\aoe_loot.h", + "ViewState": "AgIAACcAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000680|", + "WhenOpened": "2025-08-16T01:59:55.31Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 1, + "Title": "mod_aoe_loot.conf.dist", + "DocumentMoniker": "C:\\Azerothcore (WotLK \u002B Playerbots)\\azerothcore-wotlk\\modules\\mod-aoe-loot\\conf\\mod_aoe_loot.conf.dist", + "RelativeDocumentMoniker": "conf\\mod_aoe_loot.conf.dist", + "ToolTip": "C:\\Azerothcore (WotLK \u002B Playerbots)\\azerothcore-wotlk\\modules\\mod-aoe-loot\\conf\\mod_aoe_loot.conf.dist", + "RelativeToolTip": "conf\\mod_aoe_loot.conf.dist", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAAcAAAAgAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001001|", + "WhenOpened": "2025-08-16T01:40:45.459Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 0, + "Title": "aoe_loot.cpp", + "DocumentMoniker": "C:\\Azerothcore (WotLK \u002B Playerbots)\\azerothcore-wotlk\\modules\\mod-aoe-loot\\src\\aoe_loot.cpp", + "RelativeDocumentMoniker": "src\\aoe_loot.cpp", + "ToolTip": "C:\\Azerothcore (WotLK \u002B Playerbots)\\azerothcore-wotlk\\modules\\mod-aoe-loot\\src\\aoe_loot.cpp", + "RelativeToolTip": "src\\aoe_loot.cpp", + "ViewState": "AgIAAAYAAAAAAAAAAAAAACYAAAADAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000677|", + "WhenOpened": "2025-08-16T01:40:05.831Z", + "EditorCaption": "" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/.vs/mod-aoe-loot/v17/Solution.VC.db b/.vs/mod-aoe-loot/v17/Solution.VC.db new file mode 100644 index 0000000..b94ab50 Binary files /dev/null and b/.vs/mod-aoe-loot/v17/Solution.VC.db differ diff --git a/.vs/mod-aoe-loot/v17/ipch/AutoPCH/9b5505974df1ab6b/PLAYER.ipch b/.vs/mod-aoe-loot/v17/ipch/AutoPCH/9b5505974df1ab6b/PLAYER.ipch new file mode 100644 index 0000000..4591113 Binary files /dev/null and b/.vs/mod-aoe-loot/v17/ipch/AutoPCH/9b5505974df1ab6b/PLAYER.ipch differ diff --git a/.vs/mod-aoe-loot/v17/ipch/AutoPCH/e2b692f399a8f884/QUESTACTION.ipch b/.vs/mod-aoe-loot/v17/ipch/AutoPCH/e2b692f399a8f884/QUESTACTION.ipch new file mode 100644 index 0000000..82475a8 Binary files /dev/null and b/.vs/mod-aoe-loot/v17/ipch/AutoPCH/e2b692f399a8f884/QUESTACTION.ipch differ diff --git a/.vs/mod-aoe-loot/v17/ipch/AutoPCH/f16803946d677ffc/AOE_LOOT.ipch b/.vs/mod-aoe-loot/v17/ipch/AutoPCH/f16803946d677ffc/AOE_LOOT.ipch new file mode 100644 index 0000000..1ed9233 Binary files /dev/null and b/.vs/mod-aoe-loot/v17/ipch/AutoPCH/f16803946d677ffc/AOE_LOOT.ipch differ diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite new file mode 100644 index 0000000..bbd2ad8 Binary files /dev/null and b/.vs/slnx.sqlite differ diff --git a/conf/mod_aoe_loot.conf.dist b/conf/mod_aoe_loot.conf.dist index 610c6b1..3d38a5d 100644 --- a/conf/mod_aoe_loot.conf.dist +++ b/conf/mod_aoe_loot.conf.dist @@ -1,40 +1,48 @@ ######################################## -# AoeLoot module configuration +# AoeLoot Module Configuration ######################################## # -# AOELoot.Enable -# Default: 2 - (Enabled for solo and group play) -# 1 - (Enabled for solo play only) -# 0 - (Disabled) +# AoeLoot.EnableMod +# Values: 0 = Disable module +# 1 = Enable module +# Default: 1 (Enabled) -AOELoot.Enable = 2 +AoeLoot.EnableMod = 1 -# AOELoot.Message -# Description: Enable module notification message on login -# Default: 0 - (Disabled) -# 1 - (Enabled) +# AoeLoot.EnableAOELoot +# Values: 0 = Disable AoE looting feature +# 1 = Enable AoE looting feature (solo play only) +# 2 = Enable AoE looting feature (solo and group play) +# Default: 2 (Enabled for solo and group play) -AOELoot.Message = 0 +AoeLoot.EnableAOELoot = 2 -# AOELoot.Range +# AoeLoot.Message +# Description: Enable module notification message on login +# Values: 0 = Disable message +# 1 = Enable message +# Default: 1 (Enabled) + +AoeLoot.Message = 1 + +# AoeLoot.Range # Description: Radius for AoE looting area # Default: 55.0 -AOELoot.Range = 55.0 +AoeLoot.Range = 55.0 -# AOELoot.DefaultLootMethod +# AoeLoot.DefaultLootMethod # Description: Sets the default loot method for newly created groups -# Values: 0 = Free For All (everyone can loot everything) -# 1 = Round Robin (items are distributed in turn) -# 2 = Master Loot (only master looter can distribute items) -# 3 = Group Loot (group rolls on items above threshold) -# 4 = Need Before Greed (players roll Need/Greed/Pass on items above threshold) -# Default: 3 (Group Loot) +# Values: 0 = Free For All (players can loot everything) +# 1 = Round Robin (players take turns looting) +# 2 = Group Loot (Blizzard default; players take turns looting, except items at or above the DefautlLootThreshold are rolled for by all players) +# 3 = Need Before Greed (players take turns looting, except items at or above the DefaultLootThreshold are rolled for by players who can use them) +# 4 = Master Loot (only master looter can distribute items) +# Default: 2 (Group Loot) -AOELoot.DefaultLootMethod = 3 +AoeLoot.DefaultLootMethod = 2 -# -# AOELoot.DefaultLootThreshold +# AoeLoot.DefaultLootThreshold # Description: Item quality threshold for group loot rolls (applies only to Group Loot and Need Before Greed) # Values: 0 = Poor (Gray) # 1 = Common (White) @@ -44,26 +52,18 @@ AOELoot.DefaultLootMethod = 3 # 5 = Legendary (Orange) # Default: 2 (Uncommon) -AOELoot.DefaultLootThreshold = 2 +AoeLoot.DefaultLootThreshold = 2 -# AOELoot.Debug +# AoeLoot.Debug # Description: Enables debugging mode. # Values: 0 = Disabled # 1 = Enabled # Default: 0 (Disabled) -AOELoot.Debug = 0 - -# AOELoot.QuestItemsAsRegular -# Description: Treat quest items as regular loot instead of processing them separately -# Values: 0 = Process quest items separately (default behavior) -# 1 = Treat quest items as regular loot -# Default: 0 (Process separately) - -AOELoot.QuestItemsAsRegular = 0 +AoeLoot.Debug = 0 # Chat Commands -# .aoeloot off If module is enabled, disables AoE looting for player -# .aoeloot on If module is enabled, reenables AoE looting for player -# .aoeloot lootall Loot all enemies within AOELoot.Range +# .AoeLoot off Disables AoE looting for player +# .AoeLoot on Enables AoE looting for player (requires module to be enabled) +# .AoeLoot lootall Loot all enemies within AoeLoot.Range # diff --git a/src/aoe_loot.cpp b/src/aoe_loot.cpp index 14da0da..6a40724 100644 --- a/src/aoe_loot.cpp +++ b/src/aoe_loot.cpp @@ -26,11 +26,13 @@ using namespace WorldPackets; // Thread-safe player AoE loot preference storage (renamed) std::map playerAoeLootPreferences; -std::mutex aoeLootPreferencesMutex; +std::mutex AoeLootPreferencesMutex; -// Track groups that have had default loot settings applied by us -std::set groupsWithAppliedDefaults; -std::mutex groupLootMethodMutex; +// Helper function to check if loot system module is enabled +bool IsAoeLootModuleEnabled() +{ + return sConfigMgr->GetOption("AoeLoot.EnableMod", true); +} // Helper function to get configured loot method from config value LootMethod GetLootMethodFromConfig(uint32 configValue) @@ -48,11 +50,33 @@ LootMethod GetLootMethodFromConfig(uint32 configValue) case 4: return NEED_BEFORE_GREED; default: - LOG_WARN("module.aoe_loot", "Invalid AOELoot.DefaultLootMethod value: {}. Using Group Loot.", configValue); + LOG_WARN("module.aoe_loot", "Invalid AoeLoot.DefaultLootMethod value: {}. Using Group Loot.", configValue); return GROUP_LOOT; } } +// Helper function to apply default loot settings to a group +void ApplyDefaultLootSettings(Group* group, Player* leaderOrPlayer) +{ + if (!group || !leaderOrPlayer) + return; + + uint32 defaultLootMethodConfig = sConfigMgr->GetOption("AoeLoot.DefaultLootMethod", 3); + LootMethod lootMethod = GetLootMethodFromConfig(defaultLootMethodConfig); + group->SetLootMethod(lootMethod); + + if (lootMethod == NEED_BEFORE_GREED || lootMethod == GROUP_LOOT) + { + uint32 defaultLootThreshold = sConfigMgr->GetOption("AoeLoot.DefaultLootThreshold", 2); + group->SetLootThreshold(ItemQualities(defaultLootThreshold)); + } + + if (lootMethod == MASTER_LOOT) + { + group->SetMasterLooterGuid(leaderOrPlayer->GetGUID()); + } +} + // Define the roll vote enum value - use the correct RollVote enum #ifndef NOT_EMITED_YET #define NOT_EMITED_YET RollVote(0) @@ -61,9 +85,9 @@ LootMethod GetLootMethodFromConfig(uint32 configValue) // Helper function to check if AOE loot is enabled for current context bool AoeLootCommandScript::IsAoeLootEnabledForPlayer(Player* player) { - uint32 aoeLootMode = sConfigMgr->GetOption("AOELoot.Enable", 2); - - switch (aoeLootMode) + uint32 AoeLootMode = sConfigMgr->GetOption("AoeLoot.EnableAOELoot", 2); + + switch (AoeLootMode) { case 0: // Disabled return false; @@ -75,13 +99,16 @@ bool AoeLootCommandScript::IsAoeLootEnabledForPlayer(Player* player) return true; default: // Invalid value, default to solo + group - LOG_WARN("module.aoe_loot", "Invalid AOELoot.Enable value: {}. Using default (2).", aoeLootMode); + LOG_WARN("module.aoe_loot", "Invalid AoeLoot.EnableAOELoot value: {}. Using default (2).", AoeLootMode); return true; } } bool AoeLootServer::CanPacketReceive(WorldSession* session, WorldPacket& packet) { + if (!IsAoeLootModuleEnabled()) + return true; + if (packet.GetOpcode() == CMSG_LOOT) { Player* player = session->GetPlayer(); @@ -133,7 +160,7 @@ bool AoeLootServer::CanPacketReceive(WorldSession* session, WorldPacket& packet) // Check if player has explicitly disabled AOE loot (thread-safe) { - std::lock_guard lock(aoeLootPreferencesMutex); + std::lock_guard lock(AoeLootPreferencesMutex); auto it = playerAoeLootPreferences.find(guid); if (it != playerAoeLootPreferences.end() && !it->second) { @@ -146,7 +173,7 @@ bool AoeLootServer::CanPacketReceive(WorldSession* session, WorldPacket& packet) if (player->GetLootGUID().IsEmpty()) { ChatHandler handler(player->GetSession()); - handler.ParseCommands(".aoeloot lootall"); + handler.ParseCommands(".AoeLoot lootall"); } } return true; @@ -154,23 +181,26 @@ bool AoeLootServer::CanPacketReceive(WorldSession* session, WorldPacket& packet) ChatCommandTable AoeLootCommandScript::GetCommands() const { - static ChatCommandTable aoeLootSubCommandTable = + static ChatCommandTable AoeLootSubCommandTable = { { "lootall", TriggerAoeLootCommand, SEC_PLAYER, Console::No }, { "on", EnableAoeLootCommand, SEC_PLAYER, Console::No }, { "off", DisableAoeLootCommand, SEC_PLAYER, Console::No } }; - static ChatCommandTable aoeLootCommandTable = + static ChatCommandTable AoeLootCommandTable = { - { "aoeloot", aoeLootSubCommandTable } + { "AoeLoot", AoeLootSubCommandTable } }; - return aoeLootCommandTable; + return AoeLootCommandTable; } -bool AoeLootCommandScript::EnableAoeLootCommand(ChatHandler* handler, Optional /*args*/) +bool AoeLootCommandScript::EnableAoeLootCommand(ChatHandler* handler, Optional) { + if (!IsAoeLootModuleEnabled()) + return true; + Player* player = handler->GetSession()->GetPlayer(); if (!player) return true; @@ -180,8 +210,8 @@ bool AoeLootCommandScript::EnableAoeLootCommand(ChatHandler* handler, OptionalGetOption("AOELoot.Enable", 2); - switch (aoeLootMode) + uint32 AoeLootMode = sConfigMgr->GetOption("AoeLoot.EnableAOELoot", 2); + switch (AoeLootMode) { case 0: handler->PSendSysMessage("AOE looting is completely disabled on this server."); @@ -198,16 +228,19 @@ bool AoeLootCommandScript::EnableAoeLootCommand(ChatHandler* handler, Optional lock(aoeLootPreferencesMutex); + std::lock_guard lock(AoeLootPreferencesMutex); playerAoeLootPreferences[guid] = true; } - handler->PSendSysMessage("AOE looting has been enabled for your character. Type: '.aoeloot off' to turn AoE Looting Off."); + handler->PSendSysMessage("AOE looting has been enabled for your character. Type: '.AoeLoot off' to turn AoE Looting Off."); return true; } -bool AoeLootCommandScript::DisableAoeLootCommand(ChatHandler* handler, Optional /*args*/) +bool AoeLootCommandScript::DisableAoeLootCommand(ChatHandler* handler, Optional) { + if (!IsAoeLootModuleEnabled()) + return true; + Player* player = handler->GetSession()->GetPlayer(); if (!player) return true; @@ -216,15 +249,14 @@ bool AoeLootCommandScript::DisableAoeLootCommand(ChatHandler* handler, Optional< // Thread-safe update { - std::lock_guard lock(aoeLootPreferencesMutex); + std::lock_guard lock(AoeLootPreferencesMutex); playerAoeLootPreferences[guid] = false; } - handler->PSendSysMessage("AOE looting has been disabled for your character. Type: '.aoeloot on' to turn AoE Looting on."); + handler->PSendSysMessage("AOE looting has been disabled for your character. Type: '.AoeLoot on' to turn AoE Looting on."); return true; } -// Rename ValidateLootDistance to ValidateLootingDistance bool AoeLootCommandScript::ValidateLootingDistance(Player* player, ObjectGuid lguid, float maxDistance) { if (!player) @@ -232,7 +264,7 @@ bool AoeLootCommandScript::ValidateLootingDistance(Player* player, ObjectGuid lg // Use configured AOE distance if no specific distance provided if (maxDistance <= 0.0f) - maxDistance = sConfigMgr->GetOption("AOELoot.Range", 55.0f); + maxDistance = sConfigMgr->GetOption("AoeLoot.Range", 55.0f); if (lguid.IsGameObject()) { @@ -277,7 +309,6 @@ bool AoeLootCommandScript::ValidateLootingDistance(Player* player, ObjectGuid lg } } -// Rename ProcessLootMoney to ProcessCreatureGold bool AoeLootCommandScript::ProcessCreatureGold(Player* player, Creature* creature) { if (!player || !creature) @@ -341,8 +372,7 @@ bool AoeLootCommandScript::ProcessCreatureGold(Player* player, Creature* creatur return true; } -// Rename ProcessLootRelease to ReleaseAndCleanupLoot -void AoeLootCommandScript::ReleaseAndCleanupLoot(ObjectGuid lguid, Player* player, Loot* /*originalLoot*/) +void AoeLootCommandScript::ReleaseAndCleanupLoot(ObjectGuid lguid, Player* player, Loot*) { player->SetLootGUID(ObjectGuid::Empty); player->SendLootRelease(lguid); @@ -441,9 +471,11 @@ void AoeLootCommandScript::ReleaseAndCleanupLoot(ObjectGuid lguid, Player* playe } } -// Rename ProcessLootSlot to ProcessSingleLootSlot bool AoeLootCommandScript::ProcessSingleLootSlot(Player* player, ObjectGuid lguid, uint8 lootSlot) { + if (!IsAoeLootModuleEnabled()) + return true; + if (!player) return false; @@ -600,6 +632,9 @@ bool AoeLootCommandScript::ProcessSingleLootSlot(Player* player, ObjectGuid lgui bool AoeLootCommandScript::TriggerAoeLootCommand(ChatHandler* handler, Optional /*args*/) { + if (!IsAoeLootModuleEnabled()) + return true; + Player* player = handler->GetSession()->GetPlayer(); if (!player) return true; @@ -611,8 +646,8 @@ bool AoeLootCommandScript::TriggerAoeLootCommand(ChatHandler* handler, Optional< return true; } - float range = sConfigMgr->GetOption("AOELoot.Range", 55.0); - bool debugMode = sConfigMgr->GetOption("AOELoot.Debug", false); + float range = sConfigMgr->GetOption("AoeLoot.Range", 55.0); + bool debugMode = sConfigMgr->GetOption("AoeLoot.Debug", false); std::list nearbyCorpses; player->GetDeadCreatureListInGrid(nearbyCorpses, range); @@ -715,25 +750,8 @@ bool AoeLootCommandScript::TriggerAoeLootCommand(ChatHandler* handler, Optional< } } - // Handle quest items based on configuration - if (ShouldProcessQuestItemsSeparately()) - { - // Process quest items separately (default behavior) - ProcessQuestItemsForPlayer(player, lguid, loot); - } - else - { - // Treat quest items as regular loot (like quest loot party mod) - for (uint8 i = 0; i < loot->quest_items.size(); ++i) - { - uint8 questLootSlot = loot->items.size() + i; - ProcessSingleLootSlot(player, lguid, questLootSlot); - if (debugMode) - { - LOG_DEBUG("module.aoe_loot", "AOE Loot: looted quest item (as regular) in slot {}", questLootSlot); - } - } - } + // Always process quest items using Blizzard logic (only for players who need them) + ProcessQuestItemsForPlayer(player, lguid, loot); // Handle money if (loot->gold > 0) @@ -757,14 +775,22 @@ bool AoeLootCommandScript::TriggerAoeLootCommand(ChatHandler* handler, Optional< // Display login message to player void AoeLootPlayer::OnPlayerLogin(Player* player) { - // Check if AOE loot is enabled at all (any mode > 0) - uint32 aoeLootMode = sConfigMgr->GetOption("AOELoot.Enable", 2); - - if (aoeLootMode > 0 && sConfigMgr->GetOption("AOELoot.Message", true)) + if (!IsAoeLootModuleEnabled()) + return; + + // If player is in a group, apply default loot settings + if (Group* group = player->GetGroup()) + { + ApplyDefaultLootSettings(group, player); + } + + uint32 AoeLootMode = sConfigMgr->GetOption("AoeLoot.EnableAOELoot", 2); + + if (AoeLootMode > 0 && sConfigMgr->GetOption("AoeLoot.Message", true)) { std::string message = "AOE looting has been enabled for your character"; - switch (aoeLootMode) + switch (AoeLootMode) { case 1: message += " (solo play only)"; @@ -774,246 +800,58 @@ void AoeLootPlayer::OnPlayerLogin(Player* player) break; } - message += ". Type: '.aoeloot off' to turn AoE Looting Off."; + message += ". Type: '.AoeLoot off' to turn AoE Looting Off."; ChatHandler(player->GetSession()).PSendSysMessage(message.c_str()); } } -// Group script implementation void AoeLootGroupScript::OnCreate(Group* group, Player* leader) { - // Only apply default loot method to brand new groups, not reformed/rejoined groups - if (!group || !leader) - return; - - // Only apply to truly new groups (single member = the leader) - // When a player rejoins an existing group, the group already has its loot method preserved - if (group->GetMembersCount() > 1) + if (!IsAoeLootModuleEnabled()) return; - // Check if this is a playerbot scenario - if the leader is a bot, delay loot method setting - // to allow playerbot logic to handle leadership transfers when the player logs in - // Note: Since IsBot() may not be available, we'll use a more conservative approach - // and only apply defaults when we're certain it's a real player-led group - if (!leader->GetSession() || !leader->GetSession()->GetPlayer()) - { - // No valid session - likely a bot or disconnected player - // Don't set loot method immediately + if (!group || !leader) return; - } - - // Apply default loot settings for real player leaders + + // Apply default loot settings to the group ApplyDefaultLootSettings(group, leader); } -// Handle leadership changes (important for playerbot scenarios) -void AoeLootGroupScript::OnChangeLeader(Group* group, ObjectGuid newLeaderGuid, ObjectGuid /*oldLeaderGuid*/) -{ - if (!group) - return; - - Player* newLeader = ObjectAccessor::FindPlayer(newLeaderGuid); - if (!newLeader) - return; - - // Only apply default loot settings if: - // 1. The new leader is a real player (has valid session) - // 2. The loot method hasn't been manually changed from our defaults - if (newLeader->GetSession() && newLeader->GetSession()->GetPlayer()) - { - // Safety check for valid group GUID - ObjectGuid groupGuid = group->GetGUID(); - if (!groupGuid.IsEmpty()) - { - uint64 groupId = groupGuid.GetRawValue(); - if (groupId != 0) // Additional safety check - { - try - { - // Check if this group has had our defaults applied and if they've been changed - std::lock_guard lock(groupLootMethodMutex); - if (groupsWithAppliedDefaults.find(groupId) == groupsWithAppliedDefaults.end()) - { - // This group has never had our defaults applied - // It's safe to apply our defaults - ApplyDefaultLootSettings(group, newLeader); - } - else if (!IsLootMethodManuallySet(group)) - { - // We've applied defaults before, but they haven't been manually changed - // This handles bot-to-player leadership transfers where we want to reapply defaults - ApplyDefaultLootSettings(group, newLeader); - } - // If the loot method has been manually changed, don't override it - } - catch (const std::exception& e) - { - LOG_ERROR("module.aoe_loot", "Failed to handle leadership change for group {}: {}", groupId, e.what()); - } - } - else - { - LOG_WARN("module.aoe_loot", "Group has invalid GUID (0) in OnChangeLeader"); - } - } - else - { - LOG_WARN("module.aoe_loot", "Group has empty GUID in OnChangeLeader"); - } - } -} - -// Handle group dissolution - clean up tracking -void AoeLootGroupScript::OnDisband(Group* group) -{ - if (!group) - return; - - // Remove this group from our tracking when it's disbanded - // Safety check for valid group GUID - ObjectGuid groupGuid = group->GetGUID(); - if (!groupGuid.IsEmpty()) - { - uint64 groupId = groupGuid.GetRawValue(); - if (groupId != 0) // Additional safety check - { - try - { - std::lock_guard lock(groupLootMethodMutex); - groupsWithAppliedDefaults.erase(groupId); - } - catch (const std::exception& e) - { - LOG_ERROR("module.aoe_loot", "Failed to clean up tracking for group {}: {}", groupId, e.what()); - } - } - } -} - -// Helper function to apply default loot settings -void AoeLootGroupScript::ApplyDefaultLootSettings(Group* group, Player* leader) -{ - if (!group || !leader) - return; - - // Get configured default loot method - uint32 defaultLootMethodConfig = sConfigMgr->GetOption("AOELoot.DefaultLootMethod", 3); // 3 = Group Loot (default) - uint32 defaultLootThreshold = sConfigMgr->GetOption("AOELoot.DefaultLootThreshold", 2); // Default: Uncommon - - // Set the configured loot method for new groups - LootMethod lootMethod = GetLootMethodFromConfig(defaultLootMethodConfig); - group->SetLootMethod(lootMethod); - - // If using Need Before Greed or Group Loot, also set the quality threshold - if (lootMethod == NEED_BEFORE_GREED || lootMethod == GROUP_LOOT) - { - group->SetLootThreshold(ItemQualities(defaultLootThreshold)); - } - - // If using Master Loot, set the leader as master looter - if (lootMethod == MASTER_LOOT && leader) - { - group->SetMasterLooterGuid(leader->GetGUID()); - } - - // Track that we've applied defaults to this group - // Add safety check for valid group GUID - ObjectGuid groupGuid = group->GetGUID(); - if (!groupGuid.IsEmpty()) - { - uint64 groupId = groupGuid.GetRawValue(); - if (groupId != 0) // Additional safety check - { - try - { - std::lock_guard lock(groupLootMethodMutex); - groupsWithAppliedDefaults.insert(groupId); - } - catch (const std::exception& e) - { - LOG_ERROR("module.aoe_loot", "Failed to track group {} in ApplyDefaultLootSettings: {}", groupId, e.what()); - } - } - else - { - LOG_WARN("module.aoe_loot", "Group has invalid GUID (0) in ApplyDefaultLootSettings"); - } - } - else - { - LOG_WARN("module.aoe_loot", "Group has empty GUID in ApplyDefaultLootSettings"); - } -} - -// Helper function to check if loot method appears to be manually set -bool AoeLootGroupScript::IsLootMethodManuallySet(Group* group) -{ - if (!group) - return false; - - // Get our configured defaults - uint32 defaultLootMethodConfig = sConfigMgr->GetOption("AOELoot.DefaultLootMethod", 3); - uint32 defaultLootThreshold = sConfigMgr->GetOption("AOELoot.DefaultLootThreshold", 2); - - LootMethod expectedDefaultMethod = GetLootMethodFromConfig(defaultLootMethodConfig); - - // If the current loot method or threshold differs from our defaults, - // it was likely changed manually by a player - if (group->GetLootMethod() != expectedDefaultMethod) - return true; - - // For Group Loot and Need Before Greed, also check the threshold - if ((expectedDefaultMethod == GROUP_LOOT || expectedDefaultMethod == NEED_BEFORE_GREED) && - group->GetLootThreshold() != defaultLootThreshold) - return true; - - return false; -} - -// Helper function implementation for AoeLootGroupScript -LootMethod AoeLootGroupScript::GetLootMethodFromConfig(uint32 configValue) -{ - switch (configValue) - { - case 0: return FREE_FOR_ALL; - case 1: return ROUND_ROBIN; - case 2: return MASTER_LOOT; - case 3: return GROUP_LOOT; - case 4: return NEED_BEFORE_GREED; - default: return GROUP_LOOT; // Safe default - } -} - // Quest item processing functions -bool AoeLootCommandScript::ShouldProcessQuestItemsSeparately() -{ - return !sConfigMgr->GetOption("AOELoot.QuestItemsAsRegular", false); -} - bool AoeLootCommandScript::IsQuestItemForPlayer(Player* player, uint32 itemId) { if (!player) return false; - + const ItemTemplate* itemTemplate = sObjectMgr->GetItemTemplate(itemId); if (!itemTemplate) return false; - - // Check if this item starts a quest or is a quest item + + // Check if this item starts a quest if (itemTemplate->StartQuest != 0) - return true; - + { + uint32 questId = itemTemplate->StartQuest; + // Player must NOT have the quest, must NOT have completed it, and must NOT already have the item + if (!player->HasQuest(questId) && + player->GetQuestStatus(questId) != QUEST_STATUS_COMPLETE && + player->GetItemCount(itemId, true) == 0) // true = include bank + { + return true; + } + return false; + } + // Check if player has quests requiring this item for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot) { uint32 questId = player->GetQuestSlotQuestId(slot); if (questId == 0) continue; - + Quest const* quest = sObjectMgr->GetQuestTemplate(questId); if (!quest) continue; - + // Check quest objectives for this item for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) { @@ -1022,7 +860,7 @@ bool AoeLootCommandScript::IsQuestItemForPlayer(Player* player, uint32 itemId) // Check if player still needs this item for the quest uint32 currentCount = player->GetItemCount(itemId, true); uint32 requiredCount = quest->RequiredItemCount[i]; - + if (currentCount < requiredCount) { return true; @@ -1030,7 +868,7 @@ bool AoeLootCommandScript::IsQuestItemForPlayer(Player* player, uint32 itemId) } } } - + return false; } @@ -1040,7 +878,7 @@ bool AoeLootCommandScript::ProcessQuestItemsForPlayer(Player* player, ObjectGuid return false; bool processedAny = false; - bool debugMode = sConfigMgr->GetOption("AOELoot.Debug", false); + bool debugMode = sConfigMgr->GetOption("AoeLoot.Debug", false); for (uint8 i = 0; i < loot->quest_items.size(); ++i) { diff --git a/src/aoe_loot.h b/src/aoe_loot.h index a9f8547..a8d8a18 100644 --- a/src/aoe_loot.h +++ b/src/aoe_loot.h @@ -1,5 +1,5 @@ -#ifndef MODULE_AOELOOT_H -#define MODULE_AOELOOT_H +#ifndef MODULE_AoeLoot_H +#define MODULE_AoeLoot_H #include "ScriptMgr.h" #include "Config.h" @@ -41,22 +41,20 @@ public: AoeLootCommandScript() : CommandScript("AoeLootCommandScript") {} ChatCommandTable GetCommands() const override; - // Renamed command handlers (removed "Handle" prefix) static bool EnableAoeLootCommand(ChatHandler* handler, Optional args); static bool DisableAoeLootCommand(ChatHandler* handler, Optional args); static bool TriggerAoeLootCommand(ChatHandler* handler, Optional args); - // Renamed core processing functions + // Core processing functions static bool ProcessSingleLootSlot(Player* player, ObjectGuid lguid, uint8 lootSlot); static bool ProcessCreatureGold(Player* player, Creature* creature); static void ReleaseAndCleanupLoot(ObjectGuid lguid, Player* player, Loot* loot); // Quest item processing functions static bool IsQuestItemForPlayer(Player* player, uint32 itemId); - static bool ShouldProcessQuestItemsSeparately(); static bool ProcessQuestItemsForPlayer(Player* player, ObjectGuid lguid, Loot* loot); - - // Renamed validation functions + + // Validation functions static bool ValidateLootingDistance(Player* player, ObjectGuid lguid, float maxDistance = 0.0f); static bool IsAoeLootEnabledForPlayer(Player* player); }; @@ -66,20 +64,12 @@ class AoeLootGroupScript : public GroupScript public: AoeLootGroupScript() : GroupScript("AoeLootGroupScript") {} void OnCreate(Group* group, Player* leader) override; - void OnChangeLeader(Group* group, ObjectGuid newLeaderGuid, ObjectGuid oldLeaderGuid) override; - void OnDisband(Group* group) override; private: // Helper function to convert config value to LootMethod enum LootMethod GetLootMethodFromConfig(uint32 configValue); - - // Helper function to apply default loot settings - void ApplyDefaultLootSettings(Group* group, Player* leader); - - // Helper function to check if loot method appears to be manually set - bool IsLootMethodManuallySet(Group* group); }; void AddSC_AoeLoot(); -#endif //MODULE_AOELOOT_H +#endif