mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-13 09:07:19 +00:00
606 lines
22 KiB
C++
606 lines
22 KiB
C++
/*
|
|
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Affero General Public License as published by the
|
|
* Free Software Foundation; either version 3 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "Playerbots.h"
|
|
|
|
#include "Channel.h"
|
|
#include "Config.h"
|
|
#include "DatabaseEnv.h"
|
|
#include "DatabaseLoader.h"
|
|
#include "GuildTaskMgr.h"
|
|
#include "Metric.h"
|
|
#include "PlayerScript.h"
|
|
#include "PlayerbotAIConfig.h"
|
|
#include "RandomPlayerbotMgr.h"
|
|
#include "ScriptMgr.h"
|
|
#include "cs_playerbots.h"
|
|
#include "cmath"
|
|
#include "BattleGroundTactics.h"
|
|
#include "ObjectAccessor.h"
|
|
|
|
class PlayerbotsDatabaseScript : public DatabaseScript
|
|
{
|
|
public:
|
|
PlayerbotsDatabaseScript() : DatabaseScript("PlayerbotsDatabaseScript") {}
|
|
|
|
bool OnDatabasesLoading() override
|
|
{
|
|
DatabaseLoader playerbotLoader("server.playerbots");
|
|
playerbotLoader.SetUpdateFlags(sConfigMgr->GetOption<bool>("Playerbots.Updates.EnableDatabases", true)
|
|
? DatabaseLoader::DATABASE_PLAYERBOTS
|
|
: 0);
|
|
playerbotLoader.AddDatabase(PlayerbotsDatabase, "Playerbots");
|
|
|
|
return playerbotLoader.Load();
|
|
}
|
|
|
|
void OnDatabasesKeepAlive() override { PlayerbotsDatabase.KeepAlive(); }
|
|
|
|
void OnDatabasesClosing() override { PlayerbotsDatabase.Close(); }
|
|
|
|
void OnDatabaseWarnAboutSyncQueries(bool apply) override { PlayerbotsDatabase.WarnAboutSyncQueries(apply); }
|
|
|
|
void OnDatabaseSelectIndexLogout(Player* player, uint32& statementIndex, uint32& statementParam) override
|
|
{
|
|
statementIndex = CHAR_UPD_CHAR_OFFLINE;
|
|
statementParam = player->GetGUID().GetCounter();
|
|
}
|
|
|
|
void OnDatabaseGetDBRevision(std::string& revision) override
|
|
{
|
|
if (QueryResult resultPlayerbot =
|
|
PlayerbotsDatabase.Query("SELECT date FROM version_db_playerbots ORDER BY date DESC LIMIT 1"))
|
|
{
|
|
Field* fields = resultPlayerbot->Fetch();
|
|
revision = fields[0].Get<std::string>();
|
|
}
|
|
|
|
if (revision.empty())
|
|
{
|
|
revision = "Unknown Playerbots Database Revision";
|
|
}
|
|
}
|
|
};
|
|
|
|
class PlayerbotsPlayerScript : public PlayerScript
|
|
{
|
|
public:
|
|
PlayerbotsPlayerScript() : PlayerScript("PlayerbotsPlayerScript", {
|
|
PLAYERHOOK_ON_LOGIN,
|
|
PLAYERHOOK_ON_AFTER_UPDATE,
|
|
PLAYERHOOK_ON_CHAT,
|
|
PLAYERHOOK_ON_CHAT_WITH_CHANNEL,
|
|
PLAYERHOOK_ON_CHAT_WITH_GROUP,
|
|
PLAYERHOOK_ON_BEFORE_CRITERIA_PROGRESS,
|
|
PLAYERHOOK_ON_BEFORE_ACHI_COMPLETE,
|
|
PLAYERHOOK_CAN_PLAYER_USE_PRIVATE_CHAT,
|
|
PLAYERHOOK_ON_GIVE_EXP
|
|
}) {}
|
|
|
|
void OnPlayerLogin(Player* player) override
|
|
{
|
|
if (!player->GetSession()->IsBot())
|
|
{
|
|
sPlayerbotsMgr->AddPlayerbotData(player, false);
|
|
sRandomPlayerbotMgr->OnPlayerLogin(player);
|
|
|
|
// Before modifying the following messages, please make sure it does not violate the AGPLv3.0 license
|
|
// especially if you are distributing a repack or hosting a public server
|
|
// e.g. you can replace the URL with your own repository,
|
|
// but it should be publicly accessible and include all modifications you've made
|
|
if (sPlayerbotAIConfig->enabled)
|
|
{
|
|
ChatHandler(player->GetSession()).SendSysMessage(
|
|
"|cff00ff00This server runs with |cff00ccffmod-playerbots|r "
|
|
"|cffcccccchttps://github.com/liyunfan1223/mod-playerbots|r");
|
|
}
|
|
|
|
/*if (sPlayerbotAIConfig->enabled || sPlayerbotAIConfig->randomBotAutologin)
|
|
{
|
|
std::string roundedTime =
|
|
std::to_string(std::ceil((sPlayerbotAIConfig->maxRandomBots * 0.11 / 60) * 10) / 10.0);
|
|
roundedTime = roundedTime.substr(0, roundedTime.find('.') + 2);
|
|
|
|
ChatHandler(player->GetSession()).SendSysMessage(
|
|
"|cff00ff00Playerbots:|r bot initialization at server startup takes about '"
|
|
+ roundedTime + "' minutes.");
|
|
}*/
|
|
}
|
|
}
|
|
|
|
void OnPlayerAfterUpdate(Player* player, uint32 diff) override
|
|
{
|
|
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player))
|
|
{
|
|
botAI->UpdateAI(diff);
|
|
}
|
|
|
|
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
|
|
{
|
|
playerbotMgr->UpdateAI(diff);
|
|
}
|
|
}
|
|
|
|
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Player* receiver) override
|
|
{
|
|
/*if (type == CHAT_MSG_WHISPER)
|
|
{
|
|
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(receiver))
|
|
{
|
|
botAI->HandleCommand(type, msg, player);
|
|
|
|
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;
|
|
}
|
|
|
|
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override
|
|
{
|
|
/*for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
|
{
|
|
if (Player* member = itr->GetSource())
|
|
{
|
|
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(member))
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg) override
|
|
{
|
|
if (type == CHAT_MSG_GUILD)
|
|
{
|
|
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
|
|
{
|
|
for (PlayerBotMap::const_iterator it = playerbotMgr->GetPlayerBotsBegin();
|
|
it != playerbotMgr->GetPlayerBotsEnd(); ++it)
|
|
{
|
|
if (Player* const bot = it->second)
|
|
{
|
|
if (bot->GetGuildId() == player->GetGuildId())
|
|
{
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Channel* channel) override
|
|
{
|
|
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
|
|
{
|
|
if (channel->GetFlags() & 0x18)
|
|
{
|
|
playerbotMgr->HandleCommand(type, msg);
|
|
}
|
|
}
|
|
|
|
sRandomPlayerbotMgr->HandleCommand(type, msg, player);
|
|
}
|
|
|
|
bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* achievement) override
|
|
{
|
|
if (sRandomPlayerbotMgr->IsRandomBot(player) && (achievement->flags == 256 || achievement->flags == 768))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void OnPlayerGiveXP(Player* player, uint32& amount, Unit* /*victim*/, uint8 /*xpSource*/) override
|
|
{
|
|
// early return
|
|
if (sPlayerbotAIConfig->randomBotXPRate == 1.0 || !player)
|
|
return;
|
|
|
|
// no XP multiplier, when player is no bot.
|
|
if (!player->GetSession()->IsBot() || !sRandomPlayerbotMgr->IsRandomBot(player))
|
|
return;
|
|
|
|
// no XP multiplier, when bot has group where leader is a real player.
|
|
if (Group* group = player->GetGroup())
|
|
{
|
|
Player* leader = group->GetLeader();
|
|
if (leader && leader != player)
|
|
{
|
|
if (PlayerbotAI* leaderBotAI = GET_PLAYERBOT_AI(leader))
|
|
{
|
|
if (leaderBotAI->HasRealPlayerMaster())
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// otherwise apply bot XP multiplier.
|
|
amount = static_cast<uint32>(std::round(static_cast<float>(amount) * sPlayerbotAIConfig->randomBotXPRate));
|
|
}
|
|
};
|
|
|
|
class PlayerbotsMiscScript : public MiscScript
|
|
{
|
|
public:
|
|
PlayerbotsMiscScript() : MiscScript("PlayerbotsMiscScript", {MISCHOOK_ON_DESTRUCT_PLAYER}) {}
|
|
|
|
void OnDestructPlayer(Player* player) override
|
|
{
|
|
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player))
|
|
{
|
|
delete botAI;
|
|
}
|
|
|
|
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
|
|
{
|
|
delete playerbotMgr;
|
|
}
|
|
}
|
|
};
|
|
|
|
class PlayerbotsServerScript : public ServerScript
|
|
{
|
|
public:
|
|
PlayerbotsServerScript() : ServerScript("PlayerbotsServerScript", {
|
|
SERVERHOOK_CAN_PACKET_RECEIVE
|
|
}) {}
|
|
|
|
void OnPacketReceived(WorldSession* session, WorldPacket const& packet) override
|
|
{
|
|
if (Player* player = session->GetPlayer())
|
|
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
|
|
playerbotMgr->HandleMasterIncomingPacket(packet);
|
|
}
|
|
};
|
|
|
|
class PlayerbotsWorldScript : public WorldScript
|
|
{
|
|
public:
|
|
PlayerbotsWorldScript() : WorldScript("PlayerbotsWorldScript", {
|
|
WORLDHOOK_ON_BEFORE_WORLD_INITIALIZED
|
|
}) {}
|
|
|
|
void OnBeforeWorldInitialized() override
|
|
{
|
|
// Before modifying the following messages, please make sure it does not violate the AGPLv3.0 license
|
|
// especially if you are distributing a repack or hosting a public server
|
|
// e.g. you can replace the URL with your own repository,
|
|
// but it should be publicly accessible and include all modifications you've made
|
|
LOG_INFO("server.loading", "╔══════════════════════════════════════════════════════════╗");
|
|
LOG_INFO("server.loading", "║ ║");
|
|
LOG_INFO("server.loading", "║ AzerothCore Playerbots Module ║");
|
|
LOG_INFO("server.loading", "║ ║");
|
|
LOG_INFO("server.loading", "╟──────────────────────────────────────────────────────────╢");
|
|
LOG_INFO("server.loading", "║ mod-playerbots is a community-driven open-source ║");
|
|
LOG_INFO("server.loading", "║ project based on AzerothCore, licensed under AGPLv3.0 ║");
|
|
LOG_INFO("server.loading", "╟──────────────────────────────────────────────────────────╢");
|
|
LOG_INFO("server.loading", "║ https://github.com/liyunfan1223/mod-playerbots ║");
|
|
LOG_INFO("server.loading", "╚══════════════════════════════════════════════════════════╝");
|
|
|
|
uint32 oldMSTime = getMSTime();
|
|
|
|
LOG_INFO("server.loading", " ");
|
|
LOG_INFO("server.loading", "Load Playerbots Config...");
|
|
|
|
sPlayerbotAIConfig->Initialize();
|
|
|
|
LOG_INFO("server.loading", ">> Loaded playerbots config in {} ms", GetMSTimeDiffToNow(oldMSTime));
|
|
LOG_INFO("server.loading", " ");
|
|
}
|
|
};
|
|
|
|
class PlayerbotsScript : public PlayerbotScript
|
|
{
|
|
public:
|
|
PlayerbotsScript() : PlayerbotScript("PlayerbotsScript") {}
|
|
|
|
/*bool OnPlayerbotCheckLFGQueue(lfg::Lfg5Guids const& guidsList) override
|
|
{
|
|
bool nonBotFound = false;
|
|
for (ObjectGuid const& guid : guidsList.guids)
|
|
{
|
|
Player* player = ObjectAccessor::FindPlayer(guid);
|
|
if (guid.IsGroup() || (player && !GET_PLAYERBOT_AI(player)))
|
|
{
|
|
nonBotFound = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
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
|
|
{
|
|
if (player)
|
|
sGuildTaskMgr->CheckKillTask(player, victim);
|
|
}
|
|
|
|
void OnPlayerbotCheckPetitionAccount(Player* player, bool& found) override
|
|
{
|
|
if (found && GET_PLAYERBOT_AI(player))
|
|
found = false;
|
|
}
|
|
|
|
bool OnPlayerbotCheckUpdatesToSend(Player* player) override
|
|
{
|
|
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player))
|
|
return botAI->IsRealPlayer();
|
|
|
|
return true;
|
|
}
|
|
|
|
void OnPlayerbotPacketSent(Player* player, WorldPacket const* packet) override
|
|
{
|
|
if (!player)
|
|
return;
|
|
|
|
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player))
|
|
{
|
|
botAI->HandleBotOutgoingPacket(*packet);
|
|
}
|
|
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
|
|
{
|
|
playerbotMgr->HandleMasterOutgoingPacket(*packet);
|
|
}
|
|
}
|
|
|
|
void OnPlayerbotUpdate(uint32 diff) override
|
|
{
|
|
sRandomPlayerbotMgr->UpdateAI(diff);
|
|
sRandomPlayerbotMgr->UpdateSessions();
|
|
}
|
|
|
|
void OnPlayerbotUpdateSessions(Player* player) override
|
|
{
|
|
if (player)
|
|
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
|
|
playerbotMgr->UpdateSessions();
|
|
}
|
|
|
|
void OnPlayerbotLogout(Player* player) override
|
|
{
|
|
// immediate purge of the bot's AI upon disconnection
|
|
if (player && player->GetSession()->IsBot())
|
|
sPlayerbotsMgr->RemovePlayerbotAI(player->GetGUID()); // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
|
|
|
|
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
|
|
{
|
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
|
|
if (!botAI || botAI->IsRealPlayer())
|
|
{
|
|
playerbotMgr->LogoutAllBots();
|
|
}
|
|
}
|
|
|
|
sRandomPlayerbotMgr->OnPlayerLogout(player);
|
|
}
|
|
|
|
void OnPlayerbotLogoutBots() override
|
|
{
|
|
LOG_INFO("playerbots", "Logging out all bots...");
|
|
sRandomPlayerbotMgr->LogoutAllBots();
|
|
}
|
|
};
|
|
|
|
class PlayerBotsBGScript : public BGScript
|
|
{
|
|
public:
|
|
PlayerBotsBGScript() : BGScript("PlayerBotsBGScript") {}
|
|
|
|
void OnBattlegroundStart(Battleground* bg) override
|
|
{
|
|
BGStrategyData data;
|
|
|
|
switch (bg->GetBgTypeID())
|
|
{
|
|
case BATTLEGROUND_WS:
|
|
data.allianceStrategy = urand(0, WS_STRATEGY_MAX - 1);
|
|
data.hordeStrategy = urand(0, WS_STRATEGY_MAX - 1);
|
|
break;
|
|
case BATTLEGROUND_AB:
|
|
data.allianceStrategy = urand(0, AB_STRATEGY_MAX - 1);
|
|
data.hordeStrategy = urand(0, AB_STRATEGY_MAX - 1);
|
|
break;
|
|
case BATTLEGROUND_AV:
|
|
data.allianceStrategy = urand(0, AV_STRATEGY_MAX - 1);
|
|
data.hordeStrategy = urand(0, AV_STRATEGY_MAX - 1);
|
|
break;
|
|
case BATTLEGROUND_EY:
|
|
data.allianceStrategy = urand(0, EY_STRATEGY_MAX - 1);
|
|
data.hordeStrategy = urand(0, EY_STRATEGY_MAX - 1);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
bgStrategies[bg->GetInstanceID()] = data;
|
|
}
|
|
|
|
void OnBattlegroundEnd(Battleground* bg, TeamId /*winnerTeam*/) override { bgStrategies.erase(bg->GetInstanceID()); }
|
|
};
|
|
|
|
void AddPlayerbotsScripts()
|
|
{
|
|
new PlayerbotsDatabaseScript();
|
|
new PlayerbotsPlayerScript();
|
|
new PlayerbotsMiscScript();
|
|
new PlayerbotsServerScript();
|
|
new PlayerbotsWorldScript();
|
|
new PlayerbotsScript();
|
|
new PlayerBotsBGScript();
|
|
|
|
AddSC_playerbots_commandscript();
|
|
}
|