Compare commits

..

6 Commits

26 changed files with 147 additions and 381 deletions

View File

@@ -502,8 +502,8 @@ AiPlayerbot.AutoGearScoreLimit = 0
# "power" (bots have infinite energy, rage, and runic power)
# "taxi" (bots may use all flight paths, though they will not actually learn them)
# "raid" (bots use cheats implemented into raid strategies)
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,raid,taxi")
# Default: taxi and raid are enabled
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,taxi")
# Default: taxi is enabled
AiPlayerbot.BotCheats = "taxi,raid"
#
@@ -1107,7 +1107,7 @@ AiPlayerbot.RandomBotArenaTeamMinRating = 1000
AiPlayerbot.DeleteRandomBotArenaTeams = 0
# PvP Restricted Zones (bots don't pvp)
AiPlayerbot.PvpProhibitedZoneIds = "2255,656,2361,2362,2363,976,35,2268,3425,392,541,1446,3828,3712,3738,3565,3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395,3703,4298,3951"
AiPlayerbot.PvpProhibitedZoneIds = "2255,656,2361,2362,2363,976,35,2268,3425,392,541,1446,3828,3712,3738,3565,3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395,3703,4298,139,3951"
# PvP Restricted Areas (bots don't pvp)
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080"

View File

@@ -3970,37 +3970,15 @@ bool IsAlliance(uint8 race)
bool PlayerbotAI::HasRealPlayerMaster()
{
// if (master)
// {
// PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master);
// return !masterBotAI || masterBotAI->IsRealPlayer();
// }
//
// return false;
// Removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
/* 1) The "master" pointer can be null if the bot was created
without a master player or if the master was just removed. */
if (!master)
return false;
/* 2) Is the master player still present in the world?
If FindPlayer fails, we invalidate "master" and stop here. */
if (!ObjectAccessor::FindPlayer(master->GetGUID()))
if (master)
{
master = nullptr; // avoids repeating the check on the next tick
return false;
PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master);
return !masterBotAI || masterBotAI->IsRealPlayer();
}
/* 3) If the master is a bot, we check that it is itself controlled
by a real player. Otherwise, it's already a real player → true. */
if (PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master))
return masterBotAI->IsRealPlayer(); // bot controlled by a player?
return true; // master = real player
return false;
}
bool PlayerbotAI::HasActivePlayerMaster() { return master && !GET_PLAYERBOT_AI(master); }
bool PlayerbotAI::IsAlt() { return HasRealPlayerMaster() && !sRandomPlayerbotMgr->IsRandomBot(bot); }
@@ -4214,6 +4192,19 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
}
}
// only keep updating till initializing time has completed,
// which prevents unneeded expensive GameTime calls.
if (_isBotInitializing)
{
_isBotInitializing = GameTime::GetUptime().count() < sPlayerbotAIConfig->maxRandomBots * 0.11;
// no activity allowed during bot initialization
if (_isBotInitializing)
{
return false;
}
}
// General exceptions
if (activityType == PACKET_ACTIVITY)
{

View File

@@ -611,6 +611,7 @@ private:
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;
protected:
Player* bot;

View File

@@ -148,7 +148,7 @@ bool PlayerbotAIConfig::Initialize()
LoadList<std::vector<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.PvpProhibitedZoneIds",
"2255,656,2361,2362,2363,976,35,2268,3425,392,541,1446,3828,3712,3738,3565,"
"3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395,3703,4298,3951"),
"3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395,3703,4298,139,3951"),
pvpProhibitedZoneIds);
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.PvpProhibitedAreaIds", "976,35"),
pvpProhibitedAreaIds);

View File

@@ -38,8 +38,6 @@
#include "WorldSessionMgr.h"
#include "DatabaseEnv.h" // Added for gender choice
#include <algorithm> // Added for gender choice
#include "Log.h" // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
#include <shared_mutex> // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
class BotInitGuard
{
@@ -529,9 +527,6 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
{
botAI->ResetStrategies(!sRandomPlayerbotMgr->IsRandomBot(bot));
}
botAI->Reset(true); // Reset transient states (incl. LFG "proposal") to avoid the "one or more players are not eligible" error after reconnect.
sPlayerbotDbStore->Load(botAI);
if (master && !master->HasUnitState(UNIT_STATE_IN_FLIGHT))
@@ -551,21 +546,16 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
if (master && master->GetGroup() && !group)
{
Group* mgroup = master->GetGroup();
// if (mgroup->GetMembersCount() >= 5)
if (mgroup->GetMembersCount() + 1 > 5) // only convert in raid if the add of THIS bot make group > 5
if (mgroup->GetMembersCount() >= 5)
{
if (!mgroup->isRaidGroup() && !mgroup->isLFGGroup() && !mgroup->isBGGroup() && !mgroup->isBFGroup())
{
mgroup->ConvertToRaid();
}
//if (mgroup->isRaidGroup())
//{
//mgroup->AddMember(bot);
//}
mgroup->AddMember(bot);
LOG_DEBUG("playerbots", "[GROUP] after add: members={}, isRaid={}, isLFG={}",
(int)mgroup->GetMembersCount(), mgroup->isRaidGroup() ? 1 : 0, mgroup->isLFGGroup() ? 1 : 0);
if (mgroup->isRaidGroup())
{
mgroup->AddMember(bot);
}
}
else
{
@@ -1736,70 +1726,21 @@ void PlayerbotsMgr::RemovePlayerBotData(ObjectGuid const& guid, bool is_AI)
PlayerbotAI* PlayerbotsMgr::GetPlayerbotAI(Player* player)
{
// if (!(sPlayerbotAIConfig->enabled) || !player)
// {
if (!(sPlayerbotAIConfig->enabled) || !player)
{
return nullptr;
}
// if (player->GetSession()->isLogingOut() || player->IsDuringRemoveFromWorld()) {
// return nullptr;
// }
// // if (player->GetSession()->isLogingOut() || player->IsDuringRemoveFromWorld()) {
// // return nullptr;
// // }
// auto itr = _playerbotsAIMap.find(player->GetGUID());
// if (itr != _playerbotsAIMap.end())
// {
// if (itr->second->IsBotAI())
// return reinterpret_cast<PlayerbotAI*>(itr->second);
// }
//
// return nullptr;
// removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
if (!player || !sPlayerbotAIConfig->enabled)
return nullptr;
// First read the GUID into a local variable, but ONLY after the check!
ObjectGuid guid = player->GetGUID(); // <-- OK here, we know that player != nullptr
{
std::shared_lock rlock(_aiMutex);
auto it = _playerbotsAIMap.find(guid);
if (it != _playerbotsAIMap.end() && it->second->IsBotAI())
return static_cast<PlayerbotAI*>(it->second);
}
// Transient state: NEVER break the master ⇄ bots relationship here.
if (!ObjectAccessor::FindPlayer(guid))
auto itr = _playerbotsAIMap.find(player->GetGUID());
if (itr != _playerbotsAIMap.end())
{
RemovePlayerbotAI(guid, /*removeMgrEntry=*/false);
}
return nullptr;
}
// removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
PlayerbotAI* PlayerbotsMgr::GetPlayerbotAIByGuid(ObjectGuid guid)
{
if (!sPlayerbotAIConfig->enabled)
return nullptr;
std::shared_lock rlock(_aiMutex);
auto it = _playerbotsAIMap.find(guid);
if (it != _playerbotsAIMap.end() && it->second->IsBotAI())
return static_cast<PlayerbotAI*>(it->second);
return nullptr;
}
void PlayerbotsMgr::RemovePlayerbotAI(ObjectGuid const& guid, bool removeMgrEntry /*= true*/)
{
std::unique_lock wlock(_aiMutex);
if (auto it = _playerbotsAIMap.find(guid); it != _playerbotsAIMap.end())
{
delete it->second;
_playerbotsAIMap.erase(it);
LOG_DEBUG("playerbots", "Removed stale AI for GUID {}",
static_cast<uint64>(guid.GetRawValue()));
if (itr->second->IsBotAI())
return reinterpret_cast<PlayerbotAI*>(itr->second);
}
if (removeMgrEntry)
_playerbotsMgrMap.erase(guid); // we NO longer touch the relation in a "soft" purge
return nullptr;
}
PlayerbotMgr* PlayerbotsMgr::GetPlayerbotMgr(Player* player)

View File

@@ -12,7 +12,6 @@
#include "PlayerbotAIBase.h"
#include "QueryHolder.h"
#include "QueryResult.h"
#include <shared_mutex> // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
class ChatHandler;
class PlayerbotAI;
@@ -115,38 +114,13 @@ public:
void RemovePlayerBotData(ObjectGuid const& guid, bool is_AI);
PlayerbotAI* GetPlayerbotAI(Player* player);
PlayerbotAI* GetPlayerbotAIByGuid(ObjectGuid guid); // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
// void RemovePlayerbotAI(ObjectGuid const& guid); // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
// removeMgrEntry = true => "hard" purge (AI + manager relation), for real logouts
// removeMgrEntry = false => "soft" purge (AI only), for detected "stale" cases
void RemovePlayerbotAI(ObjectGuid const& guid, bool removeMgrEntry = true);
PlayerbotMgr* GetPlayerbotMgr(Player* player);
private:
std::unordered_map<ObjectGuid, PlayerbotAIBase*> _playerbotsAIMap;
std::unordered_map<ObjectGuid, PlayerbotAIBase*> _playerbotsMgrMap;
mutable std::shared_mutex _aiMutex; // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
};
#define sPlayerbotsMgr PlayerbotsMgr::instance()
// Temporary addition If it keeps crashing, we will use them.
// Like
// BEFORE : PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
// AFTER (safe) : PlayerbotAI* botAI = GET_PLAYERBOT_AI_SAFE(bot);
// BEFORE : if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player)) { ... }
// AFTER (safe) : if (PlayerbotAI* botAI = GET_PLAYERBOT_AI_SAFE(player)) { ... }
// --- SAFE helpers (append to PlayerbotMgr.h) ---
inline PlayerbotAI* GET_PLAYERBOT_AI_SAFE(Player* p)
{
// Avoid any dereference during transient states (nullptr, teleport, flight, etc.)
return p ? sPlayerbotsMgr->GetPlayerbotAI(p) : nullptr;
}
inline PlayerbotMgr* GET_PLAYERBOT_MGR_SAFE(Player* p)
{
return p ? sPlayerbotsMgr->GetPlayerbotMgr(p) : nullptr;
}
// --- end SAFE helpers ---
#endif

View File

@@ -30,7 +30,6 @@
#include "cs_playerbots.h"
#include "cmath"
#include "BattleGroundTactics.h"
#include "ObjectAccessor.h"
class PlayerbotsDatabaseScript : public DatabaseScript
{
@@ -109,7 +108,7 @@ public:
"|cffcccccchttps://github.com/liyunfan1223/mod-playerbots|r");
}
/*if (sPlayerbotAIConfig->enabled || sPlayerbotAIConfig->randomBotAutologin)
if (sPlayerbotAIConfig->enabled || sPlayerbotAIConfig->randomBotAutologin)
{
std::string roundedTime =
std::to_string(std::ceil((sPlayerbotAIConfig->maxRandomBots * 0.11 / 60) * 10) / 10.0);
@@ -118,7 +117,7 @@ public:
ChatHandler(player->GetSession()).SendSysMessage(
"|cff00ff00Playerbots:|r bot initialization at server startup takes about '"
+ roundedTime + "' minutes.");
}*/
}
}
}
@@ -312,7 +311,7 @@ class PlayerbotsScript : public PlayerbotScript
public:
PlayerbotsScript() : PlayerbotScript("PlayerbotsScript") {}
/*bool OnPlayerbotCheckLFGQueue(lfg::Lfg5Guids const& guidsList) override
bool OnPlayerbotCheckLFGQueue(lfg::Lfg5Guids const& guidsList) override
{
bool nonBotFound = false;
for (ObjectGuid const& guid : guidsList.guids)
@@ -326,137 +325,7 @@ public:
}
return nonBotFound;
}*/
// New LFG Function
bool OnPlayerbotCheckLFGQueue(lfg::Lfg5Guids const& guidsList)
{
const size_t totalSlots = guidsList.guids.size();
size_t ignoredEmpty = 0, ignoredNonPlayer = 0;
size_t offlinePlayers = 0, botPlayers = 0, realPlayers = 0;
bool groupGuidSeen = false;
LOG_DEBUG("playerbots", "[LFG] check start: slots={}", totalSlots);
for (size_t i = 0; i < totalSlots; ++i)
{
ObjectGuid const& guid = guidsList.guids[i];
// 1) Placeholders to ignore
if (guid.IsEmpty())
{
++ignoredEmpty;
LOG_DEBUG("playerbots", "[LFG] slot {}: <empty> -> ignored", i);
continue;
}
// Group GUID: in the original implementation this counted as "non-bot found"
if (guid.IsGroup())
{
groupGuidSeen = true;
LOG_DEBUG("playerbots", "[LFG] slot {}: <GROUP GUID> -> counts as having a real player (compat)", i);
continue;
}
// Other non-Player GUIDs: various placeholders, ignore them
if (!guid.IsPlayer())
{
++ignoredNonPlayer;
LOG_DEBUG("playerbots", "[LFG] slot {}: guid={} (non-player/high={}) -> ignored", i,
static_cast<uint64>(guid.GetRawValue()), (unsigned)guid.GetHigh());
continue;
}
// 2) Player present?
Player* player = ObjectAccessor::FindPlayer(guid);
if (!player)
{
++offlinePlayers;
LOG_DEBUG("playerbots", "[LFG] slot {}: player guid={} is offline/not in world", i,
static_cast<uint64>(guid.GetRawValue()));
continue;
}
// 3) Bot or real player?
if (GET_PLAYERBOT_AI(player) != nullptr)
{
++botPlayers;
LOG_DEBUG("playerbots", "[LFG] slot {}: BOT {} (lvl {}, class {})", i, player->GetName().c_str(),
player->GetLevel(), player->getClass());
}
else
{
++realPlayers;
LOG_DEBUG("playerbots", "[LFG] slot {}: REAL {} (lvl {}, class {})", i, player->GetName().c_str(),
player->GetLevel(), player->getClass());
}
}
// "Ultra-early phase" detection: only placeholders => DO NOT VETO
const bool onlyPlaceholders = (realPlayers + botPlayers + (groupGuidSeen ? 1 : 0)) == 0 &&
(ignoredEmpty + ignoredNonPlayer) == totalSlots;
// "Soft" LFG preflight if we actually see players AND at least one offline
if (!onlyPlaceholders && offlinePlayers > 0)
{
// Find a plausible leader: prefer a real online player, otherwise any online player
Player* leader = nullptr;
for (ObjectGuid const& guid : guidsList.guids)
if (guid.IsPlayer())
if (Player* p = ObjectAccessor::FindPlayer(guid))
if (GET_PLAYERBOT_AI(p) == nullptr)
{
leader = p;
break;
}
if (!leader)
for (ObjectGuid const& guid : guidsList.guids)
if (guid.IsPlayer())
if (Player* p = ObjectAccessor::FindPlayer(guid))
{
leader = p;
break;
}
if (leader)
{
Group* g = leader->GetGroup();
if (g)
{
LOG_DEBUG("playerbots", "[LFG-RESET] group members={}, isRaid={}, isLFGGroup={}",
(int)g->GetMembersCount(), g->isRaidGroup() ? 1 : 0, g->isLFGGroup() ? 1 : 0);
// "Soft" reset of LFG states on the bots' AI side (proposal/role-check, etc.)
for (GroupReference* ref = g->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member)
continue;
if (PlayerbotAI* ai = GET_PLAYERBOT_AI(member))
ai->Reset(true);
}
}
}
LOG_DEBUG("playerbots", "[LFG] preflight soft-reset triggered (offline detected) -> allowQueue=no (retry)");
return false; // ask the client to retry right after the reset
}
// "Hybrid" policy: permissive if only placeholders; otherwise original logic
bool allowQueue = onlyPlaceholders ? true : ((offlinePlayers == 0) && (realPlayers >= 1 || groupGuidSeen));
LOG_DEBUG("playerbots",
"[LFG] summary: slots={}, real={}, bots={}, offline={}, ignored(empty+nonPlayer)={}, "
"groupGuidSeen={} -> allowQueue={}",
totalSlots, realPlayers, botPlayers, offlinePlayers, (ignoredEmpty + ignoredNonPlayer),
(groupGuidSeen ? "yes" : "no"), (allowQueue ? "yes" : "no"));
return allowQueue;
}
// End LFG
void OnPlayerbotCheckKillTask(Player* player, Unit* victim) override
{
@@ -508,10 +377,6 @@ public:
void OnPlayerbotLogout(Player* player) override
{
// immediate purge of the bot's AI upon disconnection
if (player && player->GetSession()->IsBot())
sPlayerbotsMgr->RemovePlayerbotAI(player->GetGUID()); // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);

View File

@@ -2180,7 +2180,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot)
float range = sPlayerbotAIConfig->randomBotTeleportDistance;
Acore::AnyUnitInObjectRangeCheck u_check(bot, range);
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, range);
Cell::VisitAllObjects(bot, searcher, range);
if (!targets.empty())
{

View File

@@ -164,16 +164,15 @@ void PlayerbotFactory::Init()
{
continue;
}
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(gemId);
if (proto) {
if (proto->ItemLevel < 60)
continue;
if (proto->Flags & ITEM_FLAG_UNIQUE_EQUIPPABLE)
continue;
if (proto->ItemLevel < 60)
continue;
if (proto->Flags & ITEM_FLAG_UNIQUE_EQUIPPABLE)
{
continue;
}
if (sRandomItemMgr->IsTestItem(gemId))
continue;
@@ -181,11 +180,9 @@ void PlayerbotFactory::Init()
{
continue;
}
// LOG_INFO("playerbots", "Add {} to enchantment gems", gemId);
enchantGemIdCache.push_back(gemId);
}
LOG_INFO("playerbots", "Loading {} enchantment gems", enchantGemIdCache.size());
}
@@ -1020,16 +1017,14 @@ void PlayerbotFactory::ClearSkills()
}
bot->SetUInt32Value(PLAYER_SKILL_INDEX(0), 0);
bot->SetUInt32Value(PLAYER_SKILL_INDEX(1), 0);
// unlearn default race/class skills
if (PlayerInfo const* info = sObjectMgr->GetPlayerInfo(bot->getRace(), bot->getClass())) {
for (PlayerCreateInfoSkills::const_iterator itr = info->skills.begin(); itr != info->skills.end(); ++itr)
{
uint32 skillId = itr->SkillId;
if (!bot->HasSkill(skillId))
continue;
bot->SetSkill(skillId, 0, 0, 0);
}
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(bot->getRace(), bot->getClass());
for (PlayerCreateInfoSkills::const_iterator itr = info->skills.begin(); itr != info->skills.end(); ++itr)
{
uint32 skillId = itr->SkillId;
if (!bot->HasSkill(skillId))
continue;
bot->SetSkill(skillId, 0, 0, 0);
}
}

View File

@@ -59,7 +59,10 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
bool sameTarget = oldTarget == target && bot->GetVictim() == target;
bool inCombat = botAI->GetState() == BOT_STATE_COMBAT;
bool sameAttackMode = bot->HasUnitState(UNIT_STATE_MELEE_ATTACKING) == shouldMelee;
// there's no reason to do attack again
if (sameTarget && inCombat && sameAttackMode)
return false;
if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE ||
bot->HasUnitState(UNIT_STATE_IN_FLIGHT))
{
@@ -79,53 +82,52 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
if (!target->IsInWorld())
{
if (verbose)
botAI->TellError(std::string(target->GetName()) + " is no longer in the world.");
return false;
}
if ((sPlayerbotAIConfig->IsInPvpProhibitedZone(bot->GetZoneId()) ||
sPlayerbotAIConfig->IsInPvpProhibitedArea(bot->GetAreaId()))
&& (target->IsPlayer() || target->IsPet()))
{
if (verbose)
botAI->TellError("I cannot attack other players in PvP prohibited areas.");
return false;
}
if (bot->IsFriendlyTo(target))
{
if (verbose)
botAI->TellError(std::string(target->GetName()) + " is friendly to me.");
return false;
}
if (target->isDead())
{
if (verbose)
botAI->TellError(std::string(target->GetName()) + " is dead.");
return false;
}
if (!bot->IsWithinLOSInMap(target))
{
if (verbose)
botAI->TellError(std::string(target->GetName()) + " is not in my sight.");
return false;
}
if (sameTarget && inCombat && sameAttackMode)
{
if (verbose)
botAI->TellError("I am already attacking " + std::string(target->GetName()) + ".");
return false;
}
if (!bot->IsValidAttackTarget(target))
{
if (verbose)
botAI->TellError("I cannot attack an invalid target.");
botAI->TellError("I cannot attack an invalid target");
return false;
}
std::ostringstream msg;
msg << target->GetName();
if (bot->IsFriendlyTo(target))
{
msg << " is friendly to me";
if (verbose)
botAI->TellError(msg.str());
return false;
}
if (!bot->IsWithinLOSInMap(target))
{
msg << " is not in my sight";
if (verbose)
botAI->TellError(msg.str());
return false;
}
if (target->isDead())
{
msg << " is dead";
if (verbose)
botAI->TellError(msg.str());
return false;
}
if (sPlayerbotAIConfig->IsInPvpProhibitedZone(bot->GetZoneId())
&& (target->IsPlayer() || target->IsPet()))
{
if (verbose)
botAI->TellError("I cannot attack others in PvP prohibited zones");
return false;
}
@@ -139,7 +141,9 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
ObjectGuid guid = target->GetGUID();
bot->SetSelection(target->GetGUID());
context->GetValue<Unit*>("old target")->Set(oldTarget);
context->GetValue<Unit*>("old target")->Set(oldTarget);
context->GetValue<Unit*>("current target")->Set(target);
context->GetValue<LootObjectStack*>("available loot")->Get()->Add(guid);
@@ -153,6 +157,7 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
bot->StopMoving();
}
if (IsMovingAllowed() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target))
{
sServerFacade->SetFacingTo(bot, target);

View File

@@ -18,7 +18,7 @@ bool MoveToTravelTargetAction::Execute(Event event)
WorldLocation location = *target->getPosition();
Group* group = bot->GetGroup();
if (group && !urand(0, 1) && bot == botAI->GetGroupMaster() && !bot->IsInCombat())
if (group && !urand(0, 1) && bot == botAI->GetGroupMaster())
{
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{

View File

@@ -39,7 +39,7 @@ bool RevealGatheringItemAction::Execute(Event event)
std::list<GameObject*> targets;
AnyGameObjectInObjectRangeCheck u_check(bot, sPlayerbotAIConfig->grindDistance);
Acore::GameObjectListSearcher<AnyGameObjectInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, sPlayerbotAIConfig->reactDistance);
Cell::VisitAllObjects(bot, searcher, sPlayerbotAIConfig->reactDistance);
std::vector<GameObject*> result;
for (GameObject* go : targets)

View File

@@ -23,7 +23,7 @@ bool TravelAction::Execute(Event event)
std::list<Unit*> targets;
Acore::AnyUnitInObjectRangeCheck u_check(bot, sPlayerbotAIConfig->sightDistance * 2);
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, sPlayerbotAIConfig->sightDistance);
Cell::VisitAllObjects(bot, searcher, sPlayerbotAIConfig->sightDistance);
for (Unit* unit : targets)
{

View File

@@ -110,7 +110,7 @@ bool SummonAction::SummonUsingGos(Player* summoner, Player* player)
std::list<GameObject*> targets;
AnyGameObjectInObjectRangeCheck u_check(summoner, sPlayerbotAIConfig->sightDistance);
Acore::GameObjectListSearcher<AnyGameObjectInObjectRangeCheck> searcher(summoner, targets, u_check);
Cell::VisitObjects(summoner, searcher, sPlayerbotAIConfig->sightDistance);
Cell::VisitAllObjects(summoner, searcher, sPlayerbotAIConfig->sightDistance);
for (GameObject* go : targets)
{
@@ -130,7 +130,7 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player)
std::list<Unit*> targets;
Acore::AnyUnitInObjectRangeCheck u_check(summoner, sPlayerbotAIConfig->sightDistance);
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(summoner, targets, u_check);
Cell::VisitObjects(summoner, searcher, sPlayerbotAIConfig->sightDistance);
Cell::VisitAllObjects(summoner, searcher, sPlayerbotAIConfig->sightDistance);
for (Unit* unit : targets)
{

View File

@@ -6549,7 +6549,7 @@ bool IccSindragosaFrostBombAction::Execute(Event event)
float range = 200.0f;
Acore::AnyUnitInObjectRangeCheck u_check(bot, range);
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(bot, units, u_check);
Cell::VisitObjects(bot, searcher, range);
Cell::VisitAllObjects(bot, searcher, range);
for (Unit* unit : units)
{

View File

@@ -21,7 +21,7 @@ bool CollisionValue::Calculate()
float range = sPlayerbotAIConfig->contactDistance;
Acore::AnyUnitInObjectRangeCheck u_check(bot, range);
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, range);
Cell::VisitAllObjects(bot, searcher, range);
for (Unit* target : targets)
{

View File

@@ -26,7 +26,7 @@ void NearestCorpsesValue::FindUnits(std::list<Unit*>& targets)
{
AnyDeadUnitInObjectRangeCheck u_check(bot, range);
Acore::UnitListSearcher<AnyDeadUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, range);
Cell::VisitAllObjects(bot, searcher, range);
}
bool NearestCorpsesValue::AcceptUnit(Unit* unit) { return true; }

View File

@@ -14,7 +14,7 @@ void NearestFriendlyPlayersValue::FindUnits(std::list<Unit*>& targets)
{
Acore::AnyFriendlyUnitInObjectRangeCheck u_check(bot, bot, range);
Acore::UnitListSearcher<Acore::AnyFriendlyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, range);
Cell::VisitAllObjects(bot, searcher, range);
}
bool NearestFriendlyPlayersValue::AcceptUnit(Unit* unit)

View File

@@ -17,7 +17,7 @@ GuidVector NearestGameObjects::Calculate()
std::list<GameObject*> targets;
AnyGameObjectInObjectRangeCheck u_check(bot, range);
Acore::GameObjectListSearcher<AnyGameObjectInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, range);
Cell::VisitAllObjects(bot, searcher, range);
GuidVector result;
for (GameObject* go : targets)
@@ -34,7 +34,7 @@ GuidVector NearestTrapWithDamageValue::Calculate()
std::list<GameObject*> targets;
AnyGameObjectInObjectRangeCheck u_check(bot, range);
Acore::GameObjectListSearcher<AnyGameObjectInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, range);
Cell::VisitAllObjects(bot, searcher, range);
GuidVector result;
for (GameObject* go : targets)

View File

@@ -14,7 +14,7 @@ void NearestNonBotPlayersValue::FindUnits(std::list<Unit*>& targets)
{
Acore::AnyUnitInObjectRangeCheck u_check(bot, range);
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, range);
Cell::VisitAllObjects(bot, searcher, range);
}
bool NearestNonBotPlayersValue::AcceptUnit(Unit* unit)

View File

@@ -15,7 +15,7 @@ void NearestNpcsValue::FindUnits(std::list<Unit*>& targets)
{
Acore::AnyUnitInObjectRangeCheck u_check(bot, range);
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, range);
Cell::VisitAllObjects(bot, searcher, range);
}
bool NearestNpcsValue::AcceptUnit(Unit* unit) { return !unit->IsPlayer(); }
@@ -24,7 +24,7 @@ void NearestHostileNpcsValue::FindUnits(std::list<Unit*>& targets)
{
Acore::AnyUnitInObjectRangeCheck u_check(bot, range);
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, range);
Cell::VisitAllObjects(bot, searcher, range);
}
bool NearestHostileNpcsValue::AcceptUnit(Unit* unit) { return unit->IsHostileTo(bot) && !unit->IsPlayer(); }
@@ -33,7 +33,7 @@ void NearestVehiclesValue::FindUnits(std::list<Unit*>& targets)
{
Acore::AnyUnitInObjectRangeCheck u_check(bot, range);
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, range);
Cell::VisitAllObjects(bot, searcher, range);
}
bool NearestVehiclesValue::AcceptUnit(Unit* unit)
@@ -52,7 +52,7 @@ void NearestTriggersValue::FindUnits(std::list<Unit*>& targets)
{
Acore::AnyUnfriendlyUnitInObjectRangeCheck u_check(bot, bot, range);
Acore::UnitListSearcher<Acore::AnyUnfriendlyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, range);
Cell::VisitAllObjects(bot, searcher, range);
}
bool NearestTriggersValue::AcceptUnit(Unit* unit) { return !unit->IsPlayer(); }
@@ -61,7 +61,7 @@ void NearestTotemsValue::FindUnits(std::list<Unit*>& targets)
{
Acore::AnyUnitInObjectRangeCheck u_check(bot, range);
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, range);
Cell::VisitAllObjects(bot, searcher, range);
}
bool NearestTotemsValue::AcceptUnit(Unit* unit) { return unit->IsTotem(); }

View File

@@ -49,7 +49,7 @@ void PossibleRpgTargetsValue::FindUnits(std::list<Unit*>& targets)
{
Acore::AnyUnitInObjectRangeCheck u_check(bot, range);
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, range);
Cell::VisitAllObjects(bot, searcher, range);
}
bool PossibleRpgTargetsValue::AcceptUnit(Unit* unit)
@@ -141,7 +141,7 @@ void PossibleNewRpgTargetsValue::FindUnits(std::list<Unit*>& targets)
{
Acore::AnyUnitInObjectRangeCheck u_check(bot, range);
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, range);
Cell::VisitAllObjects(bot, searcher, range);
}
bool PossibleNewRpgTargetsValue::AcceptUnit(Unit* unit)
@@ -168,7 +168,7 @@ GuidVector PossibleNewRpgGameObjectsValue::Calculate()
std::list<GameObject*> targets;
AnyGameObjectInObjectRangeCheck u_check(bot, range);
Acore::GameObjectListSearcher<AnyGameObjectInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, range);
Cell::VisitAllObjects(bot, searcher, range);
std::vector<std::pair<ObjectGuid, float>> guidDistancePairs;

View File

@@ -21,7 +21,7 @@ void PossibleTargetsValue::FindUnits(std::list<Unit*>& targets)
{
Acore::AnyUnfriendlyUnitInObjectRangeCheck u_check(bot, bot, range);
Acore::UnitListSearcher<Acore::AnyUnfriendlyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, range);
Cell::VisitAllObjects(bot, searcher, range);
}
bool PossibleTargetsValue::AcceptUnit(Unit* unit) { return AttackersValue::IsPossibleTarget(unit, bot, range); }
@@ -30,7 +30,7 @@ void PossibleTriggersValue::FindUnits(std::list<Unit*>& targets)
{
Acore::AnyUnfriendlyUnitInObjectRangeCheck u_check(bot, bot, range);
Acore::UnitListSearcher<Acore::AnyUnfriendlyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, range);
Cell::VisitAllObjects(bot, searcher, range);
}
bool PossibleTriggersValue::AcceptUnit(Unit* unit)

View File

@@ -21,8 +21,8 @@
const int ITEM_SOUL_SHARD = 6265;
// Checks if the bot has less than 26 soul shards, and if so, allows casting Drain Soul
bool CastDrainSoulAction::isUseful() { return AI_VALUE2(uint32, "item count", "soul shard") < 26; }
// Checks if the bot has less than 20 soul shards, and if so, allows casting Drain Soul
bool CastDrainSoulAction::isUseful() { return AI_VALUE2(uint32, "item count", "soul shard") < 20; }
// Checks if the bot's health is above a certain threshold, and if so, allows casting Life Tap
bool CastLifeTapAction::isUseful() { return AI_VALUE2(uint8, "health", "self target") > sPlayerbotAIConfig->lowHealth; }

View File

@@ -29,6 +29,8 @@ public:
creators["boost"] = &WarlockStrategyFactoryInternal::boost;
creators["cc"] = &WarlockStrategyFactoryInternal::cc;
creators["pet"] = &WarlockStrategyFactoryInternal::pet;
creators["spellstone"] = &WarlockStrategyFactoryInternal::spellstone;
creators["firestone"] = &WarlockStrategyFactoryInternal::firestone;
creators["meta melee"] = &WarlockStrategyFactoryInternal::meta_melee_aoe;
creators["tank"] = &WarlockStrategyFactoryInternal::tank;
creators["aoe"] = &WarlockStrategyFactoryInternal::aoe;
@@ -40,6 +42,8 @@ private:
static Strategy* boost(PlayerbotAI* botAI) { return new WarlockBoostStrategy(botAI); }
static Strategy* cc(PlayerbotAI* botAI) { return new WarlockCcStrategy(botAI); }
static Strategy* pet(PlayerbotAI* botAI) { return new WarlockPetStrategy(botAI); }
static Strategy* spellstone(PlayerbotAI* botAI) { return new UseSpellstoneStrategy(botAI); }
static Strategy* firestone(PlayerbotAI* botAI) { return new UseFirestoneStrategy(botAI); }
static Strategy* meta_melee_aoe(PlayerbotAI* botAI) { return new MetaMeleeAoeStrategy(botAI); }
static Strategy* tank(PlayerbotAI* botAI) { return new TankWarlockStrategy(botAI); }
static Strategy* aoe(PlayerbotAI* botAI) { return new AoEWarlockStrategy(botAI); }
@@ -121,20 +125,6 @@ private:
static Strategy* curse_of_weakness(PlayerbotAI* botAI) { return new WarlockCurseOfWeaknessStrategy(botAI); }
};
class WarlockWeaponStoneStrategyFactoryInternal : public NamedObjectContext<Strategy>
{
public:
WarlockWeaponStoneStrategyFactoryInternal() : NamedObjectContext<Strategy>(false, true)
{
creators["firestone"] = &WarlockWeaponStoneStrategyFactoryInternal::firestone;
creators["spellstone"] = &WarlockWeaponStoneStrategyFactoryInternal::spellstone;
}
private:
static Strategy* firestone(PlayerbotAI* ai) { return new UseFirestoneStrategy(ai); }
static Strategy* spellstone(PlayerbotAI* ai) { return new UseSpellstoneStrategy(ai); }
};
class WarlockTriggerFactoryInternal : public NamedObjectContext<Trigger>
{
public:
@@ -343,13 +333,19 @@ private:
static Action* devour_magic_purge(PlayerbotAI* botAI) { return new CastDevourMagicPurgeAction(botAI); }
static Action* devour_magic_cleanse(PlayerbotAI* botAI) { return new CastDevourMagicCleanseAction(botAI); }
static Action* seed_of_corruption(PlayerbotAI* botAI) { return new CastSeedOfCorruptionAction(botAI); }
static Action* seed_of_corruption_on_attacker(PlayerbotAI* botAI) { return new CastSeedOfCorruptionOnAttackerAction(botAI); }
static Action* seed_of_corruption_on_attacker(PlayerbotAI* botAI)
{
return new CastSeedOfCorruptionOnAttackerAction(botAI);
}
static Action* rain_of_fire(PlayerbotAI* botAI) { return new CastRainOfFireAction(botAI); }
static Action* hellfire(PlayerbotAI* botAI) { return new CastHellfireAction(botAI); }
static Action* shadowfury(PlayerbotAI* botAI) { return new CastShadowfuryAction(botAI); }
static Action* life_tap(PlayerbotAI* botAI) { return new CastLifeTapAction(botAI); }
static Action* unstable_affliction(PlayerbotAI* ai) { return new CastUnstableAfflictionAction(ai); }
static Action* unstable_affliction_on_attacker(PlayerbotAI* ai) { return new CastUnstableAfflictionOnAttackerAction(ai); }
static Action* unstable_affliction_on_attacker(PlayerbotAI* ai)
{
return new CastUnstableAfflictionOnAttackerAction(ai);
}
static Action* haunt(PlayerbotAI* ai) { return new CastHauntAction(ai); }
static Action* demonic_empowerment(PlayerbotAI* ai) { return new CastDemonicEmpowermentAction(ai); }
static Action* metamorphosis(PlayerbotAI* ai) { return new CastMetamorphosisAction(ai); }
@@ -364,7 +360,10 @@ private:
static Action* searing_pain(PlayerbotAI* botAI) { return new CastSearingPainAction(botAI); }
static Action* shadow_ward(PlayerbotAI* botAI) { return new CastShadowWardAction(botAI); }
static Action* curse_of_agony(PlayerbotAI* botAI) { return new CastCurseOfAgonyAction(botAI); }
static Action* curse_of_agony_on_attacker(PlayerbotAI* botAI) { return new CastCurseOfAgonyOnAttackerAction(botAI); }
static Action* curse_of_agony_on_attacker(PlayerbotAI* botAI)
{
return new CastCurseOfAgonyOnAttackerAction(botAI);
}
static Action* curse_of_the_elements(PlayerbotAI* ai) { return new CastCurseOfTheElementsAction(ai); }
static Action* curse_of_doom(PlayerbotAI* ai) { return new CastCurseOfDoomAction(ai); }
static Action* curse_of_exhaustion(PlayerbotAI* ai) { return new CastCurseOfExhaustionAction(ai); }
@@ -398,7 +397,6 @@ void WarlockAiObjectContext::BuildSharedStrategyContexts(SharedNamedObjectContex
strategyContexts.Add(new WarlockPetStrategyFactoryInternal());
strategyContexts.Add(new WarlockSoulstoneStrategyFactoryInternal());
strategyContexts.Add(new WarlockCurseStrategyFactoryInternal());
strategyContexts.Add(new WarlockWeaponStoneStrategyFactoryInternal());
}
void WarlockAiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts)

View File

@@ -46,7 +46,7 @@ bool WarlockConjuredItemTrigger::IsActive()
bool OutOfSoulShardsTrigger::IsActive() { return GetSoulShardCount(botAI->GetBot()) == 0; }
bool TooManySoulShardsTrigger::IsActive() { return GetSoulShardCount(botAI->GetBot()) >= 26; }
bool TooManySoulShardsTrigger::IsActive() { return GetSoulShardCount(botAI->GetBot()) >= 6; }
bool OutOfSoulstoneTrigger::IsActive() { return GetSoulstoneCount(botAI->GetBot()) == 0; }
@@ -207,11 +207,7 @@ bool WrongPetTrigger::IsActive()
if (enabledCount != 1)
return false;
// Step 3: If there is no pet, do not trigger.
if (!pet)
return false;
// Step 4: At this point, we know only one pet strategy is enabled.
// Step 3: At this point, we know only one pet strategy is enabled.
// We check if the currently summoned pet matches the enabled strategy.
bool correctPet = false;
if (pet)
@@ -222,16 +218,16 @@ bool WrongPetTrigger::IsActive()
correctPet = true;
}
// Step 5: If the correct pet is already summoned, the trigger should not activate.
// Step 4: If the correct pet is already summoned, the trigger should not activate.
if (correctPet)
return false;
// Step 6: Finally, check if the bot actually knows the spell to summon the desired pet.
// Step 5: Finally, check if the bot actually knows the spell to summon the desired pet.
// If so, the trigger is active (bot should summon the correct pet).
if (bot->HasSpell(enabledPet->spellId))
return true;
// Step 7: If we get here, the bot doesn't know the spell required to support the active pet strategy
// Step 6: If we get here, the bot doesn't know the spell required to support the active pet strategy
return false;
}