fix(Core/RPG): Scope do-quest yield-to-grind to current objective only

This commit is contained in:
bash 2026-05-30 15:04:10 +02:00
parent 8c027e3a70
commit 4e8e3e2afe
3 changed files with 84 additions and 2 deletions

View File

@ -331,8 +331,11 @@ bool NewRpgDoQuestAction::DoIncompleteQuest(NewRpgInfo::DoQuest& data)
if (bot->GetDistance(data.pos) > 10.0f && !data.lastReachPOI) if (bot->GetDistance(data.pos) > 10.0f && !data.lastReachPOI)
{ {
// yield to attack-anything if a quest mob is right next to us // Yield to attack-anything ONLY if a mob needed by this exact
if (HasNearbyQuestMob(15.0f)) // quest+objective is right next to us. The broad variant (any
// quest in the log) yielded for every nearby mob and derailed
// turn-ins / cross-zone travel through other quests' clusters.
if (HasNearbyQuestMobForObjective(15.0f, data.questId, data.objectiveIdx))
return false; return false;
// Note: previously yielded ~10%/tick when any hostile was // Note: previously yielded ~10%/tick when any hostile was

View File

@ -1558,6 +1558,79 @@ bool NewRpgBaseAction::HasNearbyQuestMob(float range)
return false; return false;
} }
bool NewRpgBaseAction::HasNearbyQuestMobForObjective(float range, uint32 questId, int32 objectiveIdx)
{
if (!questId)
return false;
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
if (!quest)
return false;
// Turn-in path: completed quest has no remaining mob objective.
if (bot->GetQuestStatus(questId) == QUEST_STATUS_COMPLETE)
return false;
QuestStatusData const& qs = bot->getQuestStatusMap().at(questId);
uint32 neededCreatureEntry = 0;
uint32 neededItemId = 0;
if (objectiveIdx >= 0 && objectiveIdx < QUEST_OBJECTIVES_COUNT)
{
int32 entry = quest->RequiredNpcOrGo[objectiveIdx];
if (entry > 0 &&
qs.CreatureOrGOCount[objectiveIdx] < quest->RequiredNpcOrGoCount[objectiveIdx])
{
neededCreatureEntry = uint32(entry);
}
// Item objective sometimes lives in the same slot range.
if (objectiveIdx < QUEST_ITEM_OBJECTIVES_COUNT &&
quest->RequiredItemId[objectiveIdx] &&
qs.ItemCount[objectiveIdx] < quest->RequiredItemCount[objectiveIdx])
{
neededItemId = quest->RequiredItemId[objectiveIdx];
}
}
else if (objectiveIdx >= QUEST_OBJECTIVES_COUNT &&
objectiveIdx < QUEST_OBJECTIVES_COUNT + QUEST_ITEM_OBJECTIVES_COUNT)
{
int32 itemSlot = objectiveIdx - QUEST_OBJECTIVES_COUNT;
if (quest->RequiredItemId[itemSlot] &&
qs.ItemCount[itemSlot] < quest->RequiredItemCount[itemSlot])
{
neededItemId = quest->RequiredItemId[itemSlot];
}
}
if (!neededCreatureEntry && !neededItemId)
return false;
GuidVector possibleTargets = AI_VALUE(GuidVector, "possible targets");
for (ObjectGuid guid : possibleTargets)
{
Creature* c = botAI->GetCreature(guid);
if (!c || !c->IsInWorld() || !c->IsAlive())
continue;
if (!(c->GetPhaseMask() & bot->GetPhaseMask()))
continue;
if (bot->GetDistance(c) > range)
continue;
if (neededCreatureEntry && c->GetEntry() == neededCreatureEntry)
return true;
if (neededItemId)
{
CreatureTemplate const* tmpl = c->GetCreatureTemplate();
if (tmpl && tmpl->lootid &&
LootTemplates_Creature.HaveQuestLootForPlayer(tmpl->lootid, bot))
return true;
}
}
return false;
}
ObjectGuid NewRpgBaseAction::ChooseNpcOrGameObjectToInteract(bool questgiverOnly, float distanceLimit) ObjectGuid NewRpgBaseAction::ChooseNpcOrGameObjectToInteract(bool questgiverOnly, float distanceLimit)
{ {

View File

@ -68,6 +68,12 @@ protected:
// travel so we yield to attack-anything instead of running past. // travel so we yield to attack-anything instead of running past.
bool HasNearbyQuestMob(float range = 20.0f); bool HasNearbyQuestMob(float range = 20.0f);
// Narrower variant: only yields for mobs needed by the SPECIFIC
// quest+objective the bot is currently working on. Without this,
// do-quest yields for any quest in the log, derailing turn-ins
// and cross-zone travel through other quests' mob clusters.
bool HasNearbyQuestMobForObjective(float range, uint32 questId, int32 objectiveIdx);
protected: protected:
bool GetQuestPOIPosAndObjectiveIdx(uint32 questId, std::vector<POIInfo>& poiInfo, bool toComplete = false); bool GetQuestPOIPosAndObjectiveIdx(uint32 questId, std::vector<POIInfo>& poiInfo, bool toComplete = false);
static WorldPosition SelectRandomGrindPos(Player* bot); static WorldPosition SelectRandomGrindPos(Player* bot);