mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user