mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-02-20 18:10:02 +01:00
As requested revert for threadfixes last few days (#1552)
* Revert "[Large server fix] #1537 Serialize playerBots/botLoading with a mutex and use snapshot-based loops to fix concurrency crashes (#1540)" This reverts commit 3fff58df1a2058894e9b758be07869aec87c2c70. * Revert "[Fix] teleport to invalid map or invalid coordinates (x , y , z 200000, o ) given when teleporting player (g UI d full type player low , name , map , x , y , z , o ) (#1538)" This reverts commit ca2e2ef0dbd8dcfb16123db65ae638424550e50c. * Revert "Fix: prevent MoveSplineInitArgs::Validate velocity asserts (velocity > 0.01f) for bots, pets, and charmed units (#1534)" This reverts commit 4e3ac609bd23d991150d956d4e69ee6de2fcf2bf. * Revert "[Fix issue #1527] : startup crash in tank target selection — add TOCTOU & null-safety guards (#1532)" This reverts commit c6b0424c29b6a1bf5b3574135128d30d19838411. * Revert "[Fix issue #1528] Close small window where the “in a BG/arena” state can change between the check (InBattleground() / InArena()) and grabbing the pointer (GetBattleground()), which leads to a null dereference. (#1530)" This reverts commit 2e0a161623eaa97b7d9ceea076779ae0cabeb877. * Revert "Harden playerbot logout & packet dispatch; add null-safety in chat hooks and RPG checks (#1529)" This reverts commit e4ea8e2694b0f6d098a945c6f863526cd14f9b3f. * Revert "Dont wait to travel when in combat. (#1524)" This reverts commit ddfa919154529fee59e7ba30d2ebe29c0ae4abdf. * Revert "nullptr fix (#1523)" This reverts commit 380312ffd231fd5e663a8a17daa80dd39906e3f0. * Revert "Playerbots/LFG: fix false not eligible & dungeon 0/type 0, add clear diagnostics (#1521)" This reverts commit 872e4176137b66c83ebcb03932fa8ff1e39fd791. * Revert "nullptr exception (#1520)" This reverts commit 3d28a815089fd0a878a6a1d469db657c6030d4b2. * Revert "Removed bot freezing at startup and system message, not relevant anymore (#1519)" This reverts commit bcd6f5bc066d5e8a54f2d37b7dfc54e5db0dd2d1.
This commit is contained in:
parent
3fff58df1a
commit
8d51092d42
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
|
||||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "Unit.h"
|
|
||||||
#include "Player.h"
|
|
||||||
#include "MotionMaster.h"
|
|
||||||
|
|
||||||
inline bool CanStartMoveSpline(Player* bot) {
|
|
||||||
if (!bot) return false;
|
|
||||||
if (!bot->IsAlive()) return false;
|
|
||||||
if (bot->IsBeingTeleported() || bot->IsInFlight()) return false;
|
|
||||||
if (bot->HasUnitState(UNIT_STATE_LOST_CONTROL) || bot->HasRootAura() ||
|
|
||||||
bot->HasStunAura() || bot->IsCharmed() || bot->isFrozen() || bot->IsPolymorphed())
|
|
||||||
return false;
|
|
||||||
if (bot->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) != NULL_MOTION_TYPE)
|
|
||||||
return false;
|
|
||||||
if (bot->GetSpeed(MOVE_RUN) <= 0.01f) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool CanStartMoveSpline(Unit* u) {
|
|
||||||
if (!u) return false;
|
|
||||||
if (!u->IsAlive()) return false;
|
|
||||||
if (u->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_CONFUSED | UNIT_STATE_FLEEING))
|
|
||||||
return false;
|
|
||||||
if (u->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) != NULL_MOTION_TYPE)
|
|
||||||
return false;
|
|
||||||
if (u->GetSpeed(MOVE_RUN) <= 0.01f) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@ -947,21 +947,8 @@ bool BroadcastHelper::BroadcastSuggestThunderfury(PlayerbotAI* ai, Player* bot)
|
|||||||
{
|
{
|
||||||
std::map<std::string, std::string> placeholders;
|
std::map<std::string, std::string> placeholders;
|
||||||
ItemTemplate const* thunderfuryProto = sObjectMgr->GetItemTemplate(19019);
|
ItemTemplate const* thunderfuryProto = sObjectMgr->GetItemTemplate(19019);
|
||||||
// placeholders["%thunderfury_link"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatItem(thunderfuryProto); // Old code
|
placeholders["%thunderfury_link"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatItem(thunderfuryProto);
|
||||||
// [Crash fix] Protect from nil AI : a real player doesn't have PlayerbotAI.
|
|
||||||
// Before: direct deref GET_PLAYERBOT_AI(bot)->... could crash World/General.
|
|
||||||
if (auto* ai = GET_PLAYERBOT_AI(bot))
|
|
||||||
{
|
|
||||||
if (auto* chat = ai->GetChatHelper())
|
|
||||||
placeholders["%thunderfury_link"] = chat->FormatItem(thunderfuryProto);
|
|
||||||
else
|
|
||||||
placeholders["%thunderfury_link"] = ""; // fallback: no chat helper
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
placeholders["%thunderfury_link"] = ""; // fallback: no d'AI (real player)
|
|
||||||
}
|
|
||||||
// End crash fix
|
|
||||||
return BroadcastToChannelWithGlobalChance(
|
return BroadcastToChannelWithGlobalChance(
|
||||||
ai,
|
ai,
|
||||||
BOT_TEXT2("thunderfury_spam", placeholders),
|
BOT_TEXT2("thunderfury_spam", placeholders),
|
||||||
|
|||||||
@ -57,7 +57,6 @@
|
|||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
#include "UpdateTime.h"
|
#include "UpdateTime.h"
|
||||||
#include "Vehicle.h"
|
#include "Vehicle.h"
|
||||||
#include "BotMovementUtils.h"
|
|
||||||
|
|
||||||
const int SPELL_TITAN_GRIP = 49152;
|
const int SPELL_TITAN_GRIP = 49152;
|
||||||
|
|
||||||
@ -721,7 +720,6 @@ void PlayerbotAI::HandleTeleportAck()
|
|||||||
bot->GetSession()->HandleMoveWorldportAck();
|
bot->GetSession()->HandleMoveWorldportAck();
|
||||||
}
|
}
|
||||||
// SetNextCheckDelay(urand(2000, 5000));
|
// SetNextCheckDelay(urand(2000, 5000));
|
||||||
SetNextCheckDelay(urand(500, 1500)); // short delay to break bursts without hindering gameplay
|
|
||||||
if (sPlayerbotAIConfig->applyInstanceStrategies)
|
if (sPlayerbotAIConfig->applyInstanceStrategies)
|
||||||
ApplyInstanceStrategies(bot->GetMapId(), true);
|
ApplyInstanceStrategies(bot->GetMapId(), true);
|
||||||
EvaluateHealerDpsStrategy();
|
EvaluateHealerDpsStrategy();
|
||||||
@ -2375,7 +2373,7 @@ std::string PlayerbotAI::GetLocalizedGameObjectName(uint32 entry)
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*std::vector<Player*> PlayerbotAI::GetPlayersInGroup()
|
std::vector<Player*> PlayerbotAI::GetPlayersInGroup()
|
||||||
{
|
{
|
||||||
std::vector<Player*> members;
|
std::vector<Player*> members;
|
||||||
|
|
||||||
@ -2394,34 +2392,6 @@ std::string PlayerbotAI::GetLocalizedGameObjectName(uint32 entry)
|
|||||||
members.push_back(ref->GetSource());
|
members.push_back(ref->GetSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
return members;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
std::vector<Player*> PlayerbotAI::GetPlayersInGroup()
|
|
||||||
{
|
|
||||||
std::vector<Player*> members;
|
|
||||||
|
|
||||||
Group* group = bot->GetGroup();
|
|
||||||
if (!group)
|
|
||||||
return members;
|
|
||||||
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
|
||||||
{
|
|
||||||
Player* member = ref->GetSource();
|
|
||||||
if (!member)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Celaning, we don't call 2 times GET_PLAYERBOT_AI and never reference it if nil
|
|
||||||
if (auto* ai = GET_PLAYERBOT_AI(member))
|
|
||||||
{
|
|
||||||
// If it's a bot (not real player) => we ignor it
|
|
||||||
if (!ai->IsRealPlayer())
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
members.push_back(member);
|
|
||||||
}
|
|
||||||
|
|
||||||
return members;
|
return members;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4244,6 +4214,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
|
// General exceptions
|
||||||
if (activityType == PACKET_ACTIVITY)
|
if (activityType == PACKET_ACTIVITY)
|
||||||
{
|
{
|
||||||
@ -6327,27 +6310,11 @@ void PlayerbotAI::PetFollow()
|
|||||||
if (!pet)
|
if (!pet)
|
||||||
return;
|
return;
|
||||||
pet->AttackStop();
|
pet->AttackStop();
|
||||||
/* pet->InterruptNonMeleeSpells(false);
|
pet->InterruptNonMeleeSpells(false);
|
||||||
pet->ClearInPetCombat();
|
pet->ClearInPetCombat();
|
||||||
pet->GetMotionMaster()->MoveFollow(bot, PET_FOLLOW_DIST, pet->GetFollowAngle());
|
pet->GetMotionMaster()->MoveFollow(bot, PET_FOLLOW_DIST, pet->GetFollowAngle());
|
||||||
if (pet->ToPet())
|
|
||||||
pet->ToPet()->ClearCastWhenWillAvailable();*/
|
|
||||||
// [Fix: MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full:]
|
|
||||||
pet->InterruptNonMeleeSpells(false);
|
|
||||||
pet->ClearInPetCombat();
|
|
||||||
|
|
||||||
if (CanStartMoveSpline(pet))
|
|
||||||
{
|
|
||||||
pet->GetMotionMaster()->MoveFollow(bot, PET_FOLLOW_DIST, pet->GetFollowAngle());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pet->StopMovingOnCurrentPos(); // on n’envoie pas d’ordre invalide
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pet->ToPet())
|
if (pet->ToPet())
|
||||||
pet->ToPet()->ClearCastWhenWillAvailable();
|
pet->ToPet()->ClearCastWhenWillAvailable();
|
||||||
//End Fix
|
|
||||||
CharmInfo* charmInfo = pet->GetCharmInfo();
|
CharmInfo* charmInfo = pet->GetCharmInfo();
|
||||||
if (!charmInfo)
|
if (!charmInfo)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -611,6 +611,7 @@ private:
|
|||||||
Item* FindItemInInventory(std::function<bool(ItemTemplate const*)> checkItem) const;
|
Item* FindItemInInventory(std::function<bool(ItemTemplate const*)> checkItem) const;
|
||||||
void HandleCommands();
|
void HandleCommands();
|
||||||
void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL);
|
void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL);
|
||||||
|
bool _isBotInitializing = false;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Player* bot;
|
Player* bot;
|
||||||
|
|||||||
@ -36,32 +36,10 @@
|
|||||||
#include "BroadcastHelper.h"
|
#include "BroadcastHelper.h"
|
||||||
#include "PlayerbotDbStore.h"
|
#include "PlayerbotDbStore.h"
|
||||||
#include "WorldSessionMgr.h"
|
#include "WorldSessionMgr.h"
|
||||||
#include "DatabaseEnv.h"
|
#include "DatabaseEnv.h" // Added for gender choice
|
||||||
#include <algorithm>
|
#include <algorithm> // Added for gender choice
|
||||||
#include "Log.h"
|
#include "Log.h" // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
|
||||||
#include <shared_mutex> // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
|
#include <shared_mutex> // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
|
||||||
#include "TravelMgr.h"
|
|
||||||
#include <mutex>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
static std::mutex g_botMapsMx; // protect playerBots and botLoading
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
// [Crash fix] Centralize clearing of pointer values in the AI context
|
|
||||||
static void ClearAIContextPointerValues(PlayerbotAI* ai)
|
|
||||||
{
|
|
||||||
if (!ai) return;
|
|
||||||
if (AiObjectContext* ctx = ai->GetAiObjectContext())
|
|
||||||
{
|
|
||||||
// Known today
|
|
||||||
if (auto* tt = ctx->GetValue<TravelTarget*>("travel target"))
|
|
||||||
tt->Set(nullptr);
|
|
||||||
|
|
||||||
// TODO: add other pointer-type values here if you have any
|
|
||||||
// e.g.: ctx->GetValue<SomePtr>("some key")->Set(nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class BotInitGuard
|
class BotInitGuard
|
||||||
{
|
{
|
||||||
@ -109,16 +87,9 @@ public:
|
|||||||
|
|
||||||
void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId)
|
void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId)
|
||||||
{
|
{
|
||||||
/*// bot is loading
|
// bot is loading
|
||||||
if (botLoading.find(playerGuid) != botLoading.end())
|
if (botLoading.find(playerGuid) != botLoading.end())
|
||||||
return;*/
|
return;
|
||||||
|
|
||||||
// bot is loading (protégé)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx);
|
|
||||||
if (botLoading.find(playerGuid) != botLoading.end())
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// has bot already been added?
|
// has bot already been added?
|
||||||
Player* bot = ObjectAccessor::FindConnectedPlayer(playerGuid);
|
Player* bot = ObjectAccessor::FindConnectedPlayer(playerGuid);
|
||||||
@ -153,19 +124,10 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId
|
|||||||
PlayerbotMgr* mgr = GET_PLAYERBOT_MGR(masterPlayer);
|
PlayerbotMgr* mgr = GET_PLAYERBOT_MGR(masterPlayer);
|
||||||
if (!mgr)
|
if (!mgr)
|
||||||
{
|
{
|
||||||
LOG_DEBUG("mod-playerbots", "PlayerbotMgr not found for master player with GUID: {}", masterPlayer->GetGUID().GetRawValue());
|
LOG_DEBUG("playerbots", "PlayerbotMgr not found for master player with GUID: {}", masterPlayer->GetGUID().GetRawValue());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
uint32 count = mgr->GetPlayerbotsCount() + botLoading.size();
|
||||||
// read botLoading.size() locked
|
|
||||||
size_t loadingCount = 0;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx);
|
|
||||||
loadingCount = botLoading.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
// uint32 count = mgr->GetPlayerbotsCount() + botLoading.size();
|
|
||||||
uint32 count = mgr->GetPlayerbotsCount() + static_cast<uint32>(loadingCount);
|
|
||||||
if (count >= sPlayerbotAIConfig->maxAddedBots)
|
if (count >= sPlayerbotAIConfig->maxAddedBots)
|
||||||
{
|
{
|
||||||
allowed = false;
|
allowed = false;
|
||||||
@ -181,22 +143,14 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// std::shared_ptr<PlayerbotLoginQueryHolder> holder =
|
std::shared_ptr<PlayerbotLoginQueryHolder> holder =
|
||||||
// std::make_shared<PlayerbotLoginQueryHolder>(this, masterAccountId, accountId, playerGuid);
|
std::make_shared<PlayerbotLoginQueryHolder>(this, masterAccountId, accountId, playerGuid);
|
||||||
auto holder = std::make_shared<PlayerbotLoginQueryHolder>(this, masterAccountId, accountId, playerGuid);
|
|
||||||
if (!holder->Initialize())
|
if (!holder->Initialize())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// botLoading.insert(playerGuid);
|
botLoading.insert(playerGuid);
|
||||||
// Protected insert
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx);
|
|
||||||
if (botLoading.find(playerGuid) != botLoading.end())
|
|
||||||
return; // already loging
|
|
||||||
botLoading.insert(playerGuid); // we reserve the GUID
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always login in with world session to avoid race condition
|
// Always login in with world session to avoid race condition
|
||||||
sWorld->AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(holder))
|
sWorld->AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(holder))
|
||||||
@ -213,11 +167,7 @@ bool PlayerbotHolder::IsAccountLinked(uint32 accountId, uint32 linkedAccountId)
|
|||||||
|
|
||||||
void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder const& holder)
|
void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder const& holder)
|
||||||
{
|
{
|
||||||
// Copy immediatly holder value
|
|
||||||
const ObjectGuid guid = holder.GetGuid();
|
|
||||||
const uint32 masterAccountId = holder.GetMasterAccountId();
|
|
||||||
uint32 botAccountId = holder.GetAccountId();
|
uint32 botAccountId = holder.GetAccountId();
|
||||||
|
|
||||||
// At login DBC locale should be what the server is set to use by default (as spells etc are hardcoded to ENUS this
|
// At login DBC locale should be what the server is set to use by default (as spells etc are hardcoded to ENUS this
|
||||||
// allows channels to work as intended)
|
// allows channels to work as intended)
|
||||||
WorldSession* botSession = new WorldSession(botAccountId, "", 0x0, nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING,
|
WorldSession* botSession = new WorldSession(botAccountId, "", 0x0, nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING,
|
||||||
@ -232,16 +182,11 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
|
|||||||
LOG_DEBUG("mod-playerbots", "Bot player could not be loaded for account ID: {}", botAccountId);
|
LOG_DEBUG("mod-playerbots", "Bot player could not be loaded for account ID: {}", botAccountId);
|
||||||
botSession->LogoutPlayer(true);
|
botSession->LogoutPlayer(true);
|
||||||
delete botSession;
|
delete botSession;
|
||||||
// botLoading.erase(holder.GetGuid());
|
botLoading.erase(holder.GetGuid());
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx);
|
|
||||||
botLoading.erase(guid);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// uint32 masterAccount = holder.GetMasterAccountId();
|
uint32 masterAccount = holder.GetMasterAccountId();
|
||||||
uint32 masterAccount = masterAccountId; // Avoid read in 'holder' after login
|
|
||||||
WorldSession* masterSession = masterAccount ? sWorldSessionMgr->FindSession(masterAccount) : nullptr;
|
WorldSession* masterSession = masterAccount ? sWorldSessionMgr->FindSession(masterAccount) : nullptr;
|
||||||
|
|
||||||
// Check if masterSession->GetPlayer() is valid
|
// Check if masterSession->GetPlayer() is valid
|
||||||
@ -254,14 +199,10 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
|
|||||||
sRandomPlayerbotMgr->OnPlayerLogin(bot);
|
sRandomPlayerbotMgr->OnPlayerLogin(bot);
|
||||||
OnBotLogin(bot);
|
OnBotLogin(bot);
|
||||||
|
|
||||||
// botLoading.erase(holder.GetGuid());
|
botLoading.erase(holder.GetGuid());
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx);
|
|
||||||
botLoading.erase(guid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*void PlayerbotHolder::UpdateSessions()
|
void PlayerbotHolder::UpdateSessions()
|
||||||
{
|
{
|
||||||
for (PlayerBotMap::const_iterator itr = GetPlayerBotsBegin(); itr != GetPlayerBotsEnd(); ++itr)
|
for (PlayerBotMap::const_iterator itr = GetPlayerBotsBegin(); itr != GetPlayerBotsEnd(); ++itr)
|
||||||
{
|
{
|
||||||
@ -279,58 +220,15 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
|
|||||||
HandleBotPackets(bot->GetSession());
|
HandleBotPackets(bot->GetSession());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}*/
|
|
||||||
|
|
||||||
void PlayerbotHolder::UpdateSessions()
|
|
||||||
{
|
|
||||||
PlayerBotMap botsCopy;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx);
|
|
||||||
botsCopy = playerBots;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& kv : botsCopy)
|
|
||||||
{
|
|
||||||
Player* const bot = kv.second;
|
|
||||||
if (bot->IsBeingTeleported())
|
|
||||||
{
|
|
||||||
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot))
|
|
||||||
botAI->HandleTeleportAck();
|
|
||||||
}
|
|
||||||
else if (bot->IsInWorld())
|
|
||||||
{
|
|
||||||
HandleBotPackets(bot->GetSession());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*void PlayerbotHolder::HandleBotPackets(WorldSession* session)
|
void PlayerbotHolder::HandleBotPackets(WorldSession* session)
|
||||||
{
|
{
|
||||||
WorldPacket* packet;
|
WorldPacket* packet;
|
||||||
while (session->GetPacketQueue().next(packet))
|
while (session->GetPacketQueue().next(packet))
|
||||||
{
|
{
|
||||||
OpcodeClient opcode = static_cast<OpcodeClient>(packet->GetOpcode());
|
OpcodeClient opcode = static_cast<OpcodeClient>(packet->GetOpcode());
|
||||||
ClientOpcodeHandler const* opHandle = opcodeTable[opcode];
|
ClientOpcodeHandler const* opHandle = opcodeTable[opcode];
|
||||||
opHandle->Call(session, *packet);
|
|
||||||
delete packet;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
void PlayerbotHolder::HandleBotPackets(WorldSession* session) // [Crash Fix] Secure packet dispatch (avoid calling on a null handler)
|
|
||||||
{
|
|
||||||
WorldPacket* packet;
|
|
||||||
while (session->GetPacketQueue().next(packet))
|
|
||||||
{
|
|
||||||
const OpcodeClient opcode = static_cast<OpcodeClient>(packet->GetOpcode());
|
|
||||||
const ClientOpcodeHandler* opHandle = opcodeTable[opcode];
|
|
||||||
|
|
||||||
if (!opHandle)
|
|
||||||
{
|
|
||||||
// Unknown handler: drop cleanly
|
|
||||||
delete packet;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
opHandle->Call(session, *packet);
|
opHandle->Call(session, *packet);
|
||||||
delete packet;
|
delete packet;
|
||||||
}
|
}
|
||||||
@ -351,27 +249,8 @@ void PlayerbotHolder::LogoutAllBots()
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*PlayerBotMap bots = playerBots;
|
PlayerBotMap bots = playerBots;
|
||||||
for (auto& itr : bots)
|
for (auto& itr : bots)
|
||||||
{
|
|
||||||
Player* bot = itr.second;
|
|
||||||
if (!bot)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
|
||||||
if (!botAI || botAI->IsRealPlayer())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
LogoutPlayerBot(bot->GetGUID());
|
|
||||||
}*/
|
|
||||||
// Snapshot under lock for safe iteration
|
|
||||||
PlayerBotMap botsCopy;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx);
|
|
||||||
botsCopy = playerBots;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& itr : botsCopy)
|
|
||||||
{
|
{
|
||||||
Player* bot = itr.second;
|
Player* bot = itr.second;
|
||||||
if (!bot)
|
if (!bot)
|
||||||
@ -385,7 +264,7 @@ void PlayerbotHolder::LogoutAllBots()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*void PlayerbotMgr::CancelLogout()
|
void PlayerbotMgr::CancelLogout()
|
||||||
{
|
{
|
||||||
Player* master = GetMaster();
|
Player* master = GetMaster();
|
||||||
if (!master)
|
if (!master)
|
||||||
@ -406,53 +285,6 @@ void PlayerbotHolder::LogoutAllBots()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin();
|
|
||||||
it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it)
|
|
||||||
{
|
|
||||||
Player* const bot = it->second;
|
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
|
||||||
if (!botAI || botAI->IsRealPlayer())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (botAI->GetMaster() != master)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (bot->GetSession()->isLogingOut())
|
|
||||||
{
|
|
||||||
WorldPackets::Character::LogoutCancel data = WorldPacket(CMSG_LOGOUT_CANCEL);
|
|
||||||
bot->GetSession()->HandleLogoutCancelOpcode(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
void PlayerbotMgr::CancelLogout()
|
|
||||||
{
|
|
||||||
Player* master = GetMaster();
|
|
||||||
if (!master)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Snapshot of "master" bots under lock
|
|
||||||
std::vector<Player*> botsCopy;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx);
|
|
||||||
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
|
||||||
botsCopy.push_back(it->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Player* const bot : botsCopy)
|
|
||||||
{
|
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
|
||||||
if (!botAI || botAI->IsRealPlayer())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (bot->GetSession()->isLogingOut())
|
|
||||||
{
|
|
||||||
WorldPackets::Character::LogoutCancel data = WorldPacket(CMSG_LOGOUT_CANCEL);
|
|
||||||
bot->GetSession()->HandleLogoutCancelOpcode(data);
|
|
||||||
botAI->TellMaster("Logout cancelled!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin();
|
for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin();
|
||||||
it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it)
|
it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it)
|
||||||
{
|
{
|
||||||
@ -486,13 +318,10 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid)
|
|||||||
sPlayerbotDbStore->Save(botAI);
|
sPlayerbotDbStore->Save(botAI);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DEBUG("mod-playerbots", "Bot {} logging out", bot->GetName().c_str());
|
LOG_DEBUG("playerbots", "Bot {} logging out", bot->GetName().c_str());
|
||||||
bot->SaveToDB(false, false);
|
bot->SaveToDB(false, false);
|
||||||
|
|
||||||
// WorldSession* botWorldSessionPtr = bot->GetSession();
|
WorldSession* botWorldSessionPtr = bot->GetSession();
|
||||||
WorldSession* botWorldSessionPtr = bot->GetSession(); // Small safeguard on the session (as a precaution)
|
|
||||||
if (!botWorldSessionPtr)
|
|
||||||
return;
|
|
||||||
WorldSession* masterWorldSessionPtr = nullptr;
|
WorldSession* masterWorldSessionPtr = nullptr;
|
||||||
|
|
||||||
if (botWorldSessionPtr->isLogingOut())
|
if (botWorldSessionPtr->isLogingOut())
|
||||||
@ -525,13 +354,11 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid)
|
|||||||
logout = true;
|
logout = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*TravelTarget* target = nullptr;
|
TravelTarget* target = nullptr;
|
||||||
if (botAI->GetAiObjectContext()) // Maybe some day re-write to delate all pointer values.
|
if (botAI->GetAiObjectContext()) // Maybe some day re-write to delate all pointer values.
|
||||||
{
|
{
|
||||||
target = botAI->GetAiObjectContext()->GetValue<TravelTarget*>("travel target")->Get();
|
target = botAI->GetAiObjectContext()->GetValue<TravelTarget*>("travel target")->Get();
|
||||||
}*/
|
}
|
||||||
// [Crash fix] Centralized cleanup of pointer values in the context
|
|
||||||
ClearAIContextPointerValues(botAI);
|
|
||||||
|
|
||||||
// Peiru: Allow bots to always instant logout to see if this resolves logout crashes
|
// Peiru: Allow bots to always instant logout to see if this resolves logout crashes
|
||||||
logout = true;
|
logout = true;
|
||||||
@ -548,25 +375,19 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid)
|
|||||||
botWorldSessionPtr->HandleLogoutRequestOpcode(data);
|
botWorldSessionPtr->HandleLogoutRequestOpcode(data);
|
||||||
if (!bot)
|
if (!bot)
|
||||||
{
|
{
|
||||||
/*RemoveFromPlayerbotsMap(guid);
|
|
||||||
delete botWorldSessionPtr;
|
|
||||||
if (target)
|
|
||||||
delete target;*/
|
|
||||||
// [Crash fix] bot can be destroyed by the logout request: clean up without touching old pointers
|
|
||||||
RemoveFromPlayerbotsMap(guid);
|
RemoveFromPlayerbotsMap(guid);
|
||||||
delete botWorldSessionPtr;
|
delete botWorldSessionPtr;
|
||||||
|
if (target)
|
||||||
|
delete target;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/*RemoveFromPlayerbotsMap(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap
|
RemoveFromPlayerbotsMap(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap
|
||||||
delete botWorldSessionPtr; // finally delete the bot's WorldSession
|
delete botWorldSessionPtr; // finally delete the bot's WorldSession
|
||||||
if (target)
|
if (target)
|
||||||
delete target;*/
|
delete target;
|
||||||
// [Crash fix] no more deleting 'target' here: ownership handled by the AI/Context
|
|
||||||
RemoveFromPlayerbotsMap(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap
|
|
||||||
delete botWorldSessionPtr; // finally delete the bot's WorldSession
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} // if instant logout possible, do it
|
} // if instant logout possible, do it
|
||||||
@ -599,11 +420,11 @@ void PlayerbotHolder::DisablePlayerBot(ObjectGuid guid)
|
|||||||
sPlayerbotDbStore->Save(botAI);
|
sPlayerbotDbStore->Save(botAI);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DEBUG("mod-playerbots", "Bot {} logged out", bot->GetName().c_str());
|
LOG_DEBUG("playerbots", "Bot {} logged out", bot->GetName().c_str());
|
||||||
|
|
||||||
bot->SaveToDB(false, false);
|
bot->SaveToDB(false, false);
|
||||||
|
|
||||||
/*if (botAI->GetAiObjectContext()) // Maybe some day re-write to delate all pointer values.
|
if (botAI->GetAiObjectContext()) // Maybe some day re-write to delate all pointer values.
|
||||||
{
|
{
|
||||||
TravelTarget* target = botAI->GetAiObjectContext()->GetValue<TravelTarget*>("travel target")->Get();
|
TravelTarget* target = botAI->GetAiObjectContext()->GetValue<TravelTarget*>("travel target")->Get();
|
||||||
if (target)
|
if (target)
|
||||||
@ -612,54 +433,38 @@ void PlayerbotHolder::DisablePlayerBot(ObjectGuid guid)
|
|||||||
|
|
||||||
RemoveFromPlayerbotsMap(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap
|
RemoveFromPlayerbotsMap(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap
|
||||||
|
|
||||||
delete botAI;*/
|
delete botAI;
|
||||||
// [Crash fix] Centralized cleanup of pointer values in the context
|
|
||||||
ClearAIContextPointerValues(botAI);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerbotHolder::RemoveFromPlayerbotsMap(ObjectGuid guid)
|
void PlayerbotHolder::RemoveFromPlayerbotsMap(ObjectGuid guid)
|
||||||
// {
|
|
||||||
// playerBots.erase(guid);
|
|
||||||
// }
|
|
||||||
// Protected erase
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx);
|
|
||||||
playerBots.erase(guid);
|
playerBots.erase(guid);
|
||||||
}
|
}
|
||||||
|
|
||||||
Player* PlayerbotHolder::GetPlayerBot(ObjectGuid playerGuid) const
|
Player* PlayerbotHolder::GetPlayerBot(ObjectGuid playerGuid) const
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx); // We protect
|
|
||||||
PlayerBotMap::const_iterator it = playerBots.find(playerGuid);
|
PlayerBotMap::const_iterator it = playerBots.find(playerGuid);
|
||||||
return (it == playerBots.end()) ? nullptr : it->second;// (nullptr)
|
return (it == playerBots.end()) ? 0 : it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
Player* PlayerbotHolder::GetPlayerBot(ObjectGuid::LowType lowGuid) const
|
Player* PlayerbotHolder::GetPlayerBot(ObjectGuid::LowType lowGuid) const
|
||||||
{
|
{
|
||||||
ObjectGuid playerGuid = ObjectGuid::Create<HighGuid::Player>(lowGuid);
|
ObjectGuid playerGuid = ObjectGuid::Create<HighGuid::Player>(lowGuid);
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx); // We protect
|
|
||||||
PlayerBotMap::const_iterator it = playerBots.find(playerGuid);
|
PlayerBotMap::const_iterator it = playerBots.find(playerGuid);
|
||||||
return (it == playerBots.end()) ? nullptr : it->second;
|
return (it == playerBots.end()) ? 0 : it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerbotHolder::OnBotLogin(Player* const bot)
|
void PlayerbotHolder::OnBotLogin(Player* const bot)
|
||||||
{
|
{
|
||||||
// Prevent duplicate login
|
// Prevent duplicate login
|
||||||
/*if (playerBots.find(bot->GetGUID()) != playerBots.end())
|
if (playerBots.find(bot->GetGUID()) != playerBots.end())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}*/
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx);
|
|
||||||
if (playerBots.find(bot->GetGUID()) != playerBots.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
playerBots[bot->GetGUID()] = bot;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sPlayerbotsMgr->AddPlayerbotData(bot, true);
|
sPlayerbotsMgr->AddPlayerbotData(bot, true);
|
||||||
// playerBots[bot->GetGUID()] = bot;
|
playerBots[bot->GetGUID()] = bot;
|
||||||
|
|
||||||
OnBotLoginInternal(bot);
|
OnBotLoginInternal(bot);
|
||||||
|
|
||||||
@ -724,9 +529,6 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
|
|||||||
{
|
{
|
||||||
botAI->ResetStrategies(!sRandomPlayerbotMgr->IsRandomBot(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);
|
sPlayerbotDbStore->Load(botAI);
|
||||||
|
|
||||||
if (master && !master->HasUnitState(UNIT_STATE_IN_FLIGHT))
|
if (master && !master->HasUnitState(UNIT_STATE_IN_FLIGHT))
|
||||||
@ -735,17 +537,6 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
|
|||||||
bot->CleanupAfterTaxiFlight();
|
bot->CleanupAfterTaxiFlight();
|
||||||
}
|
}
|
||||||
|
|
||||||
// [Fix MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full: 0x00000000000019ba Type: Player Low: 6586] Ensure valid speeds before any next movement command
|
|
||||||
bot->StopMoving();
|
|
||||||
bot->UpdateSpeed(MOVE_WALK, true);
|
|
||||||
bot->UpdateSpeed(MOVE_RUN, true);
|
|
||||||
bot->UpdateSpeed(MOVE_SWIM, true);
|
|
||||||
bot->UpdateSpeed(MOVE_FLIGHT, true); // OK even if not flying
|
|
||||||
|
|
||||||
if (bot->GetSpeed(MOVE_RUN) <= 0.01f) // Belt-and-suspenders: if the run speed has stayed ~0, reset to the default rate
|
|
||||||
bot->SetSpeedRate(MOVE_RUN, 1.0f);
|
|
||||||
// End Fix
|
|
||||||
|
|
||||||
// check activity
|
// check activity
|
||||||
botAI->AllowActivity(ALL_ACTIVITY, true);
|
botAI->AllowActivity(ALL_ACTIVITY, true);
|
||||||
|
|
||||||
@ -757,21 +548,16 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
|
|||||||
if (master && master->GetGroup() && !group)
|
if (master && master->GetGroup() && !group)
|
||||||
{
|
{
|
||||||
Group* mgroup = master->GetGroup();
|
Group* mgroup = master->GetGroup();
|
||||||
// if (mgroup->GetMembersCount() >= 5)
|
if (mgroup->GetMembersCount() >= 5)
|
||||||
if (mgroup->GetMembersCount() + 1 > 5) // only convert in raid if the add of THIS bot make group > 5
|
|
||||||
{
|
{
|
||||||
if (!mgroup->isRaidGroup() && !mgroup->isLFGGroup() && !mgroup->isBGGroup() && !mgroup->isBFGroup())
|
if (!mgroup->isRaidGroup() && !mgroup->isLFGGroup() && !mgroup->isBGGroup() && !mgroup->isBFGroup())
|
||||||
{
|
{
|
||||||
mgroup->ConvertToRaid();
|
mgroup->ConvertToRaid();
|
||||||
}
|
}
|
||||||
//if (mgroup->isRaidGroup())
|
if (mgroup->isRaidGroup())
|
||||||
//{
|
{
|
||||||
//mgroup->AddMember(bot);
|
mgroup->AddMember(bot);
|
||||||
//}
|
}
|
||||||
mgroup->AddMember(bot);
|
|
||||||
|
|
||||||
LOG_DEBUG("mod-playerbots", "[GROUP] after add: members={}, isRaid={}, isLFG={}",
|
|
||||||
(int)mgroup->GetMembersCount(), mgroup->isRaidGroup() ? 1 : 0, mgroup->isLFGGroup() ? 1 : 0);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -950,11 +736,9 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (GET_PLAYERBOT_AI(bot))
|
if (GET_PLAYERBOT_AI(bot))
|
||||||
if (PlayerbotAI* ai = GET_PLAYERBOT_AI(bot)) // [Tidy/Crash fix] Acquire AI once and reuse; avoid multiple GET_PLAYERBOT_AI calls.
|
|
||||||
{
|
{
|
||||||
// if (Player* master = GET_PLAYERBOT_AI(bot)->GetMaster())
|
if (Player* master = GET_PLAYERBOT_AI(bot)->GetMaster())
|
||||||
if (Player* master = ai->GetMaster())
|
|
||||||
{
|
{
|
||||||
if (master->GetSession()->GetSecurity() <= SEC_PLAYER && sPlayerbotAIConfig->autoInitOnly &&
|
if (master->GetSession()->GetSecurity() <= SEC_PLAYER && sPlayerbotAIConfig->autoInitOnly &&
|
||||||
cmd != "init=auto")
|
cmd != "init=auto")
|
||||||
@ -1374,13 +1158,8 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
|||||||
// If the user requested a specific gender, skip any character that doesn't match.
|
// If the user requested a specific gender, skip any character that doesn't match.
|
||||||
if (gender != -1 && GetOfflinePlayerGender(guid) != gender)
|
if (gender != -1 && GetOfflinePlayerGender(guid) != gender)
|
||||||
continue;
|
continue;
|
||||||
/*if (botLoading.find(guid) != botLoading.end())
|
if (botLoading.find(guid) != botLoading.end())
|
||||||
continue;*/
|
continue;
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx);
|
|
||||||
if (botLoading.find(guid) != botLoading.end())
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (ObjectAccessor::FindConnectedPlayer(guid))
|
if (ObjectAccessor::FindConnectedPlayer(guid))
|
||||||
continue;
|
continue;
|
||||||
uint32 guildId = sCharacterCache->GetCharacterGuildIdByGuid(guid);
|
uint32 guildId = sCharacterCache->GetCharacterGuildIdByGuid(guid);
|
||||||
@ -1444,25 +1223,12 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
|||||||
|
|
||||||
if (charnameStr == "!" && master && master->GetSession()->GetSecurity() > SEC_GAMEMASTER)
|
if (charnameStr == "!" && master && master->GetSession()->GetSecurity() > SEC_GAMEMASTER)
|
||||||
{
|
{
|
||||||
/*for (PlayerBotMap::const_iterator i = GetPlayerBotsBegin(); i != GetPlayerBotsEnd(); ++i)
|
for (PlayerBotMap::const_iterator i = GetPlayerBotsBegin(); i != GetPlayerBotsEnd(); ++i)
|
||||||
{
|
{
|
||||||
if (Player* bot = i->second)
|
if (Player* bot = i->second)
|
||||||
if (bot->IsInWorld())
|
if (bot->IsInWorld())
|
||||||
bots.insert(bot->GetName());
|
bots.insert(bot->GetName());
|
||||||
}*/
|
}
|
||||||
// Snapshot under lock
|
|
||||||
std::vector<Player*> botsCopy;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx);
|
|
||||||
for (PlayerBotMap::const_iterator i = GetPlayerBotsBegin(); i != GetPlayerBotsEnd(); ++i)
|
|
||||||
botsCopy.push_back(i->second);
|
|
||||||
}
|
|
||||||
for (Player* const bot : botsCopy)
|
|
||||||
{
|
|
||||||
if (bot && bot->IsInWorld())
|
|
||||||
bots.insert(bot->GetName());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> chars = split(charnameStr, ',');
|
std::vector<std::string> chars = split(charnameStr, ',');
|
||||||
@ -1566,7 +1332,7 @@ uint32 PlayerbotHolder::GetAccountId(ObjectGuid guid)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*std::string const PlayerbotHolder::ListBots(Player* master)
|
std::string const PlayerbotHolder::ListBots(Player* master)
|
||||||
{
|
{
|
||||||
std::set<std::string> bots;
|
std::set<std::string> bots;
|
||||||
std::map<uint8, std::string> classNames;
|
std::map<uint8, std::string> classNames;
|
||||||
@ -1652,103 +1418,6 @@ uint32 PlayerbotHolder::GetAccountId(ObjectGuid guid)
|
|||||||
out << online[name] << name << " " << classes[name];
|
out << online[name] << name << " " << classes[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
return out.str();
|
|
||||||
}*/
|
|
||||||
|
|
||||||
std::string const PlayerbotHolder::ListBots(Player* master)
|
|
||||||
{
|
|
||||||
std::set<std::string> bots;
|
|
||||||
std::map<uint8, std::string> classNames;
|
|
||||||
|
|
||||||
classNames[CLASS_DEATH_KNIGHT] = "Death Knight";
|
|
||||||
classNames[CLASS_DRUID] = "Druid";
|
|
||||||
classNames[CLASS_HUNTER] = "Hunter";
|
|
||||||
classNames[CLASS_MAGE] = "Mage";
|
|
||||||
classNames[CLASS_PALADIN] = "Paladin";
|
|
||||||
classNames[CLASS_PRIEST] = "Priest";
|
|
||||||
classNames[CLASS_ROGUE] = "Rogue";
|
|
||||||
classNames[CLASS_SHAMAN] = "Shaman";
|
|
||||||
classNames[CLASS_WARLOCK] = "Warlock";
|
|
||||||
classNames[CLASS_WARRIOR] = "Warrior";
|
|
||||||
classNames[CLASS_DEATH_KNIGHT] = "DeathKnight";
|
|
||||||
|
|
||||||
std::map<std::string, std::string> online;
|
|
||||||
std::vector<std::string> names;
|
|
||||||
std::map<std::string, std::string> classes;
|
|
||||||
|
|
||||||
// Snapshot under lock
|
|
||||||
std::vector<Player*> botsCopy;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx);
|
|
||||||
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
|
||||||
botsCopy.push_back(it->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Player* const bot : botsCopy)
|
|
||||||
{
|
|
||||||
std::string const name = bot->GetName();
|
|
||||||
bots.insert(name);
|
|
||||||
|
|
||||||
names.push_back(name);
|
|
||||||
online[name] = "+";
|
|
||||||
classes[name] = classNames[bot->getClass()];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (master)
|
|
||||||
{
|
|
||||||
QueryResult results = CharacterDatabase.Query(
|
|
||||||
"SELECT class, name FROM characters WHERE account = {}",
|
|
||||||
master->GetSession()->GetAccountId());
|
|
||||||
|
|
||||||
if (results)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
Field* fields = results->Fetch();
|
|
||||||
uint8 cls = fields[0].Get<uint8>();
|
|
||||||
std::string const name = fields[1].Get<std::string>();
|
|
||||||
if (bots.find(name) == bots.end() && name != master->GetSession()->GetPlayerName())
|
|
||||||
{
|
|
||||||
names.push_back(name);
|
|
||||||
online[name] = "-";
|
|
||||||
classes[name] = classNames[cls];
|
|
||||||
}
|
|
||||||
} while (results->NextRow());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::sort(names.begin(), names.end());
|
|
||||||
|
|
||||||
if (master)
|
|
||||||
{
|
|
||||||
if (Group* group = master->GetGroup())
|
|
||||||
{
|
|
||||||
Group::MemberSlotList const& groupSlot = group->GetMemberSlots();
|
|
||||||
for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++)
|
|
||||||
{
|
|
||||||
Player* member = ObjectAccessor::FindPlayer(itr->guid);
|
|
||||||
if (member && sRandomPlayerbotMgr->IsRandomBot(member))
|
|
||||||
{
|
|
||||||
std::string const name = member->GetName();
|
|
||||||
|
|
||||||
names.push_back(name);
|
|
||||||
online[name] = "+";
|
|
||||||
classes[name] = classNames[member->getClass()];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostringstream out;
|
|
||||||
bool first = true;
|
|
||||||
out << "Bot roster: ";
|
|
||||||
for (std::vector<std::string>::iterator i = names.begin(); i != names.end(); ++i)
|
|
||||||
{
|
|
||||||
if (first) first = false; else out << ", ";
|
|
||||||
std::string const name = *i;
|
|
||||||
out << online[name] << name << " " << classes[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
return out.str();
|
return out.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1775,7 +1444,7 @@ std::string const PlayerbotHolder::LookupBots(Player* master)
|
|||||||
return ret_msg;
|
return ret_msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*uint32 PlayerbotHolder::GetPlayerbotsCountByClass(uint32 cls)
|
uint32 PlayerbotHolder::GetPlayerbotsCountByClass(uint32 cls)
|
||||||
{
|
{
|
||||||
uint32 count = 0;
|
uint32 count = 0;
|
||||||
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
||||||
@ -1787,25 +1456,6 @@ std::string const PlayerbotHolder::LookupBots(Player* master)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}*/
|
|
||||||
|
|
||||||
uint32 PlayerbotHolder::GetPlayerbotsCountByClass(uint32 cls)
|
|
||||||
{
|
|
||||||
uint32 count = 0;
|
|
||||||
|
|
||||||
// Snapshot under lock
|
|
||||||
std::vector<Player*> botsCopy;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx);
|
|
||||||
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
|
||||||
botsCopy.push_back(it->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Player* const bot : botsCopy)
|
|
||||||
if (bot && bot->IsInWorld() && bot->getClass() == cls)
|
|
||||||
++count;
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerbotMgr::PlayerbotMgr(Player* const master) : PlayerbotHolder(), master(master), lastErrorTell(0) {}
|
PlayerbotMgr::PlayerbotMgr(Player* const master) : PlayerbotHolder(), master(master), lastErrorTell(0) {}
|
||||||
@ -1822,42 +1472,6 @@ void PlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
|
|||||||
CheckTellErrors(elapsed);
|
CheckTellErrors(elapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*void PlayerbotMgr::HandleCommand(uint32 type, std::string const text)
|
|
||||||
{
|
|
||||||
Player* master = GetMaster();
|
|
||||||
if (!master)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (text.find(sPlayerbotAIConfig->commandSeparator) != std::string::npos)
|
|
||||||
{
|
|
||||||
std::vector<std::string> commands;
|
|
||||||
split(commands, text, sPlayerbotAIConfig->commandSeparator.c_str());
|
|
||||||
for (std::vector<std::string>::iterator i = commands.begin(); i != commands.end(); ++i)
|
|
||||||
{
|
|
||||||
HandleCommand(type, *i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
|
||||||
{
|
|
||||||
Player* const bot = it->second;
|
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
|
||||||
if (botAI)
|
|
||||||
botAI->HandleCommand(type, text, master);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin();
|
|
||||||
it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it)
|
|
||||||
{
|
|
||||||
Player* const bot = it->second;
|
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
|
||||||
if (botAI && botAI->GetMaster() == master)
|
|
||||||
botAI->HandleCommand(type, text, master);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
void PlayerbotMgr::HandleCommand(uint32 type, std::string const text)
|
void PlayerbotMgr::HandleCommand(uint32 type, std::string const text)
|
||||||
{
|
{
|
||||||
Player* master = GetMaster();
|
Player* master = GetMaster();
|
||||||
@ -1869,26 +1483,21 @@ void PlayerbotMgr::HandleCommand(uint32 type, std::string const text)
|
|||||||
std::vector<std::string> commands;
|
std::vector<std::string> commands;
|
||||||
split(commands, text, sPlayerbotAIConfig->commandSeparator.c_str());
|
split(commands, text, sPlayerbotAIConfig->commandSeparator.c_str());
|
||||||
for (std::vector<std::string>::iterator i = commands.begin(); i != commands.end(); ++i)
|
for (std::vector<std::string>::iterator i = commands.begin(); i != commands.end(); ++i)
|
||||||
|
{
|
||||||
HandleCommand(type, *i);
|
HandleCommand(type, *i);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Snapshot of "master" bots under lock to avoid race conditions
|
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
||||||
std::vector<Player*> botsCopy;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx);
|
|
||||||
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
|
||||||
botsCopy.push_back(it->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Player* const bot : botsCopy)
|
|
||||||
{
|
{
|
||||||
|
Player* const bot = it->second;
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||||
if (botAI)
|
if (botAI)
|
||||||
botAI->HandleCommand(type, text, master);
|
botAI->HandleCommand(type, text, master);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Random bots : unchanges
|
|
||||||
for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin();
|
for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin();
|
||||||
it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it)
|
it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it)
|
||||||
{
|
{
|
||||||
@ -1899,65 +1508,18 @@ void PlayerbotMgr::HandleCommand(uint32 type, std::string const text)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*void PlayerbotMgr::HandleMasterIncomingPacket(WorldPacket const& packet)
|
|
||||||
{
|
|
||||||
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
|
||||||
{
|
|
||||||
Player* const bot = it->second;
|
|
||||||
if (!bot)
|
|
||||||
continue;
|
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
|
||||||
if (botAI)
|
|
||||||
botAI->HandleMasterIncomingPacket(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin();
|
|
||||||
it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it)
|
|
||||||
{
|
|
||||||
Player* const bot = it->second;
|
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
|
||||||
if (botAI && botAI->GetMaster() == GetMaster())
|
|
||||||
botAI->HandleMasterIncomingPacket(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (packet.GetOpcode())
|
|
||||||
{
|
|
||||||
// if master is logging out, log out all bots
|
|
||||||
case CMSG_LOGOUT_REQUEST:
|
|
||||||
{
|
|
||||||
LogoutAllBots();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// if master cancelled logout, cancel too
|
|
||||||
case CMSG_LOGOUT_CANCEL:
|
|
||||||
{
|
|
||||||
CancelLogout();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
void PlayerbotMgr::HandleMasterIncomingPacket(WorldPacket const& packet)
|
void PlayerbotMgr::HandleMasterIncomingPacket(WorldPacket const& packet)
|
||||||
{
|
{
|
||||||
// Snapshot of "master" bots under lock
|
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
||||||
std::vector<Player*> botsCopy;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx);
|
|
||||||
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
|
||||||
botsCopy.push_back(it->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Player* const bot : botsCopy)
|
|
||||||
{
|
{
|
||||||
|
Player* const bot = it->second;
|
||||||
if (!bot)
|
if (!bot)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||||
if (botAI)
|
if (botAI)
|
||||||
botAI->HandleMasterIncomingPacket(packet);
|
botAI->HandleMasterIncomingPacket(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Boucle random bots (inchangée)
|
|
||||||
for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin();
|
for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin();
|
||||||
it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it)
|
it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it)
|
||||||
{
|
{
|
||||||
@ -1984,8 +1546,7 @@ void PlayerbotMgr::HandleMasterIncomingPacket(WorldPacket const& packet)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlayerbotMgr::HandleMasterOutgoingPacket(WorldPacket const& packet)
|
||||||
/*void PlayerbotMgr::HandleMasterOutgoingPacket(WorldPacket const& packet)
|
|
||||||
{
|
{
|
||||||
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
||||||
{
|
{
|
||||||
@ -1995,33 +1556,6 @@ void PlayerbotMgr::HandleMasterIncomingPacket(WorldPacket const& packet)
|
|||||||
botAI->HandleMasterOutgoingPacket(packet);
|
botAI->HandleMasterOutgoingPacket(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin();
|
|
||||||
it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it)
|
|
||||||
{
|
|
||||||
Player* const bot = it->second;
|
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
|
||||||
if (botAI && botAI->GetMaster() == GetMaster())
|
|
||||||
botAI->HandleMasterOutgoingPacket(packet);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
void PlayerbotMgr::HandleMasterOutgoingPacket(WorldPacket const& packet)
|
|
||||||
{
|
|
||||||
// Snapshot of "master" bots under lock
|
|
||||||
std::vector<Player*> botsCopy;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx);
|
|
||||||
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
|
||||||
botsCopy.push_back(it->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Player* const bot : botsCopy)
|
|
||||||
{
|
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
|
||||||
if (botAI)
|
|
||||||
botAI->HandleMasterOutgoingPacket(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Random bots loop unchanged
|
|
||||||
for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin();
|
for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin();
|
||||||
it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it)
|
it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it)
|
||||||
{
|
{
|
||||||
@ -2032,7 +1566,7 @@ void PlayerbotMgr::HandleMasterOutgoingPacket(WorldPacket const& packet)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*void PlayerbotMgr::SaveToDB()
|
void PlayerbotMgr::SaveToDB()
|
||||||
{
|
{
|
||||||
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
||||||
{
|
{
|
||||||
@ -2047,32 +1581,6 @@ void PlayerbotMgr::HandleMasterOutgoingPacket(WorldPacket const& packet)
|
|||||||
if (GET_PLAYERBOT_AI(bot) && GET_PLAYERBOT_AI(bot)->GetMaster() == GetMaster())
|
if (GET_PLAYERBOT_AI(bot) && GET_PLAYERBOT_AI(bot)->GetMaster() == GetMaster())
|
||||||
bot->SaveToDB(false, false);
|
bot->SaveToDB(false, false);
|
||||||
}
|
}
|
||||||
}*/
|
|
||||||
|
|
||||||
void PlayerbotMgr::SaveToDB()
|
|
||||||
{
|
|
||||||
// Snapshot of "master" bots under lock
|
|
||||||
std::vector<Player*> botsCopy;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_botMapsMx);
|
|
||||||
for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
|
|
||||||
botsCopy.push_back(it->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Player* const bot : botsCopy)
|
|
||||||
{
|
|
||||||
if (bot)
|
|
||||||
bot->SaveToDB(false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin();
|
|
||||||
it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it)
|
|
||||||
{
|
|
||||||
Player* const bot = it->second;
|
|
||||||
PlayerbotAI* ai = GET_PLAYERBOT_AI(bot);
|
|
||||||
if (ai && ai->GetMaster() == GetMaster())
|
|
||||||
bot->SaveToDB(false, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerbotMgr::OnBotLoginInternal(Player* const bot)
|
void PlayerbotMgr::OnBotLoginInternal(Player* const bot)
|
||||||
@ -2085,7 +1593,7 @@ void PlayerbotMgr::OnBotLoginInternal(Player* const bot)
|
|||||||
botAI->SetMaster(master);
|
botAI->SetMaster(master);
|
||||||
botAI->ResetStrategies();
|
botAI->ResetStrategies();
|
||||||
|
|
||||||
LOG_INFO("mod-playerbots", "Bot {} logged in", bot->GetName().c_str());
|
LOG_INFO("playerbots", "Bot {} logged in", bot->GetName().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerbotMgr::OnPlayerLogin(Player* player)
|
void PlayerbotMgr::OnPlayerLogin(Player* player)
|
||||||
@ -2278,7 +1786,7 @@ void PlayerbotsMgr::RemovePlayerbotAI(ObjectGuid const& guid, bool removeMgrEntr
|
|||||||
{
|
{
|
||||||
delete it->second;
|
delete it->second;
|
||||||
_playerbotsAIMap.erase(it);
|
_playerbotsAIMap.erase(it);
|
||||||
LOG_DEBUG("mod-playerbots", "Removed stale AI for GUID {}",
|
LOG_DEBUG("playerbots", "Removed stale AI for GUID {}",
|
||||||
static_cast<uint64>(guid.GetRawValue()));
|
static_cast<uint64>(guid.GetRawValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,6 @@
|
|||||||
#include "cs_playerbots.h"
|
#include "cs_playerbots.h"
|
||||||
#include "cmath"
|
#include "cmath"
|
||||||
#include "BattleGroundTactics.h"
|
#include "BattleGroundTactics.h"
|
||||||
#include "ObjectAccessor.h"
|
|
||||||
|
|
||||||
class PlayerbotsDatabaseScript : public DatabaseScript
|
class PlayerbotsDatabaseScript : public DatabaseScript
|
||||||
{
|
{
|
||||||
@ -109,7 +108,7 @@ public:
|
|||||||
"|cffcccccchttps://github.com/liyunfan1223/mod-playerbots|r");
|
"|cffcccccchttps://github.com/liyunfan1223/mod-playerbots|r");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if (sPlayerbotAIConfig->enabled || sPlayerbotAIConfig->randomBotAutologin)
|
if (sPlayerbotAIConfig->enabled || sPlayerbotAIConfig->randomBotAutologin)
|
||||||
{
|
{
|
||||||
std::string roundedTime =
|
std::string roundedTime =
|
||||||
std::to_string(std::ceil((sPlayerbotAIConfig->maxRandomBots * 0.11 / 60) * 10) / 10.0);
|
std::to_string(std::ceil((sPlayerbotAIConfig->maxRandomBots * 0.11 / 60) * 10) / 10.0);
|
||||||
@ -118,7 +117,7 @@ public:
|
|||||||
ChatHandler(player->GetSession()).SendSysMessage(
|
ChatHandler(player->GetSession()).SendSysMessage(
|
||||||
"|cff00ff00Playerbots:|r bot initialization at server startup takes about '"
|
"|cff00ff00Playerbots:|r bot initialization at server startup takes about '"
|
||||||
+ roundedTime + "' minutes.");
|
+ roundedTime + "' minutes.");
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +136,7 @@ public:
|
|||||||
|
|
||||||
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Player* receiver) override
|
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Player* receiver) override
|
||||||
{
|
{
|
||||||
/*if (type == CHAT_MSG_WHISPER)
|
if (type == CHAT_MSG_WHISPER)
|
||||||
{
|
{
|
||||||
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(receiver))
|
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(receiver))
|
||||||
{
|
{
|
||||||
@ -145,23 +144,14 @@ public:
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
if (type == CHAT_MSG_WHISPER && receiver) // [Crash Fix] Add non-null receiver check to avoid calling on a null pointer in edge cases.
|
|
||||||
{
|
|
||||||
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(receiver))
|
|
||||||
{
|
|
||||||
botAI->HandleCommand(type, msg, player);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override
|
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override
|
||||||
{
|
{
|
||||||
/*for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
||||||
{
|
{
|
||||||
if (Player* member = itr->GetSource())
|
if (Player* member = itr->GetSource())
|
||||||
{
|
{
|
||||||
@ -170,18 +160,6 @@ public:
|
|||||||
botAI->HandleCommand(type, msg, player);
|
botAI->HandleCommand(type, msg, player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}*/
|
|
||||||
if (!group) return; // [Crash Fix] 'group' should not be null in this hook, but this safeguard prevents a crash if the caller changes or in case of an unexpected call.
|
|
||||||
|
|
||||||
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
|
||||||
{
|
|
||||||
Player* member = itr->GetSource();
|
|
||||||
if (!member) continue;
|
|
||||||
|
|
||||||
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(member))
|
|
||||||
{
|
|
||||||
botAI->HandleCommand(type, msg, player);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,9 +176,7 @@ public:
|
|||||||
{
|
{
|
||||||
if (bot->GetGuildId() == player->GetGuildId())
|
if (bot->GetGuildId() == player->GetGuildId())
|
||||||
{
|
{
|
||||||
// GET_PLAYERBOT_AI(bot)->HandleCommand(type, msg, player);
|
GET_PLAYERBOT_AI(bot)->HandleCommand(type, msg, player);
|
||||||
if (PlayerbotAI* ai = GET_PLAYERBOT_AI(bot)) // [Crash Fix] Possible crash source because we don't check if the returned pointer is not null
|
|
||||||
ai->HandleCommand(type, msg, player);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -335,7 +311,7 @@ class PlayerbotsScript : public PlayerbotScript
|
|||||||
public:
|
public:
|
||||||
PlayerbotsScript() : PlayerbotScript("PlayerbotsScript") {}
|
PlayerbotsScript() : PlayerbotScript("PlayerbotsScript") {}
|
||||||
|
|
||||||
/*bool OnPlayerbotCheckLFGQueue(lfg::Lfg5Guids const& guidsList) override
|
bool OnPlayerbotCheckLFGQueue(lfg::Lfg5Guids const& guidsList) override
|
||||||
{
|
{
|
||||||
bool nonBotFound = false;
|
bool nonBotFound = false;
|
||||||
for (ObjectGuid const& guid : guidsList.guids)
|
for (ObjectGuid const& guid : guidsList.guids)
|
||||||
@ -349,137 +325,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return nonBotFound;
|
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
|
void OnPlayerbotCheckKillTask(Player* player, Unit* victim) override
|
||||||
{
|
{
|
||||||
|
|||||||
@ -49,46 +49,4 @@ int strcmpi(char const* s1, char const* s2);
|
|||||||
#define GAI_VALUE(type, name) sSharedValueContext->getGlobalValue<type>(name)->Get()
|
#define GAI_VALUE(type, name) sSharedValueContext->getGlobalValue<type>(name)->Get()
|
||||||
#define GAI_VALUE2(type, name, param) sSharedValueContext->getGlobalValue<type>(name, param)->Get()
|
#define GAI_VALUE2(type, name, param) sSharedValueContext->getGlobalValue<type>(name, param)->Get()
|
||||||
|
|
||||||
// ---- Safe teleport wrappers (module-only) ----
|
|
||||||
#include "Map.h"
|
|
||||||
#include <cmath>
|
|
||||||
#include "TravelMgr.h"
|
|
||||||
|
|
||||||
inline bool TeleportToSafe(Player* p, uint32 mapId, float x, float y, float z, float o)
|
|
||||||
{
|
|
||||||
if (!p) return false;
|
|
||||||
|
|
||||||
// If the height is invalid (-200000) or not finite, attempt ONE correction on the same map.
|
|
||||||
if (z <= -199000.0f || !std::isfinite(z))
|
|
||||||
{
|
|
||||||
if (p->GetMapId() == mapId && p->GetMap())
|
|
||||||
{
|
|
||||||
float hz = p->GetMap()->GetHeight(p->GetPhaseMask(), x, y, p->GetPositionZ(), true);
|
|
||||||
if (hz > -199000.0f && std::isfinite(hz))
|
|
||||||
z = hz;
|
|
||||||
else
|
|
||||||
return false; // still invalid -> cancel the TP
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false; // different map: do not "guess" the height here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p->TeleportTo(mapId, x, y, z, o);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool TeleportToSafe(Player* p, Position const& pos)
|
|
||||||
{
|
|
||||||
// Position doesn't have mapId: we keep actual bot map
|
|
||||||
return TeleportToSafe(p, p->GetMapId(),
|
|
||||||
pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(),
|
|
||||||
pos.GetOrientation());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool TeleportToSafe(Player* p, WorldPosition pos)
|
|
||||||
{
|
|
||||||
return TeleportToSafe(p, pos.getMapId(), pos.getX(), pos.getY(), pos.getZ(), pos.getO());
|
|
||||||
}
|
|
||||||
// ---- /Safe teleport wrappers ----
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -994,18 +994,9 @@ void RandomPlayerbotMgr::CheckBgQueue()
|
|||||||
isRated = ginfo.IsRated;
|
isRated = ginfo.IsRated;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if (bgQueue.IsPlayerInvitedToRatedArena(player->GetGUID()) ||
|
if (bgQueue.IsPlayerInvitedToRatedArena(player->GetGUID()) ||
|
||||||
(player->InArena() && player->GetBattleground()->isRated()))
|
(player->InArena() && player->GetBattleground()->isRated()))
|
||||||
isRated = true;*/
|
|
||||||
if (bgQueue.IsPlayerInvitedToRatedArena(player->GetGUID())) // [Crash Fix] Issue Crash in RandomPlayerbotMgr:1018 #1528
|
|
||||||
{
|
|
||||||
isRated = true;
|
isRated = true;
|
||||||
}
|
|
||||||
else if (Battleground const* bg = player->GetBattleground())
|
|
||||||
{
|
|
||||||
if (player->InArena() && bg->isRated())
|
|
||||||
isRated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isRated)
|
if (isRated)
|
||||||
BattlegroundData[queueTypeId][bracketId].ratedArenaPlayerCount++;
|
BattlegroundData[queueTypeId][bracketId].ratedArenaPlayerCount++;
|
||||||
@ -1020,24 +1011,15 @@ void RandomPlayerbotMgr::CheckBgQueue()
|
|||||||
else
|
else
|
||||||
BattlegroundData[queueTypeId][bracketId].bgHordePlayerCount++;
|
BattlegroundData[queueTypeId][bracketId].bgHordePlayerCount++;
|
||||||
|
|
||||||
/*// If a player has joined the BG, update the instance count in BattlegroundData (for consistency)
|
// If a player has joined the BG, update the instance count in BattlegroundData (for consistency)
|
||||||
if (player->InBattleground())
|
if (player->InBattleground())
|
||||||
{
|
{
|
||||||
std::vector<uint32>* instanceIds = nullptr;
|
std::vector<uint32>* instanceIds = nullptr;
|
||||||
uint32 instanceId = player->GetBattleground()->GetInstanceID();
|
uint32 instanceId = player->GetBattleground()->GetInstanceID();
|
||||||
|
|
||||||
instanceIds = &BattlegroundData[queueTypeId][bracketId].bgInstances;*/
|
|
||||||
// If a player has joined the BG, update the instance count in BattlegroundData (for consistency)
|
|
||||||
if (Battleground const* bg = player->GetBattleground()) // [Crash Fix] Issue Crash in RandomPlayerbotMgr:1018 #1528
|
|
||||||
{
|
|
||||||
std::vector<uint32>* instanceIds = nullptr;
|
|
||||||
uint32 instanceId = bg->GetInstanceID();
|
|
||||||
|
|
||||||
instanceIds = &BattlegroundData[queueTypeId][bracketId].bgInstances;
|
instanceIds = &BattlegroundData[queueTypeId][bracketId].bgInstances;
|
||||||
|
if (instanceIds &&
|
||||||
if (instanceIds &&
|
|
||||||
std::find(instanceIds->begin(), instanceIds->end(), instanceId) == instanceIds->end())
|
std::find(instanceIds->begin(), instanceIds->end(), instanceId) == instanceIds->end())
|
||||||
|
|
||||||
instanceIds->push_back(instanceId);
|
instanceIds->push_back(instanceId);
|
||||||
|
|
||||||
BattlegroundData[queueTypeId][bracketId].bgInstanceCount = instanceIds->size();
|
BattlegroundData[queueTypeId][bracketId].bgInstanceCount = instanceIds->size();
|
||||||
@ -1100,20 +1082,10 @@ void RandomPlayerbotMgr::CheckBgQueue()
|
|||||||
isRated = ginfo.IsRated;
|
isRated = ginfo.IsRated;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if (bgQueue.IsPlayerInvitedToRatedArena(guid) || (bot->InArena() && bot->GetBattleground()->isRated()))
|
if (bgQueue.IsPlayerInvitedToRatedArena(guid) || (bot->InArena() && bot->GetBattleground()->isRated()))
|
||||||
isRated = true;*/
|
|
||||||
if (bgQueue.IsPlayerInvitedToRatedArena(guid)) // [Crash Fix] Issue Crash in RandomPlayerbotMgr:1018 #1528
|
|
||||||
{
|
|
||||||
isRated = true;
|
isRated = true;
|
||||||
}
|
|
||||||
else if (Battleground const* bg = bot->GetBattleground())
|
|
||||||
{
|
|
||||||
if (bot->InArena() && bg->isRated())
|
|
||||||
isRated = true;
|
|
||||||
}
|
|
||||||
// END [Crash Fix] Issue Crash in RandomPlayerbotMgr:1018 #1528
|
|
||||||
|
|
||||||
if (isRated)
|
if (isRated)
|
||||||
BattlegroundData[queueTypeId][bracketId].ratedArenaBotCount++;
|
BattlegroundData[queueTypeId][bracketId].ratedArenaBotCount++;
|
||||||
else
|
else
|
||||||
BattlegroundData[queueTypeId][bracketId].skirmishArenaBotCount++;
|
BattlegroundData[queueTypeId][bracketId].skirmishArenaBotCount++;
|
||||||
@ -1126,15 +1098,10 @@ void RandomPlayerbotMgr::CheckBgQueue()
|
|||||||
BattlegroundData[queueTypeId][bracketId].bgHordeBotCount++;
|
BattlegroundData[queueTypeId][bracketId].bgHordeBotCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if (bot->InBattleground())
|
if (bot->InBattleground())
|
||||||
{
|
{
|
||||||
std::vector<uint32>* instanceIds = nullptr;
|
std::vector<uint32>* instanceIds = nullptr;
|
||||||
uint32 instanceId = bot->GetBattleground()->GetInstanceID();*/
|
uint32 instanceId = bot->GetBattleground()->GetInstanceID();
|
||||||
if (Battleground const* bg = bot->GetBattleground()) // [Crash Fix] Issue Crash in RandomPlayerbotMgr:1018 #1528
|
|
||||||
{
|
|
||||||
std::vector<uint32>* instanceIds = nullptr;
|
|
||||||
uint32 instanceId = bg->GetInstanceID();
|
|
||||||
//END [Crash Fix] Issue Crash in RandomPlayerbotMgr:1018 #1528
|
|
||||||
bool isArena = false;
|
bool isArena = false;
|
||||||
bool isRated = false;
|
bool isRated = false;
|
||||||
|
|
||||||
@ -1142,8 +1109,7 @@ void RandomPlayerbotMgr::CheckBgQueue()
|
|||||||
if (bot->InArena())
|
if (bot->InArena())
|
||||||
{
|
{
|
||||||
isArena = true;
|
isArena = true;
|
||||||
// if (bot->GetBattleground()->isRated())
|
if (bot->GetBattleground()->isRated())
|
||||||
if (bg->isRated()) // [Crash Fix] Issue Crash in RandomPlayerbotMgr:1018 #1528
|
|
||||||
{
|
{
|
||||||
isRated = true;
|
isRated = true;
|
||||||
instanceIds = &BattlegroundData[queueTypeId][bracketId].ratedArenaInstances;
|
instanceIds = &BattlegroundData[queueTypeId][bracketId].ratedArenaInstances;
|
||||||
@ -1759,11 +1725,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent blink to be detected by visible real players
|
// Prevent blink to be detected by visible real players
|
||||||
/*if (botAI->HasPlayerNearby(150.0f))
|
if (botAI->HasPlayerNearby(150.0f))
|
||||||
{
|
|
||||||
break;
|
|
||||||
}*/
|
|
||||||
if (botAI && botAI->HasPlayerNearby(150.0f)) // [Crash fix] 'botAI' can be null earlier in the function.
|
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1772,8 +1734,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
|
|||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||||
if (botAI)
|
if (botAI)
|
||||||
botAI->Reset(true);
|
botAI->Reset(true);
|
||||||
//bot->TeleportTo(loc.GetMapId(), x, y, z, 0);
|
bot->TeleportTo(loc.GetMapId(), x, y, z, 0);
|
||||||
TeleportToSafe(bot, loc.GetMapId(), x, y, z, 0); // [Fix] Avoid silly teleports
|
|
||||||
bot->SendMovementFlagUpdate();
|
bot->SendMovementFlagUpdate();
|
||||||
|
|
||||||
if (pmo)
|
if (pmo)
|
||||||
@ -2372,10 +2333,8 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot)
|
|||||||
PlayerbotsDatabase.Execute(stmt);
|
PlayerbotsDatabase.Execute(stmt);
|
||||||
|
|
||||||
// teleport to a random inn for bot level
|
// teleport to a random inn for bot level
|
||||||
/*if (GET_PLAYERBOT_AI(bot))
|
if (GET_PLAYERBOT_AI(bot))
|
||||||
GET_PLAYERBOT_AI(bot)->Reset(true);*/
|
GET_PLAYERBOT_AI(bot)->Reset(true);
|
||||||
if (auto* ai = GET_PLAYERBOT_AI(bot)) // [Crash fix] Avoid 2 calls to GET_PLAYERBOT_AI and protect the dereference.
|
|
||||||
ai->Reset(true);
|
|
||||||
|
|
||||||
if (bot->GetGroup())
|
if (bot->GetGroup())
|
||||||
bot->RemoveFromGroup();
|
bot->RemoveFromGroup();
|
||||||
@ -2415,10 +2374,8 @@ void RandomPlayerbotMgr::RandomizeMin(Player* bot)
|
|||||||
PlayerbotsDatabase.Execute(stmt);
|
PlayerbotsDatabase.Execute(stmt);
|
||||||
|
|
||||||
// teleport to a random inn for bot level
|
// teleport to a random inn for bot level
|
||||||
/*if (GET_PLAYERBOT_AI(bot))
|
if (GET_PLAYERBOT_AI(bot))
|
||||||
GET_PLAYERBOT_AI(bot)->Reset(true);*/
|
GET_PLAYERBOT_AI(bot)->Reset(true);
|
||||||
if (auto* ai = GET_PLAYERBOT_AI(bot)) // [Crash fix] Avoid 2 calls to GET_PLAYERBOT_AI and protect the dereference.
|
|
||||||
ai->Reset(true);
|
|
||||||
|
|
||||||
if (bot->GetGroup())
|
if (bot->GetGroup())
|
||||||
bot->RemoveFromGroup();
|
bot->RemoveFromGroup();
|
||||||
@ -2511,7 +2468,7 @@ void RandomPlayerbotMgr::Refresh(Player* bot)
|
|||||||
|
|
||||||
bool RandomPlayerbotMgr::IsRandomBot(Player* bot)
|
bool RandomPlayerbotMgr::IsRandomBot(Player* bot)
|
||||||
{
|
{
|
||||||
/*if (bot && GET_PLAYERBOT_AI(bot))
|
if (bot && GET_PLAYERBOT_AI(bot))
|
||||||
{
|
{
|
||||||
if (GET_PLAYERBOT_AI(bot)->IsRealPlayer())
|
if (GET_PLAYERBOT_AI(bot)->IsRealPlayer())
|
||||||
return false;
|
return false;
|
||||||
@ -2521,17 +2478,6 @@ bool RandomPlayerbotMgr::IsRandomBot(Player* bot)
|
|||||||
return IsRandomBot(bot->GetGUID().GetCounter());
|
return IsRandomBot(bot->GetGUID().GetCounter());
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;*/
|
|
||||||
|
|
||||||
if (bot) // [Tidy] Single AI acquisition + same logic.
|
|
||||||
{
|
|
||||||
if (auto* ai = GET_PLAYERBOT_AI(bot))
|
|
||||||
{
|
|
||||||
if (ai->IsRealPlayer())
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return IsRandomBot(bot->GetGUID().GetCounter());
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2549,7 +2495,7 @@ bool RandomPlayerbotMgr::IsRandomBot(ObjectGuid::LowType bot)
|
|||||||
|
|
||||||
bool RandomPlayerbotMgr::IsAddclassBot(Player* bot)
|
bool RandomPlayerbotMgr::IsAddclassBot(Player* bot)
|
||||||
{
|
{
|
||||||
/*if (bot && GET_PLAYERBOT_AI(bot))
|
if (bot && GET_PLAYERBOT_AI(bot))
|
||||||
{
|
{
|
||||||
if (GET_PLAYERBOT_AI(bot)->IsRealPlayer())
|
if (GET_PLAYERBOT_AI(bot)->IsRealPlayer())
|
||||||
return false;
|
return false;
|
||||||
@ -2559,17 +2505,6 @@ bool RandomPlayerbotMgr::IsAddclassBot(Player* bot)
|
|||||||
return IsAddclassBot(bot->GetGUID().GetCounter());
|
return IsAddclassBot(bot->GetGUID().GetCounter());
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;*/
|
|
||||||
|
|
||||||
if (bot) // [Tidy] Single AI acquisition + same logic.
|
|
||||||
{
|
|
||||||
if (auto* ai = GET_PLAYERBOT_AI(bot))
|
|
||||||
{
|
|
||||||
if (ai->IsRealPlayer())
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return IsAddclassBot(bot->GetGUID().GetCounter());
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2909,9 +2844,8 @@ void RandomPlayerbotMgr::HandleCommand(uint32 type, std::string const text, Play
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// GET_PLAYERBOT_AI(bot)->HandleCommand(type, text, fromPlayer); // Possible crash source because we don't check if the returned pointer is not null
|
|
||||||
if (auto* ai = GET_PLAYERBOT_AI(bot)) // [Crash fix] Protect the call on a null AI (World/General chat path).
|
GET_PLAYERBOT_AI(bot)->HandleCommand(type, text, fromPlayer);
|
||||||
ai->HandleCommand(type, text, fromPlayer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2984,7 +2918,7 @@ void RandomPlayerbotMgr::OnPlayerLogin(Player* player)
|
|||||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||||
{
|
{
|
||||||
Player* member = gref->GetSource();
|
Player* member = gref->GetSource();
|
||||||
/*PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||||
if (botAI && member == player && (!botAI->GetMaster() || GET_PLAYERBOT_AI(botAI->GetMaster())))
|
if (botAI && member == player && (!botAI->GetMaster() || GET_PLAYERBOT_AI(botAI->GetMaster())))
|
||||||
{
|
{
|
||||||
if (!bot->InBattleground())
|
if (!bot->InBattleground())
|
||||||
@ -2995,20 +2929,6 @@ void RandomPlayerbotMgr::OnPlayerLogin(Player* player)
|
|||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}*/
|
|
||||||
if (auto* botAI = GET_PLAYERBOT_AI(bot)) // [Tidy] Avoid GET_PLAYERBOT_AI(...) on a potentially null master.
|
|
||||||
{
|
|
||||||
Player* master = botAI->GetMaster();
|
|
||||||
if (member == player && (!master || GET_PLAYERBOT_AI(master)))
|
|
||||||
{
|
|
||||||
if (!bot->InBattleground())
|
|
||||||
{
|
|
||||||
botAI->SetMaster(player);
|
|
||||||
botAI->ResetStrategies();
|
|
||||||
botAI->TellMaster("Hello");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3048,8 +2968,7 @@ void RandomPlayerbotMgr::OnPlayerLogin(Player* player)
|
|||||||
} while (true);
|
} while (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// player->TeleportTo(botPos);
|
player->TeleportTo(botPos);
|
||||||
TeleportToSafe(player, botPos); // [Fix] Avoid silly teleports
|
|
||||||
|
|
||||||
// player->Relocate(botPos.getX(), botPos.getY(), botPos.getZ(), botPos.getO());
|
// player->Relocate(botPos.getX(), botPos.getY(), botPos.getZ(), botPos.getO());
|
||||||
}
|
}
|
||||||
@ -3148,28 +3067,12 @@ void RandomPlayerbotMgr::PrintStats()
|
|||||||
lvlPerClass[bot->getClass()] += bot->GetLevel();
|
lvlPerClass[bot->getClass()] += bot->GetLevel();
|
||||||
lvlPerRace[bot->getRace()] += bot->GetLevel();
|
lvlPerRace[bot->getRace()] += bot->GetLevel();
|
||||||
|
|
||||||
/*PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||||
if (botAI->AllowActivity())
|
if (botAI->AllowActivity())
|
||||||
++active;
|
++active;
|
||||||
|
|
||||||
if (botAI->GetAiObjectContext()->GetValue<bool>("random bot update")->Get())
|
if (botAI->GetAiObjectContext()->GetValue<bool>("random bot update")->Get())
|
||||||
++update;*/
|
++update;
|
||||||
|
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); // [Crash fix] Declare botAI in the loop scope and exit early if null,
|
|
||||||
if (!botAI)
|
|
||||||
continue; // real player / no AI → ignore this bot for stats
|
|
||||||
|
|
||||||
if (botAI->AllowActivity())
|
|
||||||
++active;
|
|
||||||
|
|
||||||
// Secure access to the context and the value
|
|
||||||
if (AiObjectContext* ctx = botAI->GetAiObjectContext())
|
|
||||||
{
|
|
||||||
if (auto* v = ctx->GetValue<bool>("random bot update"))
|
|
||||||
if (v->Get())
|
|
||||||
++update;
|
|
||||||
}
|
|
||||||
// End CrashFix
|
|
||||||
|
|
||||||
uint32 botId = bot->GetGUID().GetCounter();
|
uint32 botId = bot->GetGUID().GetCounter();
|
||||||
if (!GetEventValue(botId, "randomize"))
|
if (!GetEventValue(botId, "randomize"))
|
||||||
|
|||||||
@ -1227,7 +1227,7 @@ std::string const QuestObjectiveTravelDestination::getTitle()
|
|||||||
return out.str();
|
return out.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*bool RpgTravelDestination::isActive(Player* bot) // Old Code
|
bool RpgTravelDestination::isActive(Player* bot)
|
||||||
{
|
{
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||||
AiObjectContext* context = botAI->GetAiObjectContext();
|
AiObjectContext* context = botAI->GetAiObjectContext();
|
||||||
@ -1264,62 +1264,6 @@ std::string const QuestObjectiveTravelDestination::getTitle()
|
|||||||
ReputationRank reaction = bot->GetReputationRank(factionEntry->faction);
|
ReputationRank reaction = bot->GetReputationRank(factionEntry->faction);
|
||||||
|
|
||||||
return reaction > REP_NEUTRAL;
|
return reaction > REP_NEUTRAL;
|
||||||
}*/
|
|
||||||
|
|
||||||
bool RpgTravelDestination::isActive(Player* bot)
|
|
||||||
{
|
|
||||||
// [Crash fix] Never dereference the AI if the player is real (null AI).
|
|
||||||
if (!bot)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
|
||||||
if (!botAI)
|
|
||||||
return false; // real player (no AI) => inactive destination
|
|
||||||
|
|
||||||
AiObjectContext* context = botAI->GetAiObjectContext();
|
|
||||||
if (!context)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
CreatureTemplate const* cInfo = GetCreatureTemplate();
|
|
||||||
if (!cInfo)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
bool isUsefull = false;
|
|
||||||
|
|
||||||
if (cInfo->npcflag & UNIT_NPC_FLAG_VENDOR)
|
|
||||||
if (AI_VALUE2_LAZY(bool, "group or", "should sell,can sell,following party,near leader"))
|
|
||||||
isUsefull = true;
|
|
||||||
|
|
||||||
if (cInfo->npcflag & UNIT_NPC_FLAG_REPAIR)
|
|
||||||
if (AI_VALUE2_LAZY(bool, "group or", "should repair,can repair,following party,near leader"))
|
|
||||||
isUsefull = true;
|
|
||||||
|
|
||||||
if (!isUsefull)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// [Crash fix] Read the ignore list via 'context' and check that the Value exists
|
|
||||||
GuidSet const* ignoreList = nullptr;
|
|
||||||
if (auto* value = context->GetValue<GuidSet&>("ignore rpg target"))
|
|
||||||
ignoreList = &value->Get();
|
|
||||||
|
|
||||||
if (ignoreList)
|
|
||||||
{
|
|
||||||
for (ObjectGuid const& guid : *ignoreList)
|
|
||||||
{
|
|
||||||
if (guid.GetEntry() == getEntry())
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Secure access to the faction template
|
|
||||||
if (FactionTemplateEntry const* factionEntry = sFactionTemplateStore.LookupEntry(cInfo->faction))
|
|
||||||
{
|
|
||||||
ReputationRank reaction = bot->GetReputationRank(factionEntry->faction);
|
|
||||||
return reaction > REP_NEUTRAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// As a precaution, if the faction is not found, consider inactive
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CreatureTemplate const* RpgTravelDestination::GetCreatureTemplate() { return sObjectMgr->GetCreatureTemplate(entry); }
|
CreatureTemplate const* RpgTravelDestination::GetCreatureTemplate() { return sObjectMgr->GetCreatureTemplate(entry); }
|
||||||
|
|||||||
@ -164,16 +164,15 @@ void PlayerbotFactory::Init()
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(gemId);
|
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(gemId);
|
||||||
if (proto) {
|
|
||||||
if (proto->ItemLevel < 60)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (proto->Flags & ITEM_FLAG_UNIQUE_EQUIPPABLE)
|
if (proto->ItemLevel < 60)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (proto->Flags & ITEM_FLAG_UNIQUE_EQUIPPABLE)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sRandomItemMgr->IsTestItem(gemId))
|
if (sRandomItemMgr->IsTestItem(gemId))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -181,11 +180,9 @@ void PlayerbotFactory::Init()
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LOG_INFO("playerbots", "Add {} to enchantment gems", gemId);
|
// LOG_INFO("playerbots", "Add {} to enchantment gems", gemId);
|
||||||
enchantGemIdCache.push_back(gemId);
|
enchantGemIdCache.push_back(gemId);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO("playerbots", "Loading {} enchantment gems", enchantGemIdCache.size());
|
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(0), 0);
|
||||||
bot->SetUInt32Value(PLAYER_SKILL_INDEX(1), 0);
|
bot->SetUInt32Value(PLAYER_SKILL_INDEX(1), 0);
|
||||||
|
|
||||||
// unlearn default race/class skills
|
// unlearn default race/class skills
|
||||||
if (PlayerInfo const* info = sObjectMgr->GetPlayerInfo(bot->getRace(), bot->getClass())) {
|
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(bot->getRace(), bot->getClass());
|
||||||
for (PlayerCreateInfoSkills::const_iterator itr = info->skills.begin(); itr != info->skills.end(); ++itr)
|
for (PlayerCreateInfoSkills::const_iterator itr = info->skills.begin(); itr != info->skills.end(); ++itr)
|
||||||
{
|
{
|
||||||
uint32 skillId = itr->SkillId;
|
uint32 skillId = itr->SkillId;
|
||||||
if (!bot->HasSkill(skillId))
|
if (!bot->HasSkill(skillId))
|
||||||
continue;
|
continue;
|
||||||
bot->SetSkill(skillId, 0, 0, 0);
|
bot->SetSkill(skillId, 0, 0, 0);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,6 @@
|
|||||||
#include "LastMovementValue.h"
|
#include "LastMovementValue.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "Transport.h"
|
#include "Transport.h"
|
||||||
#include "BotMovementUtils.h"
|
|
||||||
|
|
||||||
bool ReachAreaTriggerAction::Execute(Event event)
|
bool ReachAreaTriggerAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
@ -41,18 +40,7 @@ bool ReachAreaTriggerAction::Execute(Event event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// bot->GetMotionMaster()->MovePoint(at->map, at->x, at->y, at->z);
|
bot->GetMotionMaster()->MovePoint(at->map, at->x, at->y, at->z);
|
||||||
// [Fix: MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full:]
|
|
||||||
if (CanStartMoveSpline(bot))
|
|
||||||
{
|
|
||||||
bot->GetMotionMaster()->MovePoint(at->map, at->x, at->y, at->z);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bot->StopMovingOnCurrentPos();
|
|
||||||
botAI->SetNextCheckDelay(sPlayerbotAIConfig->reactDelay);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
float distance = bot->GetDistance(at->x, at->y, at->z);
|
float distance = bot->GetDistance(at->x, at->y, at->z);
|
||||||
float delay = 1000.0f * distance / bot->GetSpeed(MOVE_RUN) + sPlayerbotAIConfig->reactDelay;
|
float delay = 1000.0f * distance / bot->GetSpeed(MOVE_RUN) + sPlayerbotAIConfig->reactDelay;
|
||||||
|
|||||||
@ -176,8 +176,7 @@ bool BGJoinAction::gatherArenaTeam(ArenaType type)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
memberBotAI->Reset();
|
memberBotAI->Reset();
|
||||||
// member->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), 0);
|
member->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), 0);
|
||||||
TeleportToSafe(member, bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), 0);
|
|
||||||
|
|
||||||
LOG_INFO("playerbots", "Bot {} <{}>: Member of <{}>", member->GetGUID().ToString().c_str(),
|
LOG_INFO("playerbots", "Bot {} <{}>: Member of <{}>", member->GetGUID().ToString().c_str(),
|
||||||
member->GetName().c_str(), arenateam->GetName().c_str());
|
member->GetName().c_str(), arenateam->GetName().c_str());
|
||||||
|
|||||||
@ -4289,11 +4289,9 @@ bool ArenaTactics::moveToCenter(Battleground* bg)
|
|||||||
{
|
{
|
||||||
// they like to hang around at the tip of the pipes doing nothing, so we just teleport them down
|
// they like to hang around at the tip of the pipes doing nothing, so we just teleport them down
|
||||||
if (bot->GetDistance(1333.07f, 817.18f, 13.35f) < 4)
|
if (bot->GetDistance(1333.07f, 817.18f, 13.35f) < 4)
|
||||||
// bot->TeleportTo(bg->GetMapId(), 1330.96f, 816.75f, 3.2f, bot->GetOrientation());
|
bot->TeleportTo(bg->GetMapId(), 1330.96f, 816.75f, 3.2f, bot->GetOrientation());
|
||||||
TeleportToSafe(bot, bg->GetMapId(), 1330.96f, 816.75f, 3.2f, bot->GetOrientation()); // [Fix] Avaid silly teleport
|
|
||||||
if (bot->GetDistance(1250.13f, 764.79f, 13.34f) < 4)
|
if (bot->GetDistance(1250.13f, 764.79f, 13.34f) < 4)
|
||||||
// bot->TeleportTo(bg->GetMapId(), 1252.19f, 765.41f, 3.2f, bot->GetOrientation());
|
bot->TeleportTo(bg->GetMapId(), 1252.19f, 765.41f, 3.2f, bot->GetOrientation());
|
||||||
TeleportToSafe(bot, bg->GetMapId(), 1252.19f, 765.41f, 3.2f, bot->GetOrientation()); // [Fix] Avaid silly teleport
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BATTLEGROUND_RV:
|
case BATTLEGROUND_RV:
|
||||||
|
|||||||
@ -18,7 +18,7 @@ bool MoveToTravelTargetAction::Execute(Event event)
|
|||||||
WorldLocation location = *target->getPosition();
|
WorldLocation location = *target->getPosition();
|
||||||
|
|
||||||
Group* group = bot->GetGroup();
|
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())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
|
|||||||
@ -42,7 +42,6 @@
|
|||||||
#include "Vehicle.h"
|
#include "Vehicle.h"
|
||||||
#include "WaypointMovementGenerator.h"
|
#include "WaypointMovementGenerator.h"
|
||||||
#include "Corpse.h"
|
#include "Corpse.h"
|
||||||
#include "BotMovementUtils.h"
|
|
||||||
|
|
||||||
MovementAction::MovementAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name)
|
MovementAction::MovementAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name)
|
||||||
{
|
{
|
||||||
@ -82,10 +81,6 @@ bool MovementAction::JumpTo(uint32 mapId, float x, float y, float z, MovementPri
|
|||||||
float botZ = bot->GetPositionZ();
|
float botZ = bot->GetPositionZ();
|
||||||
float speed = bot->GetSpeed(MOVE_RUN);
|
float speed = bot->GetSpeed(MOVE_RUN);
|
||||||
MotionMaster& mm = *bot->GetMotionMaster();
|
MotionMaster& mm = *bot->GetMotionMaster();
|
||||||
// [Fix: MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full:]
|
|
||||||
if (!CanStartMoveSpline(bot))
|
|
||||||
return false;
|
|
||||||
// End Fix
|
|
||||||
mm.Clear();
|
mm.Clear();
|
||||||
mm.MoveJump(x, y, z, speed, speed, 1);
|
mm.MoveJump(x, y, z, speed, speed, 1);
|
||||||
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), 1000, priority);
|
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), 1000, priority);
|
||||||
@ -212,10 +207,6 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
|
|||||||
if (distance > 0.01f)
|
if (distance > 0.01f)
|
||||||
{
|
{
|
||||||
MotionMaster& mm = *vehicleBase->GetMotionMaster(); // need to move vehicle, not bot
|
MotionMaster& mm = *vehicleBase->GetMotionMaster(); // need to move vehicle, not bot
|
||||||
// [Fix: MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full:]
|
|
||||||
if (!CanStartMoveSpline(bot))
|
|
||||||
return false;
|
|
||||||
// End Fix
|
|
||||||
mm.Clear();
|
mm.Clear();
|
||||||
if (!backwards)
|
if (!backwards)
|
||||||
{
|
{
|
||||||
@ -251,10 +242,6 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
|
|||||||
// botAI->InterruptSpell();
|
// botAI->InterruptSpell();
|
||||||
// }
|
// }
|
||||||
MotionMaster& mm = *bot->GetMotionMaster();
|
MotionMaster& mm = *bot->GetMotionMaster();
|
||||||
//[Fix: MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full:]
|
|
||||||
if (!CanStartMoveSpline(bot))
|
|
||||||
return false;
|
|
||||||
// End Fix
|
|
||||||
mm.Clear();
|
mm.Clear();
|
||||||
if (!backwards)
|
if (!backwards)
|
||||||
{
|
{
|
||||||
@ -297,10 +284,6 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
|
|||||||
// }
|
// }
|
||||||
MotionMaster& mm = *bot->GetMotionMaster();
|
MotionMaster& mm = *bot->GetMotionMaster();
|
||||||
G3D::Vector3 endP = path.back();
|
G3D::Vector3 endP = path.back();
|
||||||
// [Fix: MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full:]
|
|
||||||
if (!CanStartMoveSpline(bot))
|
|
||||||
return false;
|
|
||||||
// End Fix
|
|
||||||
mm.Clear();
|
mm.Clear();
|
||||||
if (!backwards)
|
if (!backwards)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -147,8 +147,7 @@ bool AutoReleaseSpiritAction::HandleBattlegroundSpiritHealer()
|
|||||||
// and in IOC it's not within clicking range when they res in own base
|
// and in IOC it's not within clicking range when they res in own base
|
||||||
|
|
||||||
// Teleport to nearest friendly Spirit Healer when not currently in range of one.
|
// Teleport to nearest friendly Spirit Healer when not currently in range of one.
|
||||||
// bot->TeleportTo(bot->GetMapId(), spiritHealer->GetPositionX(), spiritHealer->GetPositionY(), spiritHealer->GetPositionZ(), 0.f);
|
bot->TeleportTo(bot->GetMapId(), spiritHealer->GetPositionX(), spiritHealer->GetPositionY(), spiritHealer->GetPositionZ(), 0.f);
|
||||||
TeleportToSafe(bot, bot->GetMapId(), spiritHealer->GetPositionX(), spiritHealer->GetPositionY(), spiritHealer->GetPositionZ(), 0.f); // [Fix] Avoid silly teleport
|
|
||||||
RESET_AI_VALUE(bool, "combat::self target");
|
RESET_AI_VALUE(bool, "combat::self target");
|
||||||
RESET_AI_VALUE(WorldPosition, "current position");
|
RESET_AI_VALUE(WorldPosition, "current position");
|
||||||
}
|
}
|
||||||
@ -245,8 +244,7 @@ int64 RepopAction::CalculateDeadTime() const
|
|||||||
|
|
||||||
void RepopAction::PerformGraveyardTeleport(const GraveyardStruct* graveyard) const
|
void RepopAction::PerformGraveyardTeleport(const GraveyardStruct* graveyard) const
|
||||||
{
|
{
|
||||||
// bot->TeleportTo(graveyard->Map, graveyard->x, graveyard->y, graveyard->z, 0.f);
|
bot->TeleportTo(graveyard->Map, graveyard->x, graveyard->y, graveyard->z, 0.f);
|
||||||
TeleportToSafe(bot, graveyard->Map, graveyard->x, graveyard->y, graveyard->z, 0.f); // [Fix] Avoid Silly teleport
|
|
||||||
RESET_AI_VALUE(bool, "combat::self target");
|
RESET_AI_VALUE(bool, "combat::self target");
|
||||||
RESET_AI_VALUE(WorldPosition, "current position");
|
RESET_AI_VALUE(WorldPosition, "current position");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -169,8 +169,7 @@ bool FindCorpseAction::Execute(Event event)
|
|||||||
if (deadTime > delay)
|
if (deadTime > delay)
|
||||||
{
|
{
|
||||||
bot->GetMotionMaster()->Clear();
|
bot->GetMotionMaster()->Clear();
|
||||||
// bot->TeleportTo(moveToPos.getMapId(), moveToPos.getX(), moveToPos.getY(), moveToPos.getZ(), 0);
|
bot->TeleportTo(moveToPos.getMapId(), moveToPos.getX(), moveToPos.getY(), moveToPos.getZ(), 0);
|
||||||
TeleportToSafe(bot, moveToPos.getMapId(), moveToPos.getX(), moveToPos.getY(), moveToPos.getZ(), 0); // [fix] Avoid Silly Teleport
|
|
||||||
}
|
}
|
||||||
|
|
||||||
moved = true;
|
moved = true;
|
||||||
@ -351,8 +350,7 @@ bool SpiritHealerAction::Execute(Event event)
|
|||||||
// if (!botAI->HasActivePlayerMaster())
|
// if (!botAI->HasActivePlayerMaster())
|
||||||
// {
|
// {
|
||||||
context->GetValue<uint32>("death count")->Set(dCount + 1);
|
context->GetValue<uint32>("death count")->Set(dCount + 1);
|
||||||
// return bot->TeleportTo(ClosestGrave->Map, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, 0.f);
|
return bot->TeleportTo(ClosestGrave->Map, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, 0.f);
|
||||||
return TeleportToSafe(bot, ClosestGrave->Map, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, 0.f); // [Fix] Avoid Silly teleport
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// LOG_INFO("playerbots", "Bot {} {}:{} <{}> can't find a spirit healer", bot->GetGUID().ToString().c_str(),
|
// LOG_INFO("playerbots", "Bot {} {}:{} <{}> can't find a spirit healer", bot->GetGUID().ToString().c_str(),
|
||||||
|
|||||||
@ -957,8 +957,7 @@ bool IccGunshipTeleportHordeAction::Execute(Event event)
|
|||||||
|
|
||||||
bool IccGunshipTeleportHordeAction::TeleportTo(const Position& position)
|
bool IccGunshipTeleportHordeAction::TeleportTo(const Position& position)
|
||||||
{
|
{
|
||||||
// return bot->TeleportTo(bot->GetMapId(), position.GetPositionX(), position.GetPositionY(), position.GetPositionZ(),
|
return bot->TeleportTo(bot->GetMapId(), position.GetPositionX(), position.GetPositionY(), position.GetPositionZ(),
|
||||||
return TeleportToSafe(bot, bot->GetMapId(), position.GetPositionX(), position.GetPositionY(), position.GetPositionZ(),// [Fix]Avoid silly teleport
|
|
||||||
bot->GetOrientation());
|
bot->GetOrientation());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,6 @@
|
|||||||
#include "RaidNaxxStrategy.h"
|
#include "RaidNaxxStrategy.h"
|
||||||
#include "ScriptedCreature.h"
|
#include "ScriptedCreature.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
#include "BotMovementUtils.h"
|
|
||||||
|
|
||||||
bool GrobbulusGoBehindAction::Execute(Event event)
|
bool GrobbulusGoBehindAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
@ -259,26 +258,11 @@ bool RazuviousUseObedienceCrystalAction::Execute(Event event)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (charm->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == NULL_MOTION_TYPE)
|
if (charm->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == NULL_MOTION_TYPE)
|
||||||
/*{
|
{
|
||||||
charm->GetMotionMaster()->Clear();
|
charm->GetMotionMaster()->Clear();
|
||||||
charm->GetMotionMaster()->MoveChase(target);
|
charm->GetMotionMaster()->MoveChase(target);
|
||||||
charm->GetAI()->AttackStart(target);
|
|
||||||
}*/
|
|
||||||
// [Fix: MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full:]
|
|
||||||
{
|
|
||||||
if (CanStartMoveSpline(charm))
|
|
||||||
{
|
|
||||||
charm->GetMotionMaster()->Clear();
|
|
||||||
charm->GetMotionMaster()->MoveChase(target);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
charm->StopMoving();
|
|
||||||
}
|
|
||||||
|
|
||||||
charm->GetAI()->AttackStart(target);
|
charm->GetAI()->AttackStart(target);
|
||||||
}
|
}
|
||||||
// End Fix
|
|
||||||
Aura* forceObedience = botAI->GetAura("force obedience", charm);
|
Aura* forceObedience = botAI->GetAura("force obedience", charm);
|
||||||
uint32 duration_time;
|
uint32 duration_time;
|
||||||
if (!forceObedience)
|
if (!forceObedience)
|
||||||
|
|||||||
@ -1357,14 +1357,10 @@ bool KologarnMarkDpsTargetAction::Execute(Event event)
|
|||||||
|
|
||||||
bool KologarnFallFromFloorAction::Execute(Event event)
|
bool KologarnFallFromFloorAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
/*return bot->TeleportTo(bot->GetMapId(), ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionX(),
|
return bot->TeleportTo(bot->GetMapId(), ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionX(),
|
||||||
ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionY(),
|
ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionY(),
|
||||||
ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionZ(),
|
ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionZ(),
|
||||||
ULDUAR_KOLOGARN_RESTORE_POSITION.GetOrientation());*/
|
ULDUAR_KOLOGARN_RESTORE_POSITION.GetOrientation());
|
||||||
return TeleportToSafe(bot, bot->GetMapId(), ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionX(), // [Fix] Avoid silly teleport
|
|
||||||
ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionY(),
|
|
||||||
ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionZ(),
|
|
||||||
ULDUAR_KOLOGARN_RESTORE_POSITION.GetOrientation());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KologarnFallFromFloorAction::isUseful()
|
bool KologarnFallFromFloorAction::isUseful()
|
||||||
@ -1411,18 +1407,14 @@ bool KologarnEyebeamAction::Execute(Event event)
|
|||||||
KologarnEyebeamTrigger kologarnEyebeamTrigger(botAI);
|
KologarnEyebeamTrigger kologarnEyebeamTrigger(botAI);
|
||||||
if (runToLeftSide)
|
if (runToLeftSide)
|
||||||
{
|
{
|
||||||
// teleportedToPoint = bot->TeleportTo(bot->GetMapId(), ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION.GetPositionX(),
|
teleportedToPoint = bot->TeleportTo(bot->GetMapId(), ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION.GetPositionX(),
|
||||||
teleportedToPoint = TeleportToSafe(bot, bot->GetMapId(),
|
|
||||||
ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION.GetPositionX(),
|
|
||||||
ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION.GetPositionY(),
|
ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION.GetPositionY(),
|
||||||
ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION.GetPositionZ(),
|
ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION.GetPositionZ(),
|
||||||
ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION.GetOrientation());
|
ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION.GetOrientation());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// teleportedToPoint = bot->TeleportTo(bot->GetMapId(), ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION.GetPositionX(),
|
teleportedToPoint = bot->TeleportTo(bot->GetMapId(), ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION.GetPositionX(),
|
||||||
teleportedToPoint = TeleportToSafe(bot, bot->GetMapId(),
|
|
||||||
ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION.GetPositionX(),
|
|
||||||
ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION.GetPositionY(),
|
ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION.GetPositionY(),
|
||||||
ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION.GetPositionZ(),
|
ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION.GetPositionZ(),
|
||||||
ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION.GetOrientation());
|
ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION.GetOrientation());
|
||||||
|
|||||||
@ -175,13 +175,9 @@ bool EmalonOverchargeAction::isUseful()
|
|||||||
|
|
||||||
bool EmalonFallFromFloorAction::Execute(Event event)
|
bool EmalonFallFromFloorAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
/*return bot->TeleportTo(bot->GetMapId(), VOA_EMALON_RESTORE_POSITION.GetPositionX(),
|
return bot->TeleportTo(bot->GetMapId(), VOA_EMALON_RESTORE_POSITION.GetPositionX(),
|
||||||
VOA_EMALON_RESTORE_POSITION.GetPositionY(), VOA_EMALON_RESTORE_POSITION.GetPositionZ(),
|
VOA_EMALON_RESTORE_POSITION.GetPositionY(), VOA_EMALON_RESTORE_POSITION.GetPositionZ(),
|
||||||
VOA_EMALON_RESTORE_POSITION.GetOrientation());*/
|
VOA_EMALON_RESTORE_POSITION.GetOrientation());
|
||||||
return TeleportToSafe(bot, bot->GetMapId(), VOA_EMALON_RESTORE_POSITION.GetPositionX(), //[Fix] Avoid Silly Teleport
|
|
||||||
VOA_EMALON_RESTORE_POSITION.GetPositionY(),
|
|
||||||
VOA_EMALON_RESTORE_POSITION.GetPositionZ(),
|
|
||||||
VOA_EMALON_RESTORE_POSITION.GetOrientation());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EmalonFallFromFloorAction::isUseful()
|
bool EmalonFallFromFloorAction::isUseful()
|
||||||
|
|||||||
@ -67,8 +67,7 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest)
|
|||||||
bot->GetName(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId(),
|
bot->GetName(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId(),
|
||||||
dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), dest.getMapId(), bot->GetZoneId(),
|
dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), dest.getMapId(), bot->GetZoneId(),
|
||||||
zone_name);
|
zone_name);
|
||||||
// return bot->TeleportTo(dest);
|
return bot->TeleportTo(dest);
|
||||||
return TeleportToSafe(bot, dest); //[Fix] Avoid Silly teleport
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float dis = bot->GetExactDist(dest);
|
float dis = bot->GetExactDist(dest);
|
||||||
|
|||||||
@ -14,7 +14,7 @@ class FindTargetForTankStrategy : public FindNonCcTargetStrategy
|
|||||||
public:
|
public:
|
||||||
FindTargetForTankStrategy(PlayerbotAI* botAI) : FindNonCcTargetStrategy(botAI), minThreat(0) {}
|
FindTargetForTankStrategy(PlayerbotAI* botAI) : FindNonCcTargetStrategy(botAI), minThreat(0) {}
|
||||||
|
|
||||||
/*void CheckAttacker(Unit* creature, ThreatMgr* threatMgr) override
|
void CheckAttacker(Unit* creature, ThreatMgr* threatMgr) override
|
||||||
{
|
{
|
||||||
if (!creature || !creature->IsAlive())
|
if (!creature || !creature->IsAlive())
|
||||||
{
|
{
|
||||||
@ -37,43 +37,6 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (minThreat >= threat)
|
|
||||||
{
|
|
||||||
minThreat = threat;
|
|
||||||
result = creature;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
void CheckAttacker(Unit* creature, ThreatMgr* threatMgr) override
|
|
||||||
{
|
|
||||||
// [Crash fix] Filter out anything that is not ready/valid
|
|
||||||
if (!creature || !creature->IsAlive() || !creature->IsInWorld() || creature->IsDuringRemoveFromWorld())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!threatMgr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Player* bot = botAI->GetBot();
|
|
||||||
if (!bot)
|
|
||||||
return;
|
|
||||||
|
|
||||||
float threat = threatMgr->GetThreat(bot);
|
|
||||||
|
|
||||||
if (!result || !result->IsAlive() || !result->IsInWorld() || result->IsDuringRemoveFromWorld())
|
|
||||||
{
|
|
||||||
// [Crash fix] If the previous target has become invalid, restart cleanly
|
|
||||||
minThreat = threat;
|
|
||||||
result = creature;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Neglect si la victime actuelle est le MT (ou s'il n'y a pas de victime)
|
|
||||||
if (HostileReference* cv = threatMgr->getCurrentVictim())
|
|
||||||
{
|
|
||||||
Unit* victim = cv->getTarget();
|
|
||||||
if (victim && victim->ToPlayer() && botAI->IsMainTank(victim->ToPlayer()))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minThreat >= threat)
|
if (minThreat >= threat)
|
||||||
{
|
{
|
||||||
minThreat = threat;
|
minThreat = threat;
|
||||||
@ -90,7 +53,7 @@ class FindTankTargetSmartStrategy : public FindTargetStrategy
|
|||||||
public:
|
public:
|
||||||
FindTankTargetSmartStrategy(PlayerbotAI* botAI) : FindTargetStrategy(botAI) {}
|
FindTankTargetSmartStrategy(PlayerbotAI* botAI) : FindTargetStrategy(botAI) {}
|
||||||
|
|
||||||
/*void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override
|
void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override
|
||||||
{
|
{
|
||||||
if (Group* group = botAI->GetBot()->GetGroup())
|
if (Group* group = botAI->GetBot()->GetGroup())
|
||||||
{
|
{
|
||||||
@ -106,32 +69,8 @@ public:
|
|||||||
{
|
{
|
||||||
result = attacker;
|
result = attacker;
|
||||||
}
|
}
|
||||||
}*/
|
|
||||||
void CheckAttacker(Unit* attacker, ThreatMgr* /*threatMgr*/) override
|
|
||||||
{
|
|
||||||
// [Crash fix] Protect against null/out-of-world/being-removed units
|
|
||||||
if (!attacker || !attacker->IsAlive() || !attacker->IsInWorld() || attacker->IsDuringRemoveFromWorld())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (Player* me = botAI->GetBot())
|
|
||||||
{
|
|
||||||
if (Group* group = me->GetGroup())
|
|
||||||
{
|
|
||||||
ObjectGuid guid = group->GetTargetIcon(4);
|
|
||||||
if (guid && attacker->GetGUID() == guid)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// [Crash fix] If 'result' has become invalid, forget it
|
|
||||||
if (result && (!result->IsAlive() || !result->IsInWorld() || result->IsDuringRemoveFromWorld()))
|
|
||||||
result = nullptr;
|
|
||||||
|
|
||||||
if (!result || IsBetter(attacker, result))
|
|
||||||
result = attacker;
|
|
||||||
}
|
}
|
||||||
|
bool IsBetter(Unit* new_unit, Unit* old_unit)
|
||||||
/*bool IsBetter(Unit* new_unit, Unit* old_unit)
|
|
||||||
{
|
{
|
||||||
Player* bot = botAI->GetBot();
|
Player* bot = botAI->GetBot();
|
||||||
// if group has multiple tanks, main tank just focus on the current target
|
// if group has multiple tanks, main tank just focus on the current target
|
||||||
@ -158,47 +97,8 @@ public:
|
|||||||
return new_dis < old_dis;
|
return new_dis < old_dis;
|
||||||
}
|
}
|
||||||
return new_threat < old_threat;
|
return new_threat < old_threat;
|
||||||
}*/
|
|
||||||
bool IsBetter(Unit* new_unit, Unit* old_unit)
|
|
||||||
{
|
|
||||||
// [Crash fix] If either one is invalid, decide straight away
|
|
||||||
if (!new_unit || !new_unit->IsAlive() || !new_unit->IsInWorld() || new_unit->IsDuringRemoveFromWorld())
|
|
||||||
return false;
|
|
||||||
if (!old_unit || !old_unit->IsAlive() || !old_unit->IsInWorld() || old_unit->IsDuringRemoveFromWorld())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
Player* bot = botAI->GetBot();
|
|
||||||
if (!bot)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// if multiple tanks, logically focus on the current target
|
|
||||||
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("current target")->Get();
|
|
||||||
if (currentTarget && botAI->IsMainTank(bot) && botAI->GetGroupTankNum(bot) > 1)
|
|
||||||
{
|
|
||||||
if (old_unit == currentTarget)
|
|
||||||
return false;
|
|
||||||
if (new_unit == currentTarget)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
float new_threat = new_unit->GetThreatMgr().GetThreat(bot);
|
|
||||||
float old_threat = old_unit->GetThreatMgr().GetThreat(bot);
|
|
||||||
float new_dis = bot->GetDistance(new_unit);
|
|
||||||
float old_dis = bot->GetDistance(old_unit);
|
|
||||||
|
|
||||||
// hasAggro? -> withinMelee? -> threat
|
|
||||||
int nl = GetIntervalLevel(new_unit);
|
|
||||||
int ol = GetIntervalLevel(old_unit);
|
|
||||||
if (nl != ol)
|
|
||||||
return nl > ol;
|
|
||||||
|
|
||||||
if (nl == 2)
|
|
||||||
return new_dis < old_dis;
|
|
||||||
|
|
||||||
return new_threat < old_threat;
|
|
||||||
}
|
}
|
||||||
|
int32_t GetIntervalLevel(Unit* unit)
|
||||||
/*int32_t GetIntervalLevel(Unit* unit)
|
|
||||||
{
|
{
|
||||||
if (!botAI->HasAggro(unit))
|
if (!botAI->HasAggro(unit))
|
||||||
{
|
{
|
||||||
@ -209,28 +109,12 @@ public:
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}*/
|
|
||||||
int32_t GetIntervalLevel(Unit* unit)
|
|
||||||
{
|
|
||||||
// [Crash fix] Basic guards
|
|
||||||
if (!unit || !unit->IsAlive() || !unit->IsInWorld() || unit->IsDuringRemoveFromWorld())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!botAI->HasAggro(unit))
|
|
||||||
return 2;
|
|
||||||
|
|
||||||
if (Player* bot = botAI->GetBot())
|
|
||||||
{
|
|
||||||
if (bot->IsWithinMeleeRange(unit))
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Unit* TankTargetValue::Calculate()
|
Unit* TankTargetValue::Calculate()
|
||||||
{
|
{
|
||||||
// [Note] Using the "smart" strategy below. Guards have been added in CheckAttacker/IsBetter.
|
// FindTargetForTankStrategy strategy(botAI);
|
||||||
FindTankTargetSmartStrategy strategy(botAI);
|
FindTankTargetSmartStrategy strategy(botAI);
|
||||||
return FindTarget(&strategy);
|
return FindTarget(&strategy);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
Unit* FindTargetStrategy::GetResult() { return result; }
|
Unit* FindTargetStrategy::GetResult() { return result; }
|
||||||
|
|
||||||
/*Unit* TargetValue::FindTarget(FindTargetStrategy* strategy)
|
Unit* TargetValue::FindTarget(FindTargetStrategy* strategy)
|
||||||
{
|
{
|
||||||
GuidVector attackers = botAI->GetAiObjectContext()->GetValue<GuidVector>("attackers")->Get();
|
GuidVector attackers = botAI->GetAiObjectContext()->GetValue<GuidVector>("attackers")->Get();
|
||||||
for (ObjectGuid const guid : attackers)
|
for (ObjectGuid const guid : attackers)
|
||||||
@ -27,28 +27,6 @@ Unit* FindTargetStrategy::GetResult() { return result; }
|
|||||||
strategy->CheckAttacker(unit, &ThreatMgr);
|
strategy->CheckAttacker(unit, &ThreatMgr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return strategy->GetResult();
|
|
||||||
}*/
|
|
||||||
|
|
||||||
Unit* TargetValue::FindTarget(FindTargetStrategy* strategy)
|
|
||||||
{
|
|
||||||
// [Crash fix] The very first AI tick can occur before everything is "in world".
|
|
||||||
// Filter out units that are non-living / being removed / out of world.
|
|
||||||
AiObjectContext* ctx = botAI->GetAiObjectContext();
|
|
||||||
if (!ctx)
|
|
||||||
return strategy->GetResult();
|
|
||||||
|
|
||||||
GuidVector attackers = ctx->GetValue<GuidVector>("attackers")->Get();
|
|
||||||
for (ObjectGuid const& guid : attackers)
|
|
||||||
{
|
|
||||||
Unit* unit = botAI->GetUnit(guid);
|
|
||||||
if (!unit || !unit->IsAlive() || !unit->IsInWorld() || unit->IsDuringRemoveFromWorld())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ThreatMgr& threatMgrRef = unit->GetThreatMgr();
|
|
||||||
strategy->CheckAttacker(unit, &threatMgrRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
return strategy->GetResult();
|
return strategy->GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user