From 8aa78f6b393c70b46437de216c1b30b44d8c1cde Mon Sep 17 00:00:00 2001 From: bash Date: Thu, 14 May 2026 23:49:55 +0200 Subject: [PATCH] feat(Core/RPG): Switch POI when current cluster is empty --- src/Ai/World/Rpg/Action/NewRpgAction.cpp | 39 ++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/Ai/World/Rpg/Action/NewRpgAction.cpp b/src/Ai/World/Rpg/Action/NewRpgAction.cpp index 9923130fa..168bd6d62 100644 --- a/src/Ai/World/Rpg/Action/NewRpgAction.cpp +++ b/src/Ai/World/Rpg/Action/NewRpgAction.cpp @@ -433,6 +433,45 @@ bool NewRpgDoQuestAction::DoIncompleteQuest(NewRpgInfo::DoQuest& data) return MoveRandomNear(20.0f); } + // Kill-quest scout: at POI for 30s+ with no quest mob in sight + // means this cluster is empty. Switch to a different POI candidate + // (>50y away) if one exists; otherwise roam in place. + constexpr uint32 scoutTimeoutMs = 30 * 1000; + if (data.lastReachPOI && GetMSTimeDiffToNow(data.lastReachPOI) >= scoutTimeoutMs && + !HasNearbyQuestMob(30.0f)) + { + std::vector poiInfo; + if (GetQuestPOIPosAndObjectiveIdx(questId, poiInfo)) + { + std::vector alternatives; + for (size_t i = 0; i < poiInfo.size(); ++i) + { + float dx = poiInfo[i].pos.x - data.pos.GetPositionX(); + float dy = poiInfo[i].pos.y - data.pos.GetPositionY(); + if (dx * dx + dy * dy > 50.0f * 50.0f) + alternatives.push_back(i); + } + if (!alternatives.empty()) + { + size_t pickIdx = alternatives[urand(0, alternatives.size() - 1)]; + G3D::Vector2 newPoi = poiInfo[pickIdx].pos; + float dz = std::max(bot->GetMap()->GetHeight(newPoi.x, newPoi.y, MAX_HEIGHT), + bot->GetMap()->GetWaterLevel(newPoi.x, newPoi.y)); + if (dz != INVALID_HEIGHT && dz != VMAP_INVALID_HEIGHT_VALUE) + { + data.pos = WorldPosition(bot->GetMapId(), newPoi.x, newPoi.y, dz); + data.objectiveIdx = poiInfo[pickIdx].objectiveIdx; + data.lastReachPOI = 0; + data.pursuedLootGO.Clear(); + data.pursuedUseGO.Clear(); + data.pursuedUseTarget.Clear(); + return true; + } + } + } + return MoveRandomNear(20.0f); + } + // kill quest: walk toward the marker before handing off to grind. // lastReachPOI trips at ~10y so without this the bot fights on the // edge and never reaches the dense cluster. Skip if a quest mob is