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)
{
// Handle the AI check delay
if (nextAICheckDelay > elapsed)
nextAICheckDelay -= elapsed;
else
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;
}
if (!bot->GetSession() || bot->GetSession()->isLogingOut())
{
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))
// 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))
{
if (HasCheat(BotCheatMask::health))
bot->SetFullHealth();
if (HasCheat(BotCheatMask::mana) && bot->getPowerType() == POWER_MANA)
bot->SetPower(POWER_MANA, bot->GetMaxPower(POWER_MANA));
if (HasCheat(BotCheatMask::power) && bot->getPowerType() != POWER_MANA)
bot->SetPower(bot->getPowerType(), bot->GetMaxPower(bot->getPowerType()));
}
AllowActivity();
if (!CanUpdateAI())
return;
// Handle the current spell
Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL);
if (!currentSpell)
currentSpell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
if (currentSpell && currentSpell->GetSpellInfo() && currentSpell->getState() == SPELL_STATE_PREPARING)
if (currentSpell)
{
const SpellInfo* spellInfo = currentSpell->GetSpellInfo();
if (spellInfo && currentSpell->getState() == SPELL_STATE_PREPARING)
{
Unit* spellTarget = currentSpell->m_targets.GetUnitTarget();
// interrupt if target is dead
if (spellTarget && !spellTarget->IsAlive() &&
!spellInfo->IsAllowingDeadTarget())
// Interrupt if target is dead or spell can't target dead units
if (spellTarget && !spellTarget->IsAlive() && !spellInfo->IsAllowingDeadTarget())
{
InterruptSpell();
YieldThread(GetReactDelay());
@@ -346,18 +280,21 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
if (!spellInfo->Effects[i].Effect)
continue;
// Check if spell is a 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_MECHANICAL)
isHeal = true;
// Check if spell is single-target
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))
{
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())
{
InterruptSpell();
@@ -365,16 +302,19 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
return;
}
// Ensure bot is facing target if necessary
if (spellTarget && !bot->HasInArc(CAST_ANGLE_IN_FRONT, spellTarget) && (spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT))
{
sServerFacade->SetFacingTo(bot, spellTarget);
}
// wait for spell cast
// Wait for spell cast
YieldThread(GetReactDelay());
return;
}
}
// Handle transport check delay
if (nextTransportCheck > elapsed)
nextTransportCheck -= elapsed;
else
@@ -385,6 +325,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
nextTransportCheck = 1000;
Transport* newTransport = bot->GetMap()->GetTransportForPos(bot->GetPhaseMask(), bot->GetPositionX(),
bot->GetPositionY(), bot->GetPositionZ(), bot);
if (newTransport != bot->GetTransport())
{
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();
if (leader && leader != bot) // Checks if the leader is valid and is not the bot itself
Player* leader = group->GetLeader();
if (leader && leader != bot) // Ensure the leader is valid and not the bot itself
{
PlayerbotAI* leaderAI = GET_PLAYERBOT_AI(leader);
if (leaderAI && !leaderAI->IsRealPlayer())
@@ -412,18 +369,21 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
}
}
}
if (bot->GetGroup() && bot->GetGroup()->isLFGGroup())
else if (group->isLFGGroup())
{
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();
if (!member)
continue;
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
if (memberAI && !memberAI->IsRealPlayer())
continue;
hasRealPlayer = true;
break;
}
@@ -433,10 +393,6 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
ResetStrategies();
}
}
bool min = minimal;
UpdateAIInternal(elapsed, min);
YieldThread(GetReactDelay());
}
void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal)
@@ -1307,8 +1263,9 @@ void PlayerbotAI::DoNextAction(bool min)
return;
}
// change engine if just died
if (currentEngine != engines[BOT_STATE_DEAD] && !bot->IsAlive())
// Change engine if just died
bool isBotAlive = bot->IsAlive();
if (currentEngine != engines[BOT_STATE_DEAD] && !isBotAlive)
{
bot->StopMoving();
bot->GetMotionMaster()->Clear();
@@ -1331,17 +1288,18 @@ void PlayerbotAI::DoNextAction(bool min)
return;
}
// change engine if just ressed
if (currentEngine == engines[BOT_STATE_DEAD] && bot->IsAlive())
// Change engine if just ressed
if (currentEngine == engines[BOT_STATE_DEAD] && isBotAlive)
{
ChangeEngine(BOT_STATE_NON_COMBAT);
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 (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);
}
@@ -1367,7 +1325,7 @@ void PlayerbotAI::DoNextAction(bool min)
if (master)
masterBotAI = GET_PLAYERBOT_AI(master);
// test BG master set
// Test BG master set
if ((!master || (masterBotAI && !masterBotAI->IsRealPlayer())) && group)
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
@@ -1375,6 +1333,7 @@ void PlayerbotAI::DoNextAction(bool min)
{
return;
}
// Ideally we want to have the leader as master.
Player* newMaster = botAI->GetGroupMaster();
Player* playerMaster = nullptr;
@@ -1385,20 +1344,7 @@ void PlayerbotAI::DoNextAction(bool min)
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{
Player* member = gref->GetSource();
if (!member)
continue;
if (member == bot)
continue;
if (member == newMaster)
continue;
if (!member->IsInWorld())
continue;
if (!member->IsInSameRaidWith(bot))
if (!member || member == bot || member == newMaster || !member->IsInWorld() || !member->IsInSameRaidWith(bot))
continue;
PlayerbotAI* memberBotAI = GET_PLAYERBOT_AI(member);
@@ -1410,37 +1356,19 @@ void PlayerbotAI::DoNextAction(bool min)
continue;
}
// same BG
// 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())
{
// 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;
if (!group->SameSubGroup(bot, member))
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
{
// Follow real player only if higher honor points
uint32 honorpts = member->GetHonorPoints();
if (bot->GetHonorPoints() && honorpts < bot->GetHonorPoints())
continue;
}
playerMaster = member;
continue;
@@ -1450,7 +1378,6 @@ void PlayerbotAI::DoNextAction(bool min)
continue;
newMaster = member;
break;
}
}
@@ -1474,15 +1401,15 @@ void PlayerbotAI::DoNextAction(bool min)
if (master && master->IsInWorld())
{
if (master->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_WALKING) &&
sServerFacade->GetDistance2d(bot, master) < 20.0f)
float distance = sServerFacade->GetDistance2d(bot, master);
if (master->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_WALKING) && distance < 20.0f)
bot->m_movementInfo.AddMovementFlag(MOVEMENTFLAG_WALKING);
else
bot->m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_WALKING);
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);
}
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_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()
@@ -1835,33 +1717,28 @@ bool PlayerbotAI::IsHealAssistantOfIndex(Player* player, int index)
{
return false;
}
Group::MemberSlotList const& slots = group->GetMemberSlots();
int counter = 0;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (group->IsAssistant(member->GetGUID()) && IsHeal(member))
if (IsHeal(member)) // Check if the member is a healer
{
if (index == counter)
{
return player == member;
}
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)
bool isAssistant = group->IsAssistant(member->GetGUID());
// Check if the index matches for both assistant and non-assistant healers
if ((isAssistant && index == counter) || (!isAssistant && index == counter))
{
return player == member;
}
counter++;
}
}
return false;
}
@@ -1872,33 +1749,28 @@ bool PlayerbotAI::IsRangedDpsAssistantOfIndex(Player* player, int index)
{
return false;
}
Group::MemberSlotList const& slots = group->GetMemberSlots();
int counter = 0;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
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)
{
return player == member;
}
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)
bool isAssistant = group->IsAssistant(member->GetGUID());
// Check the index for both assistant and non-assistant ranges
if ((isAssistant && index == counter) || (!isAssistant && index == counter))
{
return player == member;
}
counter++;
}
}
return false;
}
@@ -2744,44 +2616,50 @@ bool PlayerbotAI::HasAura(std::string const name, Unit* unit, bool maxStack, boo
std::wstring wnamepart;
if (!Utf8toWStr(name, wnamepart))
return 0;
return false;
wstrToLower(wnamepart);
int auraAmount = 0;
// Iterate through all aura types
for (uint32 auraType = SPELL_AURA_BIND_SIGHT; auraType < TOTAL_AURAS; auraType++)
{
Unit::AuraEffectList const& auras = unit->GetAuraEffectsByType((AuraType)auraType);
if (auras.empty())
continue;
// Iterate through each aura effect
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];
if (auraName.empty() || auraName.length() != wnamepart.length() || !Utf8FitTo(auraName, wnamepart))
continue;
// Check if this is a valid aura for the bot
if (IsRealAura(bot, aurEff, unit))
{
if (checkIsOwner && aurEff)
{
if (aurEff->GetCasterGUID() != bot->GetGUID())
// Check caster if necessary
if (checkIsOwner && aurEff->GetCasterGUID() != bot->GetGUID())
continue;
}
if (checkDuration && aurEff)
{
if (aurEff->GetBase()->GetDuration() == -1)
{
// Check aura duration if necessary
if (checkDuration && aurEff->GetBase()->GetDuration() == -1)
continue;
}
}
// Count stacks and charges
uint32 maxStackAmount = spellInfo->StackAmount;
uint32 maxProcCharges = spellInfo->ProcCharges;
// Count the aura based on max stack and proc charges
if (maxStack)
{
if (maxStackAmount && aurEff->GetBase()->GetStackAmount() >= maxStackAmount)
@@ -2794,12 +2672,15 @@ bool PlayerbotAI::HasAura(std::string const name, Unit* unit, bool maxStack, boo
{
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)
{
return auraAmount == maxAuraAmount || (auraAmount > 0 && auraAmount <= maxAuraAmount);
@@ -2824,6 +2705,8 @@ bool PlayerbotAI::HasAura(uint32 spellId, Unit const* unit)
// return false;
}
// SAW
Aura* PlayerbotAI::GetAura(std::string const name, Unit* unit, bool checkIsOwner, bool checkDuration, int checkStack)
{
if (!unit)
@@ -2835,7 +2718,7 @@ Aura* PlayerbotAI::GetAura(std::string const name, Unit* unit, bool checkIsOwner
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);
if (auras.empty())
@@ -2844,38 +2727,30 @@ Aura* PlayerbotAI::GetAura(std::string const name, Unit* unit, bool checkIsOwner
for (AuraEffect const* aurEff : auras)
{
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))
continue;
if (IsRealAura(bot, aurEff, unit))
{
if (checkIsOwner && aurEff)
{
if (aurEff->GetCasterGUID() != bot->GetGUID())
if (!IsRealAura(bot, aurEff, unit))
continue;
}
if (checkDuration && aurEff)
{
if (aurEff->GetBase()->GetDuration() == -1)
{
// Check owner if necessary
if (checkIsOwner && aurEff->GetCasterGUID() != bot->GetGUID())
continue;
}
}
if (checkStack != -1 && aurEff)
{
if (aurEff->GetBase()->GetStackAmount() < checkStack)
{
// Check duration if necessary
if (checkDuration && aurEff->GetBase()->GetDuration() == -1)
continue;
}
}
// Check stack if necessary
if (checkStack != -1 && aurEff->GetBase()->GetStackAmount() < checkStack)
continue;
return aurEff->GetBase();
}
}
}
return nullptr;
}
@@ -2889,15 +2764,14 @@ bool PlayerbotAI::HasAnyAuraOf(Unit* player, ...)
va_start(vl, player);
const char* cur;
do
while ((cur = va_arg(vl, const char*)) != nullptr)
{
cur = va_arg(vl, const char*);
if (cur && HasAura(cur, player))
if (HasAura(cur, player))
{
va_end(vl);
return true;
}
} while (cur);
}
va_end(vl);
return false;
@@ -4285,7 +4159,6 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
}
}
// In bg queue. Speed up bg queue/join.
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
{
// list out items in 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)
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;
return FindItemInInventory([](ItemTemplate const* pItemProto) -> bool {
return pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == 6;
});
}
// Find Consumable
Item* PlayerbotAI::FindConsumable(uint32 displayId) const
{
// list out items in 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)
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;
return FindItemInInventory([displayId](ItemTemplate const* pItemProto) -> bool {
return (pItemProto->Class == ITEM_CLASS_CONSUMABLE || pItemProto->Class == ITEM_CLASS_TRADE_GOODS) && pItemProto->DisplayInfoID == displayId;
});
}
// Find Bandage
Item* PlayerbotAI::FindBandage() const
{
// list out items in 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)
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;
return FindItemInInventory([](ItemTemplate const* pItemProto) -> bool {
return pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_BANDAGE;
});
}
static const uint32 uPriorizedSharpStoneIds[8] = {ADAMANTITE_SHARPENING_DISPLAYID, FEL_SHARPENING_DISPLAYID,
@@ -6057,53 +5868,66 @@ std::set<uint32> PlayerbotAI::GetCurrentIncompleteQuestIds()
uint32 PlayerbotAI::GetReactDelay()
{
uint32 base = sPlayerbotAIConfig->reactDelay;
// old calculate method
uint32 base = sPlayerbotAIConfig->reactDelay; // Default 100(ms)
// If dynamic react delay is disabled, use a static calculation
if (!sPlayerbotAIConfig->dynamicReactDelay)
{
inCombat = bot->IsInCombat();
bool min = false;
// test fix lags because of BG
bool inBG = bot->InBattleground() || bot->InArena();
if (bot && !inCombat)
min = true;
if (HasRealPlayerMaster())
return base;
if (HasRealPlayerMaster() || (sPlayerbotAIConfig->fastReactInBG && inBG))
min = false;
if (min)
return base * 10;
bool inBG = bot->InBattleground() || bot->InArena();
if (sPlayerbotAIConfig->fastReactInBG && inBG)
return base;
bool inCombat = bot->IsInCombat();
if (!inCombat)
return base * 10.0f;
else if (inCombat)
return base * 2.5f;
return base;
}
float multiplier = 1.0f;
// Dynamic react delay calculation:
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;
}
bool inBg = bot->InBattleground() || bot->InArena();
if (inBg)
else
{
multiplier = sPlayerbotAIConfig->fastReactInBG ? 1.0f : 10.0f;
return base * multiplier;
}
}
// When in combat, return 5 times the base
if (bot->IsInCombat() || currentState == BOT_STATE_COMBAT)
{
multiplier = 5.0f;
return base * multiplier;
}
bool isResting = bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
if (!isResting)
// When not resting, return 10-30 times the base
if (!bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING))
{
multiplier = urand(10, 30);
return base * multiplier;
}
// In other cases, return 20-200 times the base
multiplier = urand(20, 200);
return base * multiplier;
}

View File

@@ -579,7 +579,8 @@ 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();
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);
bool _isBotInitializing = false;