mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
386 lines
14 KiB
C++
386 lines
14 KiB
C++
/*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 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 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 "BattleGroundTactics.h"
|
|
#include "Chat.h"
|
|
#include "GuildTaskMgr.h"
|
|
#include "PerfMonitor.h"
|
|
#include "PlayerbotMgr.h"
|
|
#include "RandomPlayerbotMgr.h"
|
|
#include "ScriptMgr.h"
|
|
#include "TravelNode.h"
|
|
|
|
using namespace Acore::ChatCommands;
|
|
|
|
class playerbots_commandscript : public CommandScript
|
|
{
|
|
public:
|
|
playerbots_commandscript() : CommandScript("playerbots_commandscript") {}
|
|
|
|
ChatCommandTable GetCommands() const override
|
|
{
|
|
static ChatCommandTable playerbotsDebugCommandTable = {
|
|
{"bg", HandleDebugBGCommand, SEC_GAMEMASTER, Console::Yes},
|
|
{"zone", HandleDebugZoneCommand, SEC_GAMEMASTER, Console::No},
|
|
};
|
|
|
|
static ChatCommandTable playerbotsAccountCommandTable = {
|
|
{"setKey", HandleSetSecurityKeyCommand, SEC_PLAYER, Console::No},
|
|
{"link", HandleLinkAccountCommand, SEC_PLAYER, Console::No},
|
|
{"linkedAccounts", HandleViewLinkedAccountsCommand, SEC_PLAYER, Console::No},
|
|
{"unlink", HandleUnlinkAccountCommand, SEC_PLAYER, Console::No},
|
|
};
|
|
|
|
static ChatCommandTable playerbotsTravelCommandTable = {
|
|
{"generatenode", HandleGenerateTravelNodesCommand, SEC_GAMEMASTER, Console::Yes},
|
|
};
|
|
|
|
static ChatCommandTable playerbotsCommandTable = {
|
|
{"bot", HandlePlayerbotCommand, SEC_PLAYER, Console::No},
|
|
{"gtask", HandleGuildTaskCommand, SEC_GAMEMASTER, Console::Yes},
|
|
{"pmon", HandlePerfMonCommand, SEC_GAMEMASTER, Console::Yes},
|
|
{"rndbot", HandleRandomPlayerbotCommand, SEC_GAMEMASTER, Console::Yes},
|
|
{"travel", playerbotsTravelCommandTable},
|
|
{"debug", playerbotsDebugCommandTable},
|
|
{"account", playerbotsAccountCommandTable},
|
|
};
|
|
|
|
static ChatCommandTable commandTable = {
|
|
{"playerbots", playerbotsCommandTable},
|
|
};
|
|
|
|
return commandTable;
|
|
}
|
|
|
|
static bool HandlePlayerbotCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
return PlayerbotMgr::HandlePlayerbotMgrCommand(handler, args);
|
|
}
|
|
|
|
static bool HandleRandomPlayerbotCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
return RandomPlayerbotMgr::HandlePlayerbotConsoleCommand(handler, args);
|
|
}
|
|
|
|
static bool HandleGuildTaskCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
return GuildTaskMgr::HandleConsoleCommand(handler, args);
|
|
}
|
|
|
|
static bool HandlePerfMonCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!strcmp(args, "reset"))
|
|
{
|
|
sPerfMonitor.Reset();
|
|
return true;
|
|
}
|
|
|
|
if (!strcmp(args, "tick"))
|
|
{
|
|
sPerfMonitor.PrintStats(true, false);
|
|
return true;
|
|
}
|
|
|
|
if (!strcmp(args, "stack"))
|
|
{
|
|
sPerfMonitor.PrintStats(false, true);
|
|
return true;
|
|
}
|
|
|
|
if (!strcmp(args, "toggle"))
|
|
{
|
|
sPlayerbotAIConfig.perfMonEnabled = !sPlayerbotAIConfig.perfMonEnabled;
|
|
if (sPlayerbotAIConfig.perfMonEnabled)
|
|
LOG_INFO("playerbots", "Performance monitor enabled");
|
|
else
|
|
LOG_INFO("playerbots", "Performance monitor disabled");
|
|
return true;
|
|
}
|
|
|
|
sPerfMonitor.PrintStats();
|
|
return true;
|
|
}
|
|
|
|
static bool HandleGenerateTravelNodesCommand(ChatHandler* handler, char const* /*args*/)
|
|
{
|
|
handler->PSendSysMessage("Regenerating travel node paths...");
|
|
LOG_INFO("playerbots", "Manual travel node regeneration started via console command.");
|
|
sTravelNodeMap.generateAll();
|
|
handler->PSendSysMessage("Travel node regeneration complete. Paths saved to database.");
|
|
return true;
|
|
}
|
|
|
|
static bool HandleDebugBGCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
return BGTactics::HandleConsoleCommand(handler, args);
|
|
}
|
|
|
|
// Visual constants for showpath markers. Two waypoint-family
|
|
// creatures give nodes vs path waypoints distinct visuals; both
|
|
// render at their creature_template default scale (no override).
|
|
// nodes (anchors) → 15897, prominent waypoint variant
|
|
// path waypoints → 15631, standard BG-showpath waypoint
|
|
//
|
|
// SHOWPATH_PATH_DISPLAY_ID = 0 uses the path-creature's default
|
|
// model. To experiment with a model override, set this to a known-
|
|
// good creature display ID for your DB (spell-visual IDs are not
|
|
// universally registered as creature displays — using one risks
|
|
// summoning invisible markers).
|
|
static constexpr uint32 SHOWPATH_NODE_CREATURE = 15897;
|
|
static constexpr uint32 SHOWPATH_PATH_CREATURE = 15631;
|
|
static constexpr uint32 SHOWPATH_PATH_DISPLAY_ID = 0; // 0 = default model
|
|
static constexpr uint32 SHOWPATH_DESPAWN_MS = 60000;
|
|
|
|
static bool HandleDebugZoneCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
Player* player = handler->GetSession() ? handler->GetSession()->GetPlayer() : nullptr;
|
|
if (!player)
|
|
{
|
|
handler->PSendSysMessage("Command requires an in-game player.");
|
|
return false;
|
|
}
|
|
|
|
if (!args || !*args)
|
|
{
|
|
handler->PSendSysMessage("usage: .playerbots debug zone showpath=all|node|path");
|
|
return false;
|
|
}
|
|
|
|
char* cmd = strtok(const_cast<char*>(args), " ");
|
|
// showpath=all → nodes + cached path waypoints (full picture)
|
|
// showpath=node → only node anchors
|
|
// showpath=path → only cached path waypoints (no anchors)
|
|
bool showNodes = false;
|
|
bool showLinks = false;
|
|
if (cmd && strcmp(cmd, "showpath=all") == 0)
|
|
{
|
|
showNodes = true;
|
|
showLinks = true;
|
|
}
|
|
else if (cmd && strcmp(cmd, "showpath=node") == 0)
|
|
{
|
|
showNodes = true;
|
|
showLinks = false;
|
|
}
|
|
else if (cmd && strcmp(cmd, "showpath=path") == 0)
|
|
{
|
|
showNodes = false;
|
|
showLinks = true;
|
|
}
|
|
else
|
|
{
|
|
handler->PSendSysMessage("usage: .playerbots debug zone showpath=all|node|path");
|
|
return false;
|
|
}
|
|
|
|
uint32 zoneId = player->GetZoneId();
|
|
std::vector<TravelNode*> const& nodes = sTravelNodeMap.GetNodesInZone(zoneId);
|
|
if (nodes.empty())
|
|
{
|
|
handler->PSendSysMessage("No travel nodes registered in zone {} (is the travel node system loaded?)", zoneId);
|
|
return true;
|
|
}
|
|
|
|
// node markers — full-scale anchor at each travel-node position.
|
|
uint32 nodesPlaced = 0;
|
|
if (showNodes)
|
|
{
|
|
for (TravelNode* node : nodes)
|
|
{
|
|
if (!node)
|
|
continue;
|
|
WorldPosition* pos = node->getPosition();
|
|
if (!pos || pos->GetMapId() != player->GetMapId())
|
|
continue;
|
|
Creature* wp = player->SummonCreature(SHOWPATH_NODE_CREATURE,
|
|
pos->GetPositionX(), pos->GetPositionY(),
|
|
pos->GetPositionZ(), 0,
|
|
TEMPSUMMON_TIMED_DESPAWN, SHOWPATH_DESPAWN_MS);
|
|
if (wp)
|
|
{
|
|
wp->SetOwnerGUID(player->GetGUID());
|
|
++nodesPlaced;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!showLinks)
|
|
{
|
|
handler->PSendSysMessage("Showing {} travel nodes in zone {} (60s)", nodesPlaced, zoneId);
|
|
return true;
|
|
}
|
|
|
|
// path-waypoint markers — same creature, scaled down so they
|
|
// read as a breadcrumb trail between nodes rather than as more
|
|
// anchor points. Walk-type links from any in-zone node are
|
|
// drawn; the per-waypoint same-map filter keeps the trail from
|
|
// running into other continents. Sparse zones (e.g. Teldrassil)
|
|
// would draw nothing if we required dst-in-zone too, since their
|
|
// only links go to nodes in neighbouring zones.
|
|
constexpr uint32 MAX_PATH_MARKERS = 500;
|
|
uint32 pathPlaced = 0;
|
|
uint32 linksDrawn = 0;
|
|
bool capped = false;
|
|
for (TravelNode* node : nodes)
|
|
{
|
|
if (!node)
|
|
continue;
|
|
auto* links = node->getLinks();
|
|
if (!links)
|
|
continue;
|
|
for (auto const& kv : *links)
|
|
{
|
|
TravelNode* dst = kv.first;
|
|
TravelNodePath* path = kv.second;
|
|
if (!dst || !path)
|
|
continue;
|
|
if (path->getPathType() != TravelNodePathType::walk)
|
|
continue;
|
|
++linksDrawn;
|
|
for (WorldPosition const& wpPos : path->GetPath())
|
|
{
|
|
if (wpPos.GetMapId() != player->GetMapId())
|
|
continue;
|
|
if (pathPlaced >= MAX_PATH_MARKERS)
|
|
{
|
|
capped = true;
|
|
break;
|
|
}
|
|
Creature* mk = player->SummonCreature(SHOWPATH_PATH_CREATURE,
|
|
wpPos.GetPositionX(),
|
|
wpPos.GetPositionY(), wpPos.GetPositionZ(),
|
|
0, TEMPSUMMON_TIMED_DESPAWN,
|
|
SHOWPATH_DESPAWN_MS);
|
|
if (mk)
|
|
{
|
|
mk->SetOwnerGUID(player->GetGUID());
|
|
if (SHOWPATH_PATH_DISPLAY_ID)
|
|
mk->SetDisplayId(SHOWPATH_PATH_DISPLAY_ID);
|
|
++pathPlaced;
|
|
}
|
|
}
|
|
if (capped)
|
|
break;
|
|
}
|
|
if (capped)
|
|
break;
|
|
}
|
|
|
|
handler->PSendSysMessage("Showing {} nodes + {} path waypoints across {} walk links in zone {}{} (60s)",
|
|
nodesPlaced, pathPlaced, linksDrawn, zoneId,
|
|
capped ? " — capped at 500 path markers" : "");
|
|
return true;
|
|
}
|
|
|
|
static bool HandleSetSecurityKeyCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!args || !*args)
|
|
{
|
|
handler->PSendSysMessage("Usage: .playerbots account setKey <securityKey>");
|
|
return false;
|
|
}
|
|
|
|
Player* player = handler->GetSession()->GetPlayer();
|
|
std::string key = args;
|
|
|
|
PlayerbotMgr* mgr = PlayerbotsMgr::instance().GetPlayerbotMgr(player);
|
|
if (mgr)
|
|
{
|
|
mgr->HandleSetSecurityKeyCommand(player, key);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
handler->PSendSysMessage("PlayerbotMgr instance not found.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool HandleLinkAccountCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!args || !*args)
|
|
return false;
|
|
|
|
char* accountName = strtok((char*)args, " ");
|
|
char* key = strtok(nullptr, " ");
|
|
|
|
if (!accountName || !key)
|
|
{
|
|
handler->PSendSysMessage("Usage: .playerbots account link <accountName> <securityKey>");
|
|
return false;
|
|
}
|
|
|
|
Player* player = handler->GetSession()->GetPlayer();
|
|
|
|
PlayerbotMgr* mgr = PlayerbotsMgr::instance().GetPlayerbotMgr(player);
|
|
if (mgr)
|
|
{
|
|
mgr->HandleLinkAccountCommand(player, accountName, key);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
handler->PSendSysMessage("PlayerbotMgr instance not found.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool HandleViewLinkedAccountsCommand(ChatHandler* handler, char const* /*args*/)
|
|
{
|
|
Player* player = handler->GetSession()->GetPlayer();
|
|
|
|
PlayerbotMgr* mgr = PlayerbotsMgr::instance().GetPlayerbotMgr(player);
|
|
if (mgr)
|
|
{
|
|
mgr->HandleViewLinkedAccountsCommand(player);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
handler->PSendSysMessage("PlayerbotMgr instance not found.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool HandleUnlinkAccountCommand(ChatHandler* handler, char const* args)
|
|
{
|
|
if (!args || !*args)
|
|
return false;
|
|
|
|
char* accountName = strtok((char*)args, " ");
|
|
if (!accountName)
|
|
{
|
|
handler->PSendSysMessage("Usage: .playerbots account unlink <accountName>");
|
|
return false;
|
|
}
|
|
|
|
Player* player = handler->GetSession()->GetPlayer();
|
|
|
|
PlayerbotMgr* mgr = PlayerbotsMgr::instance().GetPlayerbotMgr(player);
|
|
if (mgr)
|
|
{
|
|
mgr->HandleUnlinkAccountCommand(player, accountName);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
handler->PSendSysMessage("PlayerbotMgr instance not found.");
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
|
|
void AddPlayerbotsCommandscripts() { new playerbots_commandscript(); }
|