mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
Fix. Leave group actions (#1774)
Fix for issue #1768 where the bot master was not getting reset. I also cleaned up leave group action to focus up function scope. I moved the resets from #612 to the actual leaving function. Disclosure: LLMs were NOT used in authoring this PR. Test cases to consider for testers. 1. Master disbands group with random bots. Bots should go about their business. 2. If you can dual box, create a raid where two real player both invite random bots. One player leaves group, their bots should also leave. (edge case, if a random bot that is supposed to leave the group becomes leader they may disband the whole group.
This commit is contained in:
2
code_format.sh
Executable file → Normal file
2
code_format.sh
Executable file → Normal file
@@ -15,4 +15,4 @@ for file in $cpp_files; do
|
||||
$CLANG_FORMAT_PATH -i $file
|
||||
done
|
||||
|
||||
echo "All .cpp or .h files have been formatted."
|
||||
echo "All .cpp or .h files have been formatted."
|
||||
@@ -355,7 +355,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
|
||||
}
|
||||
|
||||
// Update the bot's group status (moved to helper function)
|
||||
UpdateAIGroupMembership();
|
||||
UpdateAIGroupAndMaster();
|
||||
|
||||
// Update internal AI
|
||||
UpdateAIInternal(elapsed, minimal);
|
||||
@@ -363,47 +363,60 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
|
||||
}
|
||||
|
||||
// Helper function for UpdateAI to check group membership and handle removal if necessary
|
||||
void PlayerbotAI::UpdateAIGroupMembership()
|
||||
void PlayerbotAI::UpdateAIGroupAndMaster()
|
||||
{
|
||||
if (!bot || !bot->GetGroup())
|
||||
if (!bot)
|
||||
return;
|
||||
Group* group = bot->GetGroup();
|
||||
// If bot is not in group verify that for is RandomBot before clearing master and resetting.
|
||||
if (!group)
|
||||
{
|
||||
if (master && sRandomPlayerbotMgr->IsRandomBot(bot))
|
||||
{
|
||||
SetMaster(nullptr);
|
||||
Reset(true);
|
||||
ResetStrategies();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (bot->InBattleground() && bot->GetBattleground()->GetBgTypeID() != BATTLEGROUND_AV)
|
||||
return;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
if (!botAI)
|
||||
return;
|
||||
|
||||
if (!bot->InBattleground() && !bot->inRandomLfgDungeon() && !group->isLFGGroup())
|
||||
PlayerbotAI* masterBotAI = nullptr;
|
||||
if (master)
|
||||
masterBotAI = GET_PLAYERBOT_AI(master);
|
||||
|
||||
if (!master || (masterBotAI && !masterBotAI->IsRealPlayer()))
|
||||
{
|
||||
Player* leader = group->GetLeader();
|
||||
if (leader && leader != bot) // Ensure the leader is valid and not the bot itself
|
||||
Player* newMaster = FindNewMaster();
|
||||
if (newMaster)
|
||||
{
|
||||
PlayerbotAI* leaderAI = GET_PLAYERBOT_AI(leader);
|
||||
if (leaderAI && !leaderAI->IsRealPlayer())
|
||||
master = newMaster;
|
||||
botAI->SetMaster(newMaster);
|
||||
botAI->ResetStrategies();
|
||||
|
||||
if (!bot->InBattleground())
|
||||
{
|
||||
LeaveOrDisbandGroup();
|
||||
botAI->ChangeStrategy("+follow", BOT_STATE_NON_COMBAT);
|
||||
|
||||
if (botAI->GetMaster() == botAI->GetGroupMaster())
|
||||
botAI->TellMaster("Hello, I follow you!");
|
||||
else
|
||||
botAI->TellMaster(!urand(0, 2) ? "Hello!" : "Hi!");
|
||||
}
|
||||
else
|
||||
{
|
||||
// we're in a battleground, stay with the pack and focus on objective
|
||||
botAI->ChangeStrategy("-follow", BOT_STATE_NON_COMBAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (group->isLFGGroup())
|
||||
{
|
||||
bool hasRealPlayer = false;
|
||||
|
||||
// Iterate over all group members to check if at least one is a real player
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member)
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||
if (memberAI && !memberAI->IsRealPlayer())
|
||||
continue;
|
||||
|
||||
hasRealPlayer = true;
|
||||
break;
|
||||
}
|
||||
if (!hasRealPlayer)
|
||||
{
|
||||
else if (!newMaster && !bot->InBattleground())
|
||||
LeaveOrDisbandGroup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -792,7 +805,6 @@ void PlayerbotAI::LeaveOrDisbandGroup()
|
||||
|
||||
WorldPacket* packet = new WorldPacket(CMSG_GROUP_DISBAND);
|
||||
bot->GetSession()->QueuePacket(packet);
|
||||
ResetStrategies();
|
||||
}
|
||||
|
||||
bool PlayerbotAI::IsAllowedCommand(std::string const text)
|
||||
@@ -1301,7 +1313,6 @@ void PlayerbotAI::DoNextAction(bool min)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Change engine if just died
|
||||
bool isBotAlive = bot->IsAlive();
|
||||
if (currentEngine != engines[BOT_STATE_DEAD] && !isBotAlive)
|
||||
@@ -1361,92 +1372,6 @@ void PlayerbotAI::DoNextAction(bool min)
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
PlayerbotAI* masterBotAI = nullptr;
|
||||
if (master)
|
||||
masterBotAI = GET_PLAYERBOT_AI(master);
|
||||
|
||||
// Test BG master set
|
||||
if ((!master || (masterBotAI && !masterBotAI->IsRealPlayer())) && group)
|
||||
{
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
if (!botAI)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Ideally we want to have the leader as master.
|
||||
Player* newMaster = botAI->GetGroupMaster();
|
||||
Player* playerMaster = nullptr;
|
||||
|
||||
// Are there any non-bot players in the group?
|
||||
if (!newMaster || GET_PLAYERBOT_AI(newMaster))
|
||||
{
|
||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||
{
|
||||
Player* member = gref->GetSource();
|
||||
if (!member || member == bot || member == newMaster || !member->IsInWorld() ||
|
||||
!member->IsInSameRaidWith(bot))
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberBotAI = GET_PLAYERBOT_AI(member);
|
||||
if (memberBotAI)
|
||||
{
|
||||
if (memberBotAI->IsRealPlayer() && !bot->InBattleground())
|
||||
playerMaster = member;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Same BG checks (optimize checking conditions here)
|
||||
if (bot->InBattleground() && bot->GetBattleground() &&
|
||||
bot->GetBattleground()->GetBgTypeID() == BATTLEGROUND_AV && !GET_PLAYERBOT_AI(member) &&
|
||||
member->InBattleground() && bot->GetMapId() == member->GetMapId())
|
||||
{
|
||||
// Skip if same BG but same subgroup or lower level
|
||||
if (!group->SameSubGroup(bot, member) || member->GetLevel() < bot->GetLevel())
|
||||
continue;
|
||||
|
||||
// Follow real player only if higher honor points
|
||||
uint32 honorpts = member->GetHonorPoints();
|
||||
if (bot->GetHonorPoints() && honorpts < bot->GetHonorPoints())
|
||||
continue;
|
||||
|
||||
playerMaster = member;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bot->InBattleground())
|
||||
continue;
|
||||
|
||||
newMaster = member;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!newMaster && playerMaster)
|
||||
newMaster = playerMaster;
|
||||
|
||||
if (newMaster && (!master || master != newMaster) && bot != newMaster)
|
||||
{
|
||||
master = newMaster;
|
||||
botAI->SetMaster(newMaster);
|
||||
botAI->ResetStrategies();
|
||||
|
||||
if (!bot->InBattleground())
|
||||
{
|
||||
botAI->ChangeStrategy("+follow", BOT_STATE_NON_COMBAT);
|
||||
|
||||
if (botAI->GetMaster() == botAI->GetGroupMaster())
|
||||
botAI->TellMaster("Hello, I follow you!");
|
||||
else
|
||||
botAI->TellMaster(!urand(0, 2) ? "Hello!" : "Hi!");
|
||||
}
|
||||
else
|
||||
{
|
||||
// we're in a battleground, stay with the pack and focus on objective
|
||||
botAI->ChangeStrategy("-follow", BOT_STATE_NON_COMBAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (master && master->IsInWorld())
|
||||
{
|
||||
@@ -4135,6 +4060,50 @@ bool IsAlliance(uint8 race)
|
||||
race == RACE_DRAENEI;
|
||||
}
|
||||
|
||||
Player* PlayerbotAI::FindNewMaster()
|
||||
{
|
||||
// Ideally we want to have the leader as master.
|
||||
Group* group = bot->GetGroup();
|
||||
// Only allow real players as masters unless in battleground.
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
Player* groupLeader = GetGroupMaster();
|
||||
PlayerbotAI* leaderBotAI = GET_PLAYERBOT_AI(groupLeader);
|
||||
if (!leaderBotAI || leaderBotAI->IsRealPlayer())
|
||||
return groupLeader;
|
||||
|
||||
// Find the real player in group
|
||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||
{
|
||||
Player* member = gref->GetSource();
|
||||
if (!member || member == bot || !member->IsInWorld() ||
|
||||
!member->IsInSameRaidWith(bot))
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberBotAI = GET_PLAYERBOT_AI(member);
|
||||
if ((!memberBotAI || memberBotAI->IsRealPlayer()) && !bot->InBattleground())
|
||||
return member;
|
||||
|
||||
if (bot->InBattleground() && bot->GetBattleground() &&
|
||||
bot->GetBattleground()->GetBgTypeID() == BATTLEGROUND_AV && !GET_PLAYERBOT_AI(member) &&
|
||||
member->InBattleground() && bot->GetMapId() == member->GetMapId())
|
||||
{
|
||||
// Skip if same BG but same subgroup or lower level
|
||||
if (!group->SameSubGroup(bot, member) || member->GetLevel() < bot->GetLevel())
|
||||
continue;
|
||||
|
||||
// Follow real player only if higher honor points
|
||||
uint32 honorpts = member->GetHonorPoints();
|
||||
if (bot->GetHonorPoints() && honorpts < bot->GetHonorPoints())
|
||||
continue;
|
||||
|
||||
return member;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool PlayerbotAI::HasRealPlayerMaster()
|
||||
{
|
||||
if (master)
|
||||
|
||||
@@ -529,7 +529,8 @@ public:
|
||||
|
||||
Player* GetBot() { return bot; }
|
||||
Player* GetMaster() { return master; }
|
||||
|
||||
Player* FindNewMaster();
|
||||
|
||||
// Checks if the bot is really a player. Players always have themselves as master.
|
||||
bool IsRealPlayer() { return master ? (master == bot) : false; }
|
||||
// Bot has a master that is a player.
|
||||
@@ -611,7 +612,7 @@ private:
|
||||
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore,
|
||||
bool mixed = false);
|
||||
bool IsTellAllowed(PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
|
||||
void UpdateAIGroupMembership();
|
||||
void UpdateAIGroupAndMaster();
|
||||
Item* FindItemInInventory(std::function<bool(ItemTemplate const*)> checkItem) const;
|
||||
void HandleCommands();
|
||||
void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL);
|
||||
|
||||
@@ -11,8 +11,11 @@
|
||||
|
||||
bool LeaveGroupAction::Execute(Event event)
|
||||
{
|
||||
Player* master = event.getOwner();
|
||||
return Leave(master);
|
||||
Player* player = event.getOwner();
|
||||
if (player == botAI->GetMaster())
|
||||
return Leave();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PartyCommandAction::Execute(Event event)
|
||||
@@ -26,13 +29,21 @@ bool PartyCommandAction::Execute(Event event)
|
||||
|
||||
if (operation != PARTY_OP_LEAVE)
|
||||
return false;
|
||||
|
||||
// Only leave if master has left the party, and randombot cannot set new master.
|
||||
Player* master = GetMaster();
|
||||
if (master && member == master->GetName())
|
||||
return Leave(bot);
|
||||
|
||||
botAI->Reset();
|
||||
|
||||
{
|
||||
if (sRandomPlayerbotMgr->IsRandomBot(bot))
|
||||
{
|
||||
Player* newMaster = botAI->FindNewMaster();
|
||||
if (newMaster || bot->InBattleground())
|
||||
{
|
||||
botAI->SetMaster(newMaster);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return Leave();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -42,17 +53,17 @@ bool UninviteAction::Execute(Event event)
|
||||
if (p.GetOpcode() == CMSG_GROUP_UNINVITE)
|
||||
{
|
||||
p.rpos(0);
|
||||
std::string membername;
|
||||
p >> membername;
|
||||
std::string memberName;
|
||||
p >> memberName;
|
||||
|
||||
// player not found
|
||||
if (!normalizePlayerName(membername))
|
||||
if (!normalizePlayerName(memberName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bot->GetName() == membername)
|
||||
return Leave(bot);
|
||||
if (bot->GetName() == memberName)
|
||||
return Leave();
|
||||
}
|
||||
|
||||
if (p.GetOpcode() == CMSG_GROUP_UNINVITE_GUID)
|
||||
@@ -62,50 +73,29 @@ bool UninviteAction::Execute(Event event)
|
||||
p >> guid;
|
||||
|
||||
if (bot->GetGUID() == guid)
|
||||
return Leave(bot);
|
||||
return Leave();
|
||||
}
|
||||
|
||||
botAI->Reset();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LeaveGroupAction::Leave(Player* player)
|
||||
bool LeaveGroupAction::Leave()
|
||||
{
|
||||
if (player &&
|
||||
!botAI &&
|
||||
!botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_INVITE, false, player))
|
||||
|
||||
if (!botAI)
|
||||
return false;
|
||||
|
||||
bool aiMaster = GET_PLAYERBOT_AI(botAI->GetMaster()) != nullptr;
|
||||
|
||||
botAI->TellMaster("Goodbye!", PLAYERBOT_SECURITY_TALK);
|
||||
|
||||
bool randomBot = sRandomPlayerbotMgr->IsRandomBot(bot);
|
||||
bool shouldStay = randomBot && bot->GetGroup() && player == bot;
|
||||
if (!shouldStay)
|
||||
{
|
||||
botAI->LeaveOrDisbandGroup();
|
||||
}
|
||||
|
||||
if (randomBot)
|
||||
{
|
||||
GET_PLAYERBOT_AI(bot)->SetMaster(nullptr);
|
||||
}
|
||||
|
||||
if (!aiMaster)
|
||||
botAI->ResetStrategies(!randomBot);
|
||||
|
||||
botAI->Reset();
|
||||
Player* master = botAI -> GetMaster();
|
||||
if (master)
|
||||
botAI->TellMaster("Goodbye!", PLAYERBOT_SECURITY_TALK);
|
||||
|
||||
botAI->LeaveOrDisbandGroup();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LeaveFarAwayAction::Execute(Event event)
|
||||
{
|
||||
// allow bot to leave party when they want
|
||||
return Leave(botAI->GetGroupMaster());
|
||||
return Leave();
|
||||
}
|
||||
|
||||
bool LeaveFarAwayAction::isUseful()
|
||||
@@ -165,7 +155,5 @@ bool LeaveFarAwayAction::isUseful()
|
||||
return true;
|
||||
}
|
||||
|
||||
botAI->Reset();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ public:
|
||||
|
||||
bool Execute(Event event) override;
|
||||
|
||||
virtual bool Leave(Player* player);
|
||||
virtual bool Leave();
|
||||
};
|
||||
|
||||
class PartyCommandAction : public LeaveGroupAction
|
||||
|
||||
Reference in New Issue
Block a user