Skip to content

Commit

Permalink
NPCBots: Add .npcbot fix command to enable GMs to attempt and fix s…
Browse files Browse the repository at this point in the history
…tuck bots. Improve `.npcbot info` command - add bot ids, localized names and class colors
  • Loading branch information
trickerer committed Oct 23, 2024
1 parent c5fb0c3 commit e5b2d19
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 36 deletions.
36 changes: 34 additions & 2 deletions src/server/game/AI/NpcBots/bot_ai.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3478,30 +3478,62 @@ void bot_ai::ReceiveEmote(Player* player, uint32 emote)
break;
case TEXT_EMOTE_TICKLE:
{
if (master != player)
if (master != player && !player->IsGameMaster())
break;

std::ostringstream report;
report << "Problems:";

if ((me->HasUnitFlag(UNIT_FLAG_STUNNED) || me->HasUnitState(UNIT_STATE_STUNNED)) &&
!me->HasAuraType(SPELL_AURA_MOD_STUN))
{
report << "\n stunned but no aura";
me->ClearUnitState(UNIT_STATE_STUNNED);
me->RemoveUnitFlag(UNIT_FLAG_STUNNED);
}
if ((me->HasUnitFlag(UNIT_FLAG_CONFUSED) || me->HasUnitState(UNIT_STATE_CONFUSED)) &&
!me->HasAuraType(SPELL_AURA_MOD_CONFUSE))
{
report << "\n confused but no aura";
me->ClearUnitState(UNIT_STATE_CONFUSED);
me->RemoveUnitFlag(UNIT_FLAG_CONFUSED);
}
if ((me->HasUnitFlag(UNIT_FLAG_FLEEING) || me->HasUnitState(UNIT_STATE_FLEEING)) &&
!me->HasAuraType(SPELL_AURA_MOD_FEAR))
{
report << "\n feared but no aura";
me->ClearUnitState(UNIT_STATE_FLEEING);
me->RemoveUnitFlag(UNIT_FLAG_FLEEING);
}
me->BotStopMovement();
if (me->IsInCombat() && !me->GetVictim())
{
report << "\n in combat but no target: attackers=" << uint32(me->getAttackers().size());
me->CombatStop(true);
}
if (IsDuringTeleport() && me->IsInWorld() && me->FindMap())
{
report << "\n being teleported but in world & in map: home=" << uint32(!!teleHomeEvent) << ", finish=" << uint32(!!teleFinishEvent);
AbortTeleport();
}
if (HasBotCommandState(BOT_COMMAND_ISSUED_ORDER))
{
report << "\n pending orders that may have got stuck";
CancelAllOrders();
}
if (HasBotCommandState(BOT_COMMAND_NOGOSSIP))
{
report << "\n forgotten NOGOSSIP command state";
RemoveBotCommandState(BOT_COMMAND_NOGOSSIP);
}
if (waitTimer > 10000)
{
report << "\n wait timer overflow: " << waitTimer;
waitTimer = 0;
}

me->BotStopMovement();
me->TextEmote(LocalizedNpcText(player, BOT_TEXT_BOT_TICKLED).c_str());
ChatHandler(player->GetSession()).SendSysMessage(report.str().c_str());
break;
}
default:
Expand Down
111 changes: 77 additions & 34 deletions src/server/game/AI/NpcBots/botcommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,7 @@ class script_bot_commands : public CommandScript
{ "recall", npcbotRecallCommandTable },
{ "kill", HandleNpcBotKillCommand, rbac::RBAC_PERM_COMMAND_NPCBOT_KILL, Console::No },
{ "suicide", HandleNpcBotKillCommand, rbac::RBAC_PERM_COMMAND_NPCBOT_KILL, Console::No },
{ "fix", HandleNpcBotFixCommand, rbac::RBAC_PERM_COMMAND_NPCBOT_REVIVE, Console::No },
{ "go", HandleNpcBotGoCommand, rbac::RBAC_PERM_COMMAND_NPCBOT_MOVE, Console::No },
{ "gs", HandleNpcBotGearScoreCommand, rbac::RBAC_PERM_COMMAND_NPCBOT_COMMAND_MISC, Console::No },
{ "sendto", npcbotSendToCommandTable },
Expand Down Expand Up @@ -2248,6 +2249,68 @@ class script_bot_commands : public CommandScript
return true;
}

static bool HandleNpcBotFixCommand(ChatHandler* handler, Optional<Variant<std::string_view, uint32>> bot_id_or_name)
{
Creature const* target = handler->getSelectedCreature();

uint32 bot_id;
if (target && target->IsNPCBot())
bot_id = target->GetEntry();
else if (bot_id_or_name)
{
if (bot_id_or_name->holds_alternative<uint32>())
bot_id = bot_id_or_name->get<uint32>();
else if (Creature const* fbot = BotDataMgr::FindBot(bot_id_or_name->get<std::string_view>(), LocaleConstant(handler->GetSessionDbLocaleIndex())))
{
target = fbot;
bot_id = target->GetEntry();
}
else
{
char* cre_id = handler->extractKeyFromLink((char*)bot_id_or_name->get<std::string_view>().data(), "Hcreature_entry");
bot_id = uint32(atoi(cre_id));
}
}
else if (target)
{
handler->SendSysMessage("You must select a npcbot");
handler->SetSentErrorMessage(true);
return false;
}
else
{
handler->SendSysMessage(".npcbot fix #[id | name | link | <selection>]");
handler->SendSysMessage("Attempts to fix different bot's unit states and ai mishaps which stall its normal function");
handler->SetSentErrorMessage(true);
return false;
}

Creature const* bot = target ? target : BotDataMgr::FindBot(bot_id);
if (!bot)
{
handler->PSendSysMessage("NpcBot %u is not found!", bot_id);
handler->SetSentErrorMessage(true);
return false;
}

NpcBotData const* bot_data = BotDataMgr::SelectNpcBotData(bot_id);
Player* owner = !bot->IsFreeBot() ? bot->GetBotOwner() : nullptr;
Player* tickler = handler->GetPlayer();

if (tickler != owner && !tickler->IsGameMaster())
{
handler->SendSysMessage("Must be in GM mode to fix other player's bot!");
handler->SetSentErrorMessage(true);
return false;
}

handler->PSendSysMessage("Trying to fix bot %s (%u) owned by %s (%u)", bot->GetName().c_str(), bot_id,
owner ? owner->GetName().c_str() : "Unknown", owner ? owner->GetGUID().GetCounter() : bot_data->owner);

bot->GetBotAI()->ReceiveEmote(tickler, TEXT_EMOTE_TICKLE);
return true;
}

static bool HandleNpcBotKillCommand(ChatHandler* handler)
{
Player* owner = handler->GetSession()->GetPlayer();
Expand Down Expand Up @@ -3867,52 +3930,28 @@ class script_bot_commands : public CommandScript

handler->PSendSysMessage("Listing NpcBots for %s, guid %u%s:", master_name.c_str(), master_guid.GetCounter(), !master ? " (offline)" : "");
handler->PSendSysMessage("Owned NpcBots: %u (active: %u)", uint32(guidvec.size()) + map_size, map_size);
LocaleConstant loc = LocaleConstant(handler->GetSessionDbLocaleIndex());
if (map)
{
for (uint8 i = BOT_CLASS_WARRIOR; i != BOT_CLASS_END; ++i)
{
uint8 count = 0;
uint8 alivecount = 0;
for (BotMap::const_iterator itr = map->begin(); itr != map->end(); ++itr)
{
if (Creature* cre = itr->second)
{
if (cre->GetBotClass() == i)
{
++count;
if (cre->IsAlive())
++alivecount;
std::string ccolor, cname;
GetBotClassNameAndColor(i, ccolor, cname);
std::string base_name = cre->GetName();
if (CreatureLocale const* creatureLocale = sObjectMgr->GetCreatureLocale(cre->GetEntry()))
if (creatureLocale->Name.size() > loc && !creatureLocale->Name[loc].empty())
base_name = creatureLocale->Name[loc];

handler->PSendSysMessage("%s (%u): %s (alive: %u)", base_name.c_str(), cre->GetEntry(), "|c" + ccolor + cname + "|r", uint32(cre->IsAlive()));
}
}
}
if (count == 0)
continue;

char const* bclass;
switch (i)
{
case BOT_CLASS_WARRIOR: bclass = "Warriors"; break;
case BOT_CLASS_PALADIN: bclass = "Paladins"; break;
case BOT_CLASS_MAGE: bclass = "Mages"; break;
case BOT_CLASS_PRIEST: bclass = "Priests"; break;
case BOT_CLASS_WARLOCK: bclass = "Warlocks"; break;
case BOT_CLASS_DRUID: bclass = "Druids"; break;
case BOT_CLASS_DEATH_KNIGHT: bclass = "Death Knights"; break;
case BOT_CLASS_ROGUE: bclass = "Rogues"; break;
case BOT_CLASS_SHAMAN: bclass = "Shamans"; break;
case BOT_CLASS_HUNTER: bclass = "Hunters"; break;
case BOT_CLASS_BM: bclass = "Blademasters"; break;
case BOT_CLASS_SPHYNX: bclass = "Destroyers"; break;
case BOT_CLASS_ARCHMAGE: bclass = "Archmagi"; break;
case BOT_CLASS_DREADLORD: bclass = "Dreadlords"; break;
case BOT_CLASS_SPELLBREAKER: bclass = "Spell Breakers"; break;
case BOT_CLASS_DARK_RANGER: bclass = "Dark Rangers"; break;
case BOT_CLASS_NECROMANCER: bclass = "Necromancers"; break;
case BOT_CLASS_SEA_WITCH: bclass = "Sea Witches"; break;
case BOT_CLASS_CRYPT_LORD: bclass = "Crypt Lords"; break;
default: bclass = "Unknown Class"; break;
}
handler->PSendSysMessage("%s: %u (alive: %u)", bclass, count, alivecount);
}
}

Expand All @@ -3922,7 +3961,11 @@ class script_bot_commands : public CommandScript
Creature const* bot = BotDataMgr::FindBot(guid.GetEntry());
std::string ccolor, cname;
GetBotClassNameAndColor(bot ? bot->GetBotClass() : uint8(BOT_CLASS_NONE), ccolor, cname);
handler->PSendSysMessage("%s (%s)", bot ? bot->GetName().c_str() : "Unknown", "|c" + ccolor + cname + "|r");
std::string base_name = bot ? bot->GetName() : "Unknown";
if (CreatureLocale const* creatureLocale = sObjectMgr->GetCreatureLocale(guid.GetEntry()))
if (creatureLocale->Name.size() > loc && !creatureLocale->Name[loc].empty())
base_name = creatureLocale->Name[loc];
handler->PSendSysMessage("%s (%u): %s (alive: %u)", base_name.c_str(), guid.GetEntry(), "|c" + ccolor + cname + "|r", bot ? uint32(bot->IsAlive()) : uint32(0));
}

return true;
Expand Down

0 comments on commit e5b2d19

Please sign in to comment.