Performance: Refactored a bunch of functions in Playerbot ai (#865)

* Update PlayerbotAI.h

* Refactored a number of functions in PlayerbotAI.cpp

* Update PlayerbotAI.cpp

* Update PlayerbotAI.cpp - update for commit done

Take
568592f188
into account.

* Missing check for aurEff

* Update PlayerbotAI.cpp

nvm...

* Update PlayerbotAI.cpp

GetAura

* Update PlayerbotAI.cpp

Simplified/Optimized sPlayerbotAIConfig->dynamicReactDelay logic for in-combat.

* Update PlayerbotAI.cpp

Dubass fix

* Update PlayerbotAI.cpp

Fix bots leaving dungeon group,. again.

* Update PlayerbotAI.cpp

* Update PlayerbotAI.cpp - order correction

...Required for proper pet behavior.

* Update PlayerbotAI.cpp - UpdateAIGroupMembership()

Final refactor of helper function as all now works as required.

* Update PlayerbotAI.cpp

FindItemInInventory

* Update PlayerbotAI.h

Added helper functions, correct public -> private
This commit is contained in:
SaW
2025-01-15 22:49:41 +01:00
committed by GitHub
parent a24a4c0865
commit 89590860fb
2 changed files with 263 additions and 438 deletions

View File

@@ -225,113 +225,47 @@ PlayerbotAI::~PlayerbotAI()
void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
{ {
// Handle the AI check delay
if (nextAICheckDelay > elapsed) if (nextAICheckDelay > elapsed)
nextAICheckDelay -= elapsed; nextAICheckDelay -= elapsed;
else else
nextAICheckDelay = 0; nextAICheckDelay = 0;
if (!bot || !bot->IsInWorld()) // Early return if bot is in invalid state
{ if (!bot || !bot->IsInWorld() || !bot->GetSession() || bot->GetSession()->isLogingOut() || bot->IsDuringRemoveFromWorld())
return; return;
}
if (!bot->GetSession() || bot->GetSession()->isLogingOut()) // Handle cheat options (set bot health and power if cheats are enabled)
{ if (bot->IsAlive() && (static_cast<uint32>(GetCheat()) > 0 || static_cast<uint32>(sPlayerbotAIConfig->botCheatMask) > 0))
return;
}
if (bot->IsDuringRemoveFromWorld())
{
return;
}
// if (!GetMaster() || !GetMaster()->IsInWorld() || !GetMaster()->GetSession() ||
// GetMaster()->GetSession()->isLogingOut()) {
// return;
// }
// if (bot->HasUnitMovementFlag(MOVEMENTFLAG_FALLING)) {
// bot->Say("Falling!", LANG_UNIVERSAL);
// }
// if (!bot->HasUnitMovementFlag(MOVEMENTFLAG_FALLING) && bot->GetPositionZ() - bot->GetFloorZ() > 0.1f) {
// bot->AddUnitMovementFlag(MOVEMENTFLAG_FALLING);
// // bot->GetMotionMaster()->MoveFall();
// }
// if (bot->HasUnitMovementFlag(MOVEMENTFLAG_FALLING) && bot->GetPositionZ() - bot->GetFloorZ() <= 0.1f) {
// bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING);
// }
// else {
// bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING);
// }
// bot->SendMovementFlagUpdate();
// bot->GetMotionMaster()->MoveFall();
// if (bot->HasUnitMovementFlag(MOVEMENTFLAG_FALLING)) {
// // bot->GetUnitMovementFlags();
// bot->Say("falling... flag: " + std::to_string(bot->GetUnitMovementFlags()), LANG_UNIVERSAL);
// }
// bot->SendMovementFlagUpdate();
// float x, y, z;
// bot->GetPosition(x, y, z);
// bot->UpdateGroundPositionZ(x, y, z);
// if (bot->GetPositionZ() - z > 0.1f) {
// }
// wake up if in combat
// if (bot->IsInCombat())
// {
// if (!inCombat)
// nextAICheckDelay = 0;
// else if (!AllowActivity())
// {
// if (AllowActivity(ALL_ACTIVITY, true))
// nextAICheckDelay = 0;
// }
// inCombat = true;
// }
// else
// {
// if (inCombat)
// nextAICheckDelay = 0;
// inCombat = false;
// }
// force stop if moving but should not
// shouldn't stop charging
// if (bot->isMoving() && !CanMove() && !bot->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FALLING))
// {
// bot->StopMoving();
// bot->GetMotionMaster()->Clear();
// bot->GetMotionMaster()->MoveIdle();
// }
// cheat options
if (bot->IsAlive() && ((uint32)GetCheat() > 0 || (uint32)sPlayerbotAIConfig->botCheatMask > 0))
{ {
if (HasCheat(BotCheatMask::health)) if (HasCheat(BotCheatMask::health))
bot->SetFullHealth(); bot->SetFullHealth();
if (HasCheat(BotCheatMask::mana) && bot->getPowerType() == POWER_MANA) if (HasCheat(BotCheatMask::mana) && bot->getPowerType() == POWER_MANA)
bot->SetPower(POWER_MANA, bot->GetMaxPower(POWER_MANA)); bot->SetPower(POWER_MANA, bot->GetMaxPower(POWER_MANA));
if (HasCheat(BotCheatMask::power) && bot->getPowerType() != POWER_MANA) if (HasCheat(BotCheatMask::power) && bot->getPowerType() != POWER_MANA)
bot->SetPower(bot->getPowerType(), bot->GetMaxPower(bot->getPowerType())); bot->SetPower(bot->getPowerType(), bot->GetMaxPower(bot->getPowerType()));
} }
AllowActivity(); AllowActivity();
if (!CanUpdateAI()) if (!CanUpdateAI())
return; return;
// Handle the current spell
Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL); Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL);
if (!currentSpell) if (!currentSpell)
currentSpell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL); currentSpell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
if (currentSpell && currentSpell->GetSpellInfo() && currentSpell->getState() == SPELL_STATE_PREPARING)
if (currentSpell)
{ {
const SpellInfo* spellInfo = currentSpell->GetSpellInfo(); const SpellInfo* spellInfo = currentSpell->GetSpellInfo();
if (spellInfo && currentSpell->getState() == SPELL_STATE_PREPARING)
{
Unit* spellTarget = currentSpell->m_targets.GetUnitTarget(); Unit* spellTarget = currentSpell->m_targets.GetUnitTarget();
// interrupt if target is dead // Interrupt if target is dead or spell can't target dead units
if (spellTarget && !spellTarget->IsAlive() && if (spellTarget && !spellTarget->IsAlive() && !spellInfo->IsAllowingDeadTarget())
!spellInfo->IsAllowingDeadTarget())
{ {
InterruptSpell(); InterruptSpell();
YieldThread(GetReactDelay()); YieldThread(GetReactDelay());
@@ -346,18 +280,21 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
if (!spellInfo->Effects[i].Effect) if (!spellInfo->Effects[i].Effect)
continue; continue;
// Check if spell is a heal
if (spellInfo->Effects[i].Effect == SPELL_EFFECT_HEAL || if (spellInfo->Effects[i].Effect == SPELL_EFFECT_HEAL ||
spellInfo->Effects[i].Effect == SPELL_EFFECT_HEAL_MAX_HEALTH || spellInfo->Effects[i].Effect == SPELL_EFFECT_HEAL_MAX_HEALTH ||
spellInfo->Effects[i].Effect == SPELL_EFFECT_HEAL_MECHANICAL) spellInfo->Effects[i].Effect == SPELL_EFFECT_HEAL_MECHANICAL)
isHeal = true; isHeal = true;
// Check if spell is single-target
if ((spellInfo->Effects[i].TargetA.GetTarget() && spellInfo->Effects[i].TargetA.GetTarget() != TARGET_UNIT_TARGET_ALLY) || if ((spellInfo->Effects[i].TargetA.GetTarget() && spellInfo->Effects[i].TargetA.GetTarget() != TARGET_UNIT_TARGET_ALLY) ||
(spellInfo->Effects[i].TargetB.GetTarget() && spellInfo->Effects[i].TargetB.GetTarget() != TARGET_UNIT_TARGET_ALLY)) (spellInfo->Effects[i].TargetB.GetTarget() && spellInfo->Effects[i].TargetB.GetTarget() != TARGET_UNIT_TARGET_ALLY))
{ {
isSingleTarget = false; isSingleTarget = false;
} }
} }
// interrupt if target ally has full health (heal by other member)
// Interrupt if target ally has full health (heal by other member)
if (isHeal && isSingleTarget && spellTarget && spellTarget->IsFullHealth()) if (isHeal && isSingleTarget && spellTarget && spellTarget->IsFullHealth())
{ {
InterruptSpell(); InterruptSpell();
@@ -365,16 +302,19 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
return; return;
} }
// Ensure bot is facing target if necessary
if (spellTarget && !bot->HasInArc(CAST_ANGLE_IN_FRONT, spellTarget) && (spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT)) if (spellTarget && !bot->HasInArc(CAST_ANGLE_IN_FRONT, spellTarget) && (spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT))
{ {
sServerFacade->SetFacingTo(bot, spellTarget); sServerFacade->SetFacingTo(bot, spellTarget);
} }
// wait for spell cast // Wait for spell cast
YieldThread(GetReactDelay()); YieldThread(GetReactDelay());
return; return;
} }
}
// Handle transport check delay
if (nextTransportCheck > elapsed) if (nextTransportCheck > elapsed)
nextTransportCheck -= elapsed; nextTransportCheck -= elapsed;
else else
@@ -385,6 +325,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
nextTransportCheck = 1000; nextTransportCheck = 1000;
Transport* newTransport = bot->GetMap()->GetTransportForPos(bot->GetPhaseMask(), bot->GetPositionX(), Transport* newTransport = bot->GetMap()->GetTransportForPos(bot->GetPhaseMask(), bot->GetPositionX(),
bot->GetPositionY(), bot->GetPositionZ(), bot); bot->GetPositionY(), bot->GetPositionZ(), bot);
if (newTransport != bot->GetTransport()) if (newTransport != bot->GetTransport())
{ {
LOG_DEBUG("playerbots", "Bot {} is on a transport", bot->GetName()); LOG_DEBUG("playerbots", "Bot {} is on a transport", bot->GetName());
@@ -399,10 +340,26 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
} }
} }
if (!bot->InBattleground() && !bot->inRandomLfgDungeon() && bot->GetGroup() && !bot->GetGroup()->isLFGGroup()) // Update the bot's group status (moved to helper function)
UpdateAIGroupMembership();
// Update internal AI
UpdateAIInternal(elapsed, minimal);
YieldThread(GetReactDelay());
}
// Helper function for UpdateAI to check group membership and handle removal if necessary
void PlayerbotAI::UpdateAIGroupMembership()
{
if (!bot || !bot->GetGroup())
return;
Group* group = bot->GetGroup();
if (!bot->InBattleground() && !bot->inRandomLfgDungeon() && !group->isLFGGroup())
{ {
Player* leader = bot->GetGroup()->GetLeader(); Player* leader = group->GetLeader();
if (leader && leader != bot) // Checks if the leader is valid and is not the bot itself if (leader && leader != bot) // Ensure the leader is valid and not the bot itself
{ {
PlayerbotAI* leaderAI = GET_PLAYERBOT_AI(leader); PlayerbotAI* leaderAI = GET_PLAYERBOT_AI(leader);
if (leaderAI && !leaderAI->IsRealPlayer()) if (leaderAI && !leaderAI->IsRealPlayer())
@@ -412,18 +369,21 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
} }
} }
} }
else if (group->isLFGGroup())
if (bot->GetGroup() && bot->GetGroup()->isLFGGroup())
{ {
bool hasRealPlayer = false; bool hasRealPlayer = false;
for (GroupReference* ref = bot->GetGroup()->GetFirstMember(); ref; ref = ref->next())
// 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(); Player* member = ref->GetSource();
if (!member) if (!member)
continue; continue;
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member); PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
if (memberAI && !memberAI->IsRealPlayer()) if (memberAI && !memberAI->IsRealPlayer())
continue; continue;
hasRealPlayer = true; hasRealPlayer = true;
break; break;
} }
@@ -433,10 +393,6 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
ResetStrategies(); ResetStrategies();
} }
} }
bool min = minimal;
UpdateAIInternal(elapsed, min);
YieldThread(GetReactDelay());
} }
void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal) void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal)
@@ -1307,8 +1263,9 @@ void PlayerbotAI::DoNextAction(bool min)
return; return;
} }
// change engine if just died // Change engine if just died
if (currentEngine != engines[BOT_STATE_DEAD] && !bot->IsAlive()) bool isBotAlive = bot->IsAlive();
if (currentEngine != engines[BOT_STATE_DEAD] && !isBotAlive)
{ {
bot->StopMoving(); bot->StopMoving();
bot->GetMotionMaster()->Clear(); bot->GetMotionMaster()->Clear();
@@ -1331,17 +1288,18 @@ void PlayerbotAI::DoNextAction(bool min)
return; return;
} }
// change engine if just ressed // Change engine if just ressed
if (currentEngine == engines[BOT_STATE_DEAD] && bot->IsAlive()) if (currentEngine == engines[BOT_STATE_DEAD] && isBotAlive)
{ {
ChangeEngine(BOT_STATE_NON_COMBAT); ChangeEngine(BOT_STATE_NON_COMBAT);
return; return;
} }
// if in combat but stick with old data - clear targets // Clear targets if in combat but sticking with old data
if (currentEngine == engines[BOT_STATE_NON_COMBAT] && bot->IsInCombat()) if (currentEngine == engines[BOT_STATE_NON_COMBAT] && bot->IsInCombat())
{ {
if (aiObjectContext->GetValue<Unit*>("current target")->Get() != nullptr) Unit* currentTarget = aiObjectContext->GetValue<Unit*>("current target")->Get();
if (currentTarget != nullptr)
{ {
aiObjectContext->GetValue<Unit*>("current target")->Set(nullptr); aiObjectContext->GetValue<Unit*>("current target")->Set(nullptr);
} }
@@ -1367,7 +1325,7 @@ void PlayerbotAI::DoNextAction(bool min)
if (master) if (master)
masterBotAI = GET_PLAYERBOT_AI(master); masterBotAI = GET_PLAYERBOT_AI(master);
// test BG master set // Test BG master set
if ((!master || (masterBotAI && !masterBotAI->IsRealPlayer())) && group) if ((!master || (masterBotAI && !masterBotAI->IsRealPlayer())) && group)
{ {
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
@@ -1375,6 +1333,7 @@ void PlayerbotAI::DoNextAction(bool min)
{ {
return; return;
} }
// Ideally we want to have the leader as master. // Ideally we want to have the leader as master.
Player* newMaster = botAI->GetGroupMaster(); Player* newMaster = botAI->GetGroupMaster();
Player* playerMaster = nullptr; Player* playerMaster = nullptr;
@@ -1385,20 +1344,7 @@ void PlayerbotAI::DoNextAction(bool min)
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{ {
Player* member = gref->GetSource(); Player* member = gref->GetSource();
if (!member || member == bot || member == newMaster || !member->IsInWorld() || !member->IsInSameRaidWith(bot))
if (!member)
continue;
if (member == bot)
continue;
if (member == newMaster)
continue;
if (!member->IsInWorld())
continue;
if (!member->IsInSameRaidWith(bot))
continue; continue;
PlayerbotAI* memberBotAI = GET_PLAYERBOT_AI(member); PlayerbotAI* memberBotAI = GET_PLAYERBOT_AI(member);
@@ -1410,37 +1356,19 @@ void PlayerbotAI::DoNextAction(bool min)
continue; continue;
} }
// same BG // Same BG checks (optimize checking conditions here)
if (bot->InBattleground() && bot->GetBattleground() && if (bot->InBattleground() && bot->GetBattleground() &&
bot->GetBattleground()->GetBgTypeID() == BATTLEGROUND_AV && !GET_PLAYERBOT_AI(member) && bot->GetBattleground()->GetBgTypeID() == BATTLEGROUND_AV && !GET_PLAYERBOT_AI(member) &&
member->InBattleground() && bot->GetMapId() == member->GetMapId()) member->InBattleground() && bot->GetMapId() == member->GetMapId())
{ {
// TODO disable move to objective if have master in bg // Skip if same BG but same subgroup or lower level
if (!group->SameSubGroup(bot, member) || member->GetLevel() < bot->GetLevel())
continue; continue;
if (!group->SameSubGroup(bot, member)) // Follow real player only if higher honor points
continue;
if (member->GetLevel() < bot->GetLevel())
continue;
// follow real player only if he has more honor/arena points
if (bot->GetBattleground()->isArena())
{
if (group->IsLeader(member->GetGUID()))
{
playerMaster = member;
break;
}
else
continue;
}
else
{
uint32 honorpts = member->GetHonorPoints(); uint32 honorpts = member->GetHonorPoints();
if (bot->GetHonorPoints() && honorpts < bot->GetHonorPoints()) if (bot->GetHonorPoints() && honorpts < bot->GetHonorPoints())
continue; continue;
}
playerMaster = member; playerMaster = member;
continue; continue;
@@ -1450,7 +1378,6 @@ void PlayerbotAI::DoNextAction(bool min)
continue; continue;
newMaster = member; newMaster = member;
break; break;
} }
} }
@@ -1474,15 +1401,15 @@ void PlayerbotAI::DoNextAction(bool min)
if (master && master->IsInWorld()) if (master && master->IsInWorld())
{ {
if (master->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_WALKING) && float distance = sServerFacade->GetDistance2d(bot, master);
sServerFacade->GetDistance2d(bot, master) < 20.0f) if (master->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_WALKING) && distance < 20.0f)
bot->m_movementInfo.AddMovementFlag(MOVEMENTFLAG_WALKING); bot->m_movementInfo.AddMovementFlag(MOVEMENTFLAG_WALKING);
else else
bot->m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_WALKING); bot->m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_WALKING);
if (master->IsSitState() && nextAICheckDelay < 1000) if (master->IsSitState() && nextAICheckDelay < 1000)
{ {
if (!bot->isMoving() && sServerFacade->GetDistance2d(bot, master) < 10.0f) if (!bot->isMoving() && distance < 10.0f)
bot->SetStandState(UNIT_STAND_STATE_SIT); bot->SetStandState(UNIT_STAND_STATE_SIT);
} }
else if (nextAICheckDelay < 1000) else if (nextAICheckDelay < 1000)
@@ -1500,51 +1427,6 @@ void PlayerbotAI::DoNextAction(bool min)
bot->RemoveAurasByType(SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED); bot->RemoveAurasByType(SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED);
bot->RemoveAurasByType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED); bot->RemoveAurasByType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED);
} }
// if (bot->IsFlying() && !bot->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) &&
// !bot->HasAuraType(SPELL_AURA_FLY))
// {
// if (bot->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FLYING))
// bot->m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_FLYING);
// if (bot->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_CAN_FLY))
// bot->m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_CAN_FLY);
// if (bot->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY))
// bot->m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
// }
/*
// land after kncokback/jump
if (bot->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FALLING))
{
// stop movement
bot->StopMoving();
bot->GetMotionMaster()->Clear();
bot->GetMotionMaster()->MoveIdle();
// remove moveFlags
bot->m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_FALLING);
bot->m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_PENDING_STOP);
// set jump destination
bot->m_movementInfo.pos = !GetJumpDestination().m_positionZ == 0 ? GetJumpDestination() :
bot->GetPosition(); bot->m_movementInfo.jump = MovementInfo::JumpInfo();
WorldPacket land(MSG_MOVE_FALL_LAND);
land << bot->GetGUID().WriteAsPacked();
bot->m_mover->BuildMovementPacket(&land);
bot->GetSession()->HandleMovementOpcodes(land);
// move stop
WorldPacket stop(MSG_MOVE_STOP);
stop << bot->GetGUID().WriteAsPacked();
bot->m_mover->BuildMovementPacket(&stop);
bot->GetSession()->HandleMovementOpcodes(stop);
ResetJumpDestination();
}
*/
} }
void PlayerbotAI::ReInitCurrentEngine() void PlayerbotAI::ReInitCurrentEngine()
@@ -1835,33 +1717,28 @@ bool PlayerbotAI::IsHealAssistantOfIndex(Player* player, int index)
{ {
return false; return false;
} }
Group::MemberSlotList const& slots = group->GetMemberSlots(); Group::MemberSlotList const& slots = group->GetMemberSlots();
int counter = 0; int counter = 0;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{ {
Player* member = ref->GetSource(); Player* member = ref->GetSource();
if (group->IsAssistant(member->GetGUID()) && IsHeal(member))
if (IsHeal(member)) // Check if the member is a healer
{ {
if (index == counter) bool isAssistant = group->IsAssistant(member->GetGUID());
{
return player == member; // Check if the index matches for both assistant and non-assistant healers
} if ((isAssistant && index == counter) || (!isAssistant && index == counter))
counter++;
}
}
// not enough
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!group->IsAssistant(member->GetGUID()) && IsHeal(member))
{
if (index == counter)
{ {
return player == member; return player == member;
} }
counter++; counter++;
} }
} }
return false; return false;
} }
@@ -1872,33 +1749,28 @@ bool PlayerbotAI::IsRangedDpsAssistantOfIndex(Player* player, int index)
{ {
return false; return false;
} }
Group::MemberSlotList const& slots = group->GetMemberSlots(); Group::MemberSlotList const& slots = group->GetMemberSlots();
int counter = 0; int counter = 0;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{ {
Player* member = ref->GetSource(); Player* member = ref->GetSource();
if (group->IsAssistant(member->GetGUID()) && IsRangedDps(member))
if (IsRangedDps(member)) // Check if the member is a ranged DPS
{ {
if (index == counter) bool isAssistant = group->IsAssistant(member->GetGUID());
{
return player == member; // Check the index for both assistant and non-assistant ranges
} if ((isAssistant && index == counter) || (!isAssistant && index == counter))
counter++;
}
}
// not enough
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!group->IsAssistant(member->GetGUID()) && IsRangedDps(member))
{
if (index == counter)
{ {
return player == member; return player == member;
} }
counter++; counter++;
} }
} }
return false; return false;
} }
@@ -2744,44 +2616,50 @@ bool PlayerbotAI::HasAura(std::string const name, Unit* unit, bool maxStack, boo
std::wstring wnamepart; std::wstring wnamepart;
if (!Utf8toWStr(name, wnamepart)) if (!Utf8toWStr(name, wnamepart))
return 0; return false;
wstrToLower(wnamepart); wstrToLower(wnamepart);
int auraAmount = 0; int auraAmount = 0;
// Iterate through all aura types
for (uint32 auraType = SPELL_AURA_BIND_SIGHT; auraType < TOTAL_AURAS; auraType++) for (uint32 auraType = SPELL_AURA_BIND_SIGHT; auraType < TOTAL_AURAS; auraType++)
{ {
Unit::AuraEffectList const& auras = unit->GetAuraEffectsByType((AuraType)auraType); Unit::AuraEffectList const& auras = unit->GetAuraEffectsByType((AuraType)auraType);
if (auras.empty()) if (auras.empty())
continue; continue;
// Iterate through each aura effect
for (AuraEffect const* aurEff : auras) for (AuraEffect const* aurEff : auras)
{ {
SpellInfo const* spellInfo = aurEff->GetSpellInfo(); if (!aurEff)
continue;
SpellInfo const* spellInfo = aurEff->GetSpellInfo();
if (!spellInfo)
continue;
// Check if the aura name matches
std::string_view const auraName = spellInfo->SpellName[0]; std::string_view const auraName = spellInfo->SpellName[0];
if (auraName.empty() || auraName.length() != wnamepart.length() || !Utf8FitTo(auraName, wnamepart)) if (auraName.empty() || auraName.length() != wnamepart.length() || !Utf8FitTo(auraName, wnamepart))
continue; continue;
// Check if this is a valid aura for the bot
if (IsRealAura(bot, aurEff, unit)) if (IsRealAura(bot, aurEff, unit))
{ {
if (checkIsOwner && aurEff) // Check caster if necessary
{ if (checkIsOwner && aurEff->GetCasterGUID() != bot->GetGUID())
if (aurEff->GetCasterGUID() != bot->GetGUID())
continue; continue;
}
if (checkDuration && aurEff) // Check aura duration if necessary
{ if (checkDuration && aurEff->GetBase()->GetDuration() == -1)
if (aurEff->GetBase()->GetDuration() == -1)
{
continue; continue;
}
} // Count stacks and charges
uint32 maxStackAmount = spellInfo->StackAmount; uint32 maxStackAmount = spellInfo->StackAmount;
uint32 maxProcCharges = spellInfo->ProcCharges; uint32 maxProcCharges = spellInfo->ProcCharges;
// Count the aura based on max stack and proc charges
if (maxStack) if (maxStack)
{ {
if (maxStackAmount && aurEff->GetBase()->GetStackAmount() >= maxStackAmount) if (maxStackAmount && aurEff->GetBase()->GetStackAmount() >= maxStackAmount)
@@ -2794,12 +2672,15 @@ bool PlayerbotAI::HasAura(std::string const name, Unit* unit, bool maxStack, boo
{ {
auraAmount++; auraAmount++;
} }
if (maxAuraAmount < 0)
return auraAmount > 0; // Early exit if maxAuraAmount is reached
if (maxAuraAmount < 0 && auraAmount > 0)
return true;
} }
} }
} }
// Return based on the maximum aura amount conditions
if (maxAuraAmount >= 0) if (maxAuraAmount >= 0)
{ {
return auraAmount == maxAuraAmount || (auraAmount > 0 && auraAmount <= maxAuraAmount); return auraAmount == maxAuraAmount || (auraAmount > 0 && auraAmount <= maxAuraAmount);
@@ -2824,6 +2705,8 @@ bool PlayerbotAI::HasAura(uint32 spellId, Unit const* unit)
// return false; // return false;
} }
// SAW
Aura* PlayerbotAI::GetAura(std::string const name, Unit* unit, bool checkIsOwner, bool checkDuration, int checkStack) Aura* PlayerbotAI::GetAura(std::string const name, Unit* unit, bool checkIsOwner, bool checkDuration, int checkStack)
{ {
if (!unit) if (!unit)
@@ -2835,7 +2718,7 @@ Aura* PlayerbotAI::GetAura(std::string const name, Unit* unit, bool checkIsOwner
wstrToLower(wnamepart); wstrToLower(wnamepart);
for (uint32 auraType = SPELL_AURA_BIND_SIGHT; auraType < TOTAL_AURAS; auraType++) for (uint32 auraType = SPELL_AURA_BIND_SIGHT; auraType < TOTAL_AURAS; ++auraType)
{ {
Unit::AuraEffectList const& auras = unit->GetAuraEffectsByType((AuraType)auraType); Unit::AuraEffectList const& auras = unit->GetAuraEffectsByType((AuraType)auraType);
if (auras.empty()) if (auras.empty())
@@ -2844,38 +2727,30 @@ Aura* PlayerbotAI::GetAura(std::string const name, Unit* unit, bool checkIsOwner
for (AuraEffect const* aurEff : auras) for (AuraEffect const* aurEff : auras)
{ {
SpellInfo const* spellInfo = aurEff->GetSpellInfo(); SpellInfo const* spellInfo = aurEff->GetSpellInfo();
std::string const& auraName = spellInfo->SpellName[0];
std::string const auraName = spellInfo->SpellName[0]; // Directly skip if name mismatch (both length and content)
if (auraName.empty() || auraName.length() != wnamepart.length() || !Utf8FitTo(auraName, wnamepart)) if (auraName.empty() || auraName.length() != wnamepart.length() || !Utf8FitTo(auraName, wnamepart))
continue; continue;
if (IsRealAura(bot, aurEff, unit)) if (!IsRealAura(bot, aurEff, unit))
{
if (checkIsOwner && aurEff)
{
if (aurEff->GetCasterGUID() != bot->GetGUID())
continue; continue;
}
if (checkDuration && aurEff) // Check owner if necessary
{ if (checkIsOwner && aurEff->GetCasterGUID() != bot->GetGUID())
if (aurEff->GetBase()->GetDuration() == -1)
{
continue; continue;
}
}
if (checkStack != -1 && aurEff) // Check duration if necessary
{ if (checkDuration && aurEff->GetBase()->GetDuration() == -1)
if (aurEff->GetBase()->GetStackAmount() < checkStack)
{
continue; continue;
}
} // Check stack if necessary
if (checkStack != -1 && aurEff->GetBase()->GetStackAmount() < checkStack)
continue;
return aurEff->GetBase(); return aurEff->GetBase();
} }
} }
}
return nullptr; return nullptr;
} }
@@ -2889,15 +2764,14 @@ bool PlayerbotAI::HasAnyAuraOf(Unit* player, ...)
va_start(vl, player); va_start(vl, player);
const char* cur; const char* cur;
do while ((cur = va_arg(vl, const char*)) != nullptr)
{ {
cur = va_arg(vl, const char*); if (HasAura(cur, player))
if (cur && HasAura(cur, player))
{ {
va_end(vl); va_end(vl);
return true; return true;
} }
} while (cur); }
va_end(vl); va_end(vl);
return false; return false;
@@ -4285,7 +4159,6 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
} }
} }
// In bg queue. Speed up bg queue/join. // In bg queue. Speed up bg queue/join.
if (bot->InBattlegroundQueue()) if (bot->InBattlegroundQueue())
{ {
@@ -4937,124 +4810,62 @@ void PlayerbotAI::Ping(float x, float y)
} }
} }
// Find Poison ...Natsukawa // Helper function to iterate through items in the inventory and bags
Item* PlayerbotAI::FindItemInInventory(std::function<bool(ItemTemplate const*)> checkItem) const
{
// List out items in the main backpack
for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; ++slot)
{
if (Item* const pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
{
ItemTemplate const* pItemProto = pItem->GetTemplate();
if (pItemProto && bot->CanUseItem(pItemProto) == EQUIP_ERR_OK && checkItem(pItemProto))
return pItem;
}
}
// List out items in other removable backpacks
for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
{
if (Bag const* const pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag))
{
for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot)
{
if (Item* const pItem = bot->GetItemByPos(bag, slot))
{
ItemTemplate const* pItemProto = pItem->GetTemplate();
if (pItemProto && bot->CanUseItem(pItemProto) == EQUIP_ERR_OK && checkItem(pItemProto))
return pItem;
}
}
}
}
return nullptr;
}
// Find Poison
Item* PlayerbotAI::FindPoison() const Item* PlayerbotAI::FindPoison() const
{ {
// list out items in main backpack return FindItemInInventory([](ItemTemplate const* pItemProto) -> bool {
for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++) return pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == 6;
{ });
if (Item* const pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
{
ItemTemplate const* pItemProto = pItem->GetTemplate();
if (!pItemProto || bot->CanUseItem(pItemProto) != EQUIP_ERR_OK)
continue;
if (pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == 6)
return pItem;
}
}
// list out items in other removable backpacks
for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
{
if (Bag const* pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag))
{
for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot)
{
if (Item* const pItem = bot->GetItemByPos(bag, slot))
{
ItemTemplate const* pItemProto = pItem->GetTemplate();
if (!pItemProto || bot->CanUseItem(pItemProto) != EQUIP_ERR_OK)
continue;
if (pItemProto->Class == ITEM_CLASS_CONSUMABLE &&
pItemProto->SubClass == ITEM_SUBCLASS_ITEM_ENHANCEMENT)
return pItem;
}
}
}
}
return nullptr;
} }
// Find Consumable
Item* PlayerbotAI::FindConsumable(uint32 displayId) const Item* PlayerbotAI::FindConsumable(uint32 displayId) const
{ {
// list out items in main backpack return FindItemInInventory([displayId](ItemTemplate const* pItemProto) -> bool {
for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++) return (pItemProto->Class == ITEM_CLASS_CONSUMABLE || pItemProto->Class == ITEM_CLASS_TRADE_GOODS) && pItemProto->DisplayInfoID == displayId;
{ });
if (Item* const pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
{
ItemTemplate const* pItemProto = pItem->GetTemplate();
if (!pItemProto || bot->CanUseItem(pItemProto) != EQUIP_ERR_OK)
continue;
if ((pItemProto->Class == ITEM_CLASS_CONSUMABLE || pItemProto->Class == ITEM_CLASS_TRADE_GOODS) &&
pItemProto->DisplayInfoID == displayId)
return pItem;
}
}
// list out items in other removable backpacks
for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
{
if (Bag const* const pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag))
{
for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot)
{
if (Item* const pItem = bot->GetItemByPos(bag, slot))
{
ItemTemplate const* pItemProto = pItem->GetTemplate();
if (!pItemProto || bot->CanUseItem(pItemProto) != EQUIP_ERR_OK)
continue;
if ((pItemProto->Class == ITEM_CLASS_CONSUMABLE || pItemProto->Class == ITEM_CLASS_TRADE_GOODS) &&
pItemProto->DisplayInfoID == displayId)
return pItem;
}
}
}
}
return nullptr;
} }
// Find Bandage
Item* PlayerbotAI::FindBandage() const Item* PlayerbotAI::FindBandage() const
{ {
// list out items in main backpack return FindItemInInventory([](ItemTemplate const* pItemProto) -> bool {
for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++) return pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_BANDAGE;
{ });
if (Item* const pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
{
ItemTemplate const* pItemProto = pItem->GetTemplate();
if (!pItemProto || bot->CanUseItem(pItemProto) != EQUIP_ERR_OK)
continue;
if (pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_BANDAGE)
return pItem;
}
}
// list out items in other removable backpacks
for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
{
if (Bag const* const pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag))
{
for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot)
{
if (Item* const pItem = bot->GetItemByPos(bag, slot))
{
ItemTemplate const* pItemProto = pItem->GetTemplate();
if (!pItemProto || bot->CanUseItem(pItemProto) != EQUIP_ERR_OK)
continue;
if (pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_BANDAGE)
return pItem;
}
}
}
}
return nullptr;
} }
static const uint32 uPriorizedSharpStoneIds[8] = {ADAMANTITE_SHARPENING_DISPLAYID, FEL_SHARPENING_DISPLAYID, static const uint32 uPriorizedSharpStoneIds[8] = {ADAMANTITE_SHARPENING_DISPLAYID, FEL_SHARPENING_DISPLAYID,
@@ -6057,53 +5868,66 @@ std::set<uint32> PlayerbotAI::GetCurrentIncompleteQuestIds()
uint32 PlayerbotAI::GetReactDelay() uint32 PlayerbotAI::GetReactDelay()
{ {
uint32 base = sPlayerbotAIConfig->reactDelay; uint32 base = sPlayerbotAIConfig->reactDelay; // Default 100(ms)
// old calculate method
// If dynamic react delay is disabled, use a static calculation
if (!sPlayerbotAIConfig->dynamicReactDelay) if (!sPlayerbotAIConfig->dynamicReactDelay)
{ {
inCombat = bot->IsInCombat(); if (HasRealPlayerMaster())
bool min = false; return base;
// test fix lags because of BG
bool inBG = bot->InBattleground() || bot->InArena();
if (bot && !inCombat)
min = true;
if (HasRealPlayerMaster() || (sPlayerbotAIConfig->fastReactInBG && inBG)) bool inBG = bot->InBattleground() || bot->InArena();
min = false; if (sPlayerbotAIConfig->fastReactInBG && inBG)
if (min) return base;
return base * 10;
bool inCombat = bot->IsInCombat();
if (!inCombat)
return base * 10.0f;
else if (inCombat)
return base * 2.5f;
return base; return base;
} }
float multiplier = 1.0f; // Dynamic react delay calculation:
if (HasRealPlayerMaster()) if (HasRealPlayerMaster())
return base;
float multiplier = 1.0f;
bool inBG = bot->InBattleground() || bot->InArena();
if (inBG)
{ {
multiplier = 1.0f; if (bot->IsInCombat() || currentState == BOT_STATE_COMBAT)
{
multiplier = sPlayerbotAIConfig->fastReactInBG ? 2.5f : 5.0f;
return base * multiplier; return base * multiplier;
} }
else
bool inBg = bot->InBattleground() || bot->InArena();
if (inBg)
{ {
multiplier = sPlayerbotAIConfig->fastReactInBG ? 1.0f : 10.0f; multiplier = sPlayerbotAIConfig->fastReactInBG ? 1.0f : 10.0f;
return base * multiplier; return base * multiplier;
} }
}
// When in combat, return 5 times the base
if (bot->IsInCombat() || currentState == BOT_STATE_COMBAT) if (bot->IsInCombat() || currentState == BOT_STATE_COMBAT)
{ {
multiplier = 5.0f; multiplier = 5.0f;
return base * multiplier; return base * multiplier;
} }
bool isResting = bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING); // When not resting, return 10-30 times the base
if (!isResting) if (!bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING))
{ {
multiplier = urand(10, 30); multiplier = urand(10, 30);
return base * multiplier; return base * multiplier;
} }
// In other cases, return 20-200 times the base
multiplier = urand(20, 200); multiplier = urand(20, 200);
return base * multiplier; return base * multiplier;
} }

View File

@@ -579,7 +579,8 @@ private:
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore, static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore,
bool mixed = false); bool mixed = false);
bool IsTellAllowed(PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL); bool IsTellAllowed(PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
void UpdateAIGroupMembership();
Item* FindItemInInventory(std::function<bool(ItemTemplate const*)> checkItem) const;
void HandleCommands(); void HandleCommands();
void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL); void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL);
bool _isBotInitializing = false; bool _isBotInitializing = false;