preserve configured loot method after server restart

This commit is contained in:
crow
2025-08-15 20:37:55 -05:00
parent ab678cc91a
commit 6525d3912a
17 changed files with 232 additions and 321 deletions

3
.vs/ProjectSettings.json Normal file
View File

@@ -0,0 +1,3 @@
{
"CurrentProjectSetting": "No Configurations"
}

View File

@@ -0,0 +1,9 @@
{
"ExpandedNodes": [
"",
"\\conf",
"\\src"
],
"SelectedNode": "\\src\\aoe_loot.h",
"PreviewInSolutionExplorer": false
}

BIN
.vs/mod-aoe-loot/v17/.wsuo Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -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": ""
}
]
}
]
}
]
}

Binary file not shown.

BIN
.vs/slnx.sqlite Normal file

Binary file not shown.

View File

@@ -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
# 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.EnableAOELoot = 2
# AoeLoot.Message
# Description: Enable module notification message on login
# Default: 0 - (Disabled)
# 1 - (Enabled)
# Values: 0 = Disable message
# 1 = Enable message
# Default: 1 (Enabled)
AOELoot.Message = 0
AoeLoot.Message = 1
# AOELoot.Range
# 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
#

View File

@@ -26,11 +26,13 @@ using namespace WorldPackets;
// Thread-safe player AoE loot preference storage (renamed)
std::map<uint64, bool> playerAoeLootPreferences;
std::mutex aoeLootPreferencesMutex;
std::mutex AoeLootPreferencesMutex;
// Track groups that have had default loot settings applied by us
std::set<uint64> groupsWithAppliedDefaults;
std::mutex groupLootMethodMutex;
// Helper function to check if loot system module is enabled
bool IsAoeLootModuleEnabled()
{
return sConfigMgr->GetOption<bool>("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<uint32>("AoeLoot.DefaultLootMethod", 3);
LootMethod lootMethod = GetLootMethodFromConfig(defaultLootMethodConfig);
group->SetLootMethod(lootMethod);
if (lootMethod == NEED_BEFORE_GREED || lootMethod == GROUP_LOOT)
{
uint32 defaultLootThreshold = sConfigMgr->GetOption<uint32>("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<uint32>("AOELoot.Enable", 2);
uint32 AoeLootMode = sConfigMgr->GetOption<uint32>("AoeLoot.EnableAOELoot", 2);
switch (aoeLootMode)
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<std::mutex> lock(aoeLootPreferencesMutex);
std::lock_guard<std::mutex> 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<std::string> /*args*/)
bool AoeLootCommandScript::EnableAoeLootCommand(ChatHandler* handler, Optional<std::string>)
{
if (!IsAoeLootModuleEnabled())
return true;
Player* player = handler->GetSession()->GetPlayer();
if (!player)
return true;
@@ -180,8 +210,8 @@ bool AoeLootCommandScript::EnableAoeLootCommand(ChatHandler* handler, Optional<s
// Check if AOE loot is enabled server-side
if (!IsAoeLootEnabledForPlayer(player))
{
uint32 aoeLootMode = sConfigMgr->GetOption<uint32>("AOELoot.Enable", 2);
switch (aoeLootMode)
uint32 AoeLootMode = sConfigMgr->GetOption<uint32>("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<s
// Thread-safe update
{
std::lock_guard<std::mutex> lock(aoeLootPreferencesMutex);
std::lock_guard<std::mutex> 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<std::string> /*args*/)
bool AoeLootCommandScript::DisableAoeLootCommand(ChatHandler* handler, Optional<std::string>)
{
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<std::mutex> lock(aoeLootPreferencesMutex);
std::lock_guard<std::mutex> 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<float>("AOELoot.Range", 55.0f);
maxDistance = sConfigMgr->GetOption<float>("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<std::string> /*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<float>("AOELoot.Range", 55.0);
bool debugMode = sConfigMgr->GetOption<bool>("AOELoot.Debug", false);
float range = sConfigMgr->GetOption<float>("AoeLoot.Range", 55.0);
bool debugMode = sConfigMgr->GetOption<bool>("AoeLoot.Debug", false);
std::list<Creature*> 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)
// Always process quest items using Blizzard logic (only for players who need them)
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);
}
}
}
// 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<uint32>("AOELoot.Enable", 2);
if (!IsAoeLootModuleEnabled())
return;
if (aoeLootMode > 0 && sConfigMgr->GetOption<bool>("AOELoot.Message", true))
// If player is in a group, apply default loot settings
if (Group* group = player->GetGroup())
{
ApplyDefaultLootSettings(group, player);
}
uint32 AoeLootMode = sConfigMgr->GetOption<uint32>("AoeLoot.EnableAOELoot", 2);
if (AoeLootMode > 0 && sConfigMgr->GetOption<bool>("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,222 +800,24 @@ 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 (!IsAoeLootModuleEnabled())
return;
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)
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
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<std::mutex> 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<std::mutex> 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<uint32>("AOELoot.DefaultLootMethod", 3); // 3 = Group Loot (default)
uint32 defaultLootThreshold = sConfigMgr->GetOption<uint32>("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<std::mutex> 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<uint32>("AOELoot.DefaultLootMethod", 3);
uint32 defaultLootThreshold = sConfigMgr->GetOption<uint32>("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<bool>("AOELoot.QuestItemsAsRegular", false);
}
bool AoeLootCommandScript::IsQuestItemForPlayer(Player* player, uint32 itemId)
{
if (!player)
@@ -999,9 +827,19 @@ bool AoeLootCommandScript::IsQuestItemForPlayer(Player* player, uint32 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)
{
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)
@@ -1040,7 +878,7 @@ bool AoeLootCommandScript::ProcessQuestItemsForPlayer(Player* player, ObjectGuid
return false;
bool processedAny = false;
bool debugMode = sConfigMgr->GetOption<bool>("AOELoot.Debug", false);
bool debugMode = sConfigMgr->GetOption<bool>("AoeLoot.Debug", false);
for (uint8 i = 0; i < loot->quest_items.size(); ++i)
{

View File

@@ -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<std::string> args);
static bool DisableAoeLootCommand(ChatHandler* handler, Optional<std::string> args);
static bool TriggerAoeLootCommand(ChatHandler* handler, Optional<std::string> 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