feat(Core/Debug): Combat-aware state label + retry counter visibility + give-up event emit

This commit is contained in:
bash 2026-06-01 00:25:16 +02:00
parent e854e9beca
commit 5baba708e0
2 changed files with 46 additions and 7 deletions

View File

@ -62,7 +62,9 @@ void MovementAction::EmitDebugMove(char const* method, char const* generator, fl
NewRpgInfo& info = botAI->rpgInfo;
NewRpgStatus status = info.GetStatus();
bool const inCombat = botAI->GetState() == BOT_STATE_COMBAT;
char const* statusName =
inCombat ? "combat" :
status == RPG_IDLE ? "idle" :
status == RPG_GO_GRIND ? "go-grind" :
status == RPG_GO_CAMP ? "go-camp" :
@ -73,14 +75,22 @@ void MovementAction::EmitDebugMove(char const* method, char const* generator, fl
status == RPG_TRAVEL_FLIGHT ? "travel-flight" :
status == RPG_OUTDOOR_PVP ? "outdoor-pvp" : "?";
// Resolve a human-readable target name from the RPG context. When
// we can name the target (quest objective, wander NPC, flight
// master, travel-node hop, etc.), it replaces the loc=(x,y,z)
// field — names are far more useful than coordinates. When no
// target can be named (combat moves, follow, flee, ad-hoc), we
// fall through to loc=(x,y,z).
// Resolve a human-readable target name. In combat, the bot is
// actively engaging an enemy that is unrelated to the RPG state's
// target — show that enemy instead of the now-stale RPG goal.
// Out of combat, fall back to the RPG context: quest objective,
// wander NPC, flight master, etc. Names are far more useful than
// coordinates; loc=(x,y,z) only when nothing nameable applies.
std::string targetName;
switch (status)
if (inCombat)
{
Unit* current = *botAI->GetAiObjectContext()->GetValue<Unit*>("current target");
Unit* enemyPlayer = *botAI->GetAiObjectContext()->GetValue<Unit*>("enemy player target");
Unit* enemy = current ? current : enemyPlayer;
if (enemy)
targetName = std::string("vs:") + enemy->GetName();
}
else switch (status)
{
case RPG_DO_QUEST:
if (auto* data = std::get_if<NewRpgInfo::DoQuest>(&info.data))
@ -164,6 +174,11 @@ void MovementAction::EmitDebugMove(char const* method, char const* generator, fl
<< " | " << statusName
<< " | " << std::fixed << std::setprecision(2) << dis << " yard"
<< " | " << (targetName.empty() ? "-" : targetName.c_str());
// Surface the RPG MoveFarTo retry counter so when bots get stuck
// it's obvious from the whisper alone (retry=N/MAX) — and the
// "give-up" event emitters below show retry=MAX/MAX explicitly.
if (info.moveRetryCount > 0)
out << " | retry=" << uint32(info.moveRetryCount) << "/" << uint32(NewRpgInfo::MAX_MOVE_RETRIES);
if (extra && *extra)
out << " | " << extra;
botAI->TellMasterNoFacing(out);

View File

@ -164,7 +164,12 @@ bool NewRpgGoGrindAction::Execute(Event /*event*/)
// transitioning out of the stuck state instead of nudging in
// place. Idle lets the status picker rotate to a new state.
if (++botAI->rpgInfo.moveRetryCount >= NewRpgInfo::MAX_MOVE_RETRIES)
{
EmitDebugMove("MoveFar", "give-up",
data->pos.GetPositionX(), data->pos.GetPositionY(), data->pos.GetPositionZ(),
"idle");
botAI->rpgInfo.ChangeToIdle();
}
return true; // consume tick, no nudge
}
@ -184,7 +189,12 @@ bool NewRpgGoCampAction::Execute(Event /*event*/)
return true;
}
if (++botAI->rpgInfo.moveRetryCount >= NewRpgInfo::MAX_MOVE_RETRIES)
{
EmitDebugMove("MoveFar", "give-up",
data->pos.GetPositionX(), data->pos.GetPositionY(), data->pos.GetPositionZ(),
"idle");
botAI->rpgInfo.ChangeToIdle();
}
return true;
}
@ -250,6 +260,9 @@ bool NewRpgWanderNpcAction::Execute(Event /*event*/)
// one. No nudge — stand still until retry.
if (++botAI->rpgInfo.moveRetryCount >= NewRpgInfo::MAX_MOVE_RETRIES)
{
EmitDebugMove("MoveFar", "give-up",
bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
"drop-npc");
data.npcOrGo = ObjectGuid();
data.lastReach = 0;
botAI->rpgInfo.moveRetryCount = 0;
@ -386,6 +399,12 @@ bool NewRpgDoQuestAction::DoIncompleteQuest(NewRpgInfo::DoQuest& data)
// catches it next tick.
if (++botAI->rpgInfo.moveRetryCount >= NewRpgInfo::MAX_MOVE_RETRIES)
{
std::ostringstream nx;
nx << "next-spawn(" << (data.currentSpawnIdx + 1) << "/"
<< data.candidateSpawns.size() << ")";
EmitDebugMove("MoveFar", "give-up",
target.GetPositionX(), target.GetPositionY(), target.GetPositionZ(),
nx.str().c_str());
++data.currentSpawnIdx;
data.lastReachPOI = 0;
botAI->rpgInfo.moveRetryCount = 0;
@ -514,7 +533,12 @@ bool NewRpgDoQuestAction::DoCompletedQuest(NewRpgInfo::DoQuest& data)
// if turn-in POI is unreachable repeatedly so the bot doesn't
// sit on a broken handler.
if (++botAI->rpgInfo.moveRetryCount >= NewRpgInfo::MAX_MOVE_RETRIES)
{
EmitDebugMove("MoveFar", "give-up",
data.pos.GetPositionX(), data.pos.GetPositionY(), data.pos.GetPositionZ(),
"idle(turn-in)");
botAI->rpgInfo.ChangeToIdle();
}
return true;
}