From 1cf604dc6075723f0377b465b28fba5f8bfa4f9c Mon Sep 17 00:00:00 2001 From: bash Date: Sun, 31 May 2026 15:48:57 +0200 Subject: [PATCH] refactor(Core/Travel): Remove teleportSpell + NODE_TELEPORT + PortalNode + hearthstone/mage A* injection (staticPortal kept) --- src/Ai/Base/Actions/MovementActions.cpp | 40 ------- src/Mgr/Travel/TravelNode.cpp | 150 +----------------------- src/Mgr/Travel/TravelNode.h | 52 +------- 3 files changed, 5 insertions(+), 237 deletions(-) diff --git a/src/Ai/Base/Actions/MovementActions.cpp b/src/Ai/Base/Actions/MovementActions.cpp index 540a54801..c7a6dde4e 100644 --- a/src/Ai/Base/Actions/MovementActions.cpp +++ b/src/Ai/Base/Actions/MovementActions.cpp @@ -3342,46 +3342,6 @@ bool MovementAction::HandleSpecialMovement(TravelPath& path) return bot->ActivateTaxiPathTo(route, flightMaster, 0); } - case PathNodeType::NODE_TELEPORT: - { - if (!next.entry) - return false; - - // Can't cast while flying — let the bot land first. - bool const canCastNow = !bot->IsFlying(); - - if (next.entry == 8690) // Hearthstone - { - if (canCastNow) - { - bool const ok = botAI->DoSpecificAction("hearthstone", - Event("move action"), true); - if (ok) - return true; - } - } - else if (canCastNow) - { - // Mage city portal / similar spell — dismount, drop - // shapeshift, queue cast. We don't gate on reagents (no - // "has reagents for" value on AC); the server-side cast - // attempt will fail cleanly if reagents are missing. - if (bot->IsMounted()) - bot->Dismount(); - botAI->RemoveShapeshift(); - if (botAI->DoSpecificAction( - "cast", - Event("rpg action", std::to_string(next.entry)), true)) - return true; - } - - // Cast didn't happen or failed — clear the cached path so - // the next tick re-resolves cleanly instead of retrying the - // same teleport edge that just failed. - AI_VALUE(LastMovement&, "last movement").setPath(TravelPath()); - return false; - } - default: return false; } diff --git a/src/Mgr/Travel/TravelNode.cpp b/src/Mgr/Travel/TravelNode.cpp index 6afed6a5b..7e0dd3de8 100644 --- a/src/Mgr/Travel/TravelNode.cpp +++ b/src/Mgr/Travel/TravelNode.cpp @@ -912,13 +912,6 @@ bool TravelPath::UpcommingSpecialMovement(WorldPosition startPos, return true; } - // Teleport spell (hearthstone et al.): fire on the next-step marker. - if (nextP->type == PathNodeType::NODE_TELEPORT) - { - cutTo(*nextP, false); - return true; - } - // Flight path: interact with flight master when in range. if (startP->type == PathNodeType::NODE_FLIGHTPATH && startPos.distance(startP->point) < INTERACTION_DISTANCE) @@ -1239,14 +1232,6 @@ TravelPath TravelNodeRoute::BuildPath(std::vector pathToStart, st // Full taxi waypoint route; same reasoning as transport. travelPath.addPath(nodePath->GetPath(), PathNodeType::NODE_FLIGHTPATH, nodePath->getPathObject()); } - else if (nodePath->getPathType() == TravelNodePathType::teleportSpell) - { - // Hearthstone or spell-cast teleport edge: emit a paired - // NODE_TELEPORT (entry = exit) so HandleSpecialMovement can - // dispatch the cast when the head reaches the entry point. - travelPath.addPoint(*prevNode->getPosition(), PathNodeType::NODE_TELEPORT, nodePath->getPathObject()); - travelPath.addPoint(*node->getPosition(), PathNodeType::NODE_TELEPORT, nodePath->getPathObject()); - } else { std::vector path = nodePath->GetPath(); @@ -1435,8 +1420,6 @@ TravelNodeRoute TravelNodeMap::GetNodeRoute(TravelNode* start, TravelNode* goal, std::vector open, closed; - std::vector portNodes; // synthetic teleport/portal edges - if (bot) { PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); @@ -1473,97 +1456,20 @@ TravelNodeRoute TravelNodeMap::GetNodeRoute(TravelNode* start, TravelNode* goal, PAI_VALUE2(uint32, "free money for", (uint32)NeedMoneyFor::travel)); } } - - // Hearthstone (item 6948 / spell 8690): inject a synthetic - // teleport edge from start to the node nearest the bot's - // home bind, so A* can pick hearthing over walking. - if (bot->IsAlive() && bot->HasItemCount(6948, 1)) - { - WorldPosition homePos = AI_VALUE(WorldPosition, "home bind"); - std::vector dummy; - TravelNode* homeNode = sTravelNodeMap.getNode(homePos, dummy, nullptr, 50.0f); - if (homeNode && homeNode != start) - { - PortalNode* portNode = new PortalNode(start); - portNode->SetPortal(start, homeNode, 8690); - - TravelNodeStub* hsStub = &m_stubs.insert(std::make_pair( - static_cast(portNode), TravelNodeStub(portNode))).first->second; - - // Cost: max(2, (10 - deathCount) * MINUTE) — matches - // reference exactly, including the uint32 underflow at - // deathCount > 10 (which makes hearthstone prohibitive - // for very-dead bots — apparently intentional). - hsStub->costFromStart = std::max(2, - (10 - AI_VALUE(uint32, "death count")) * MINUTE); - hsStub->heuristic = hsStub->dataNode->fDist(goal) / botSpeed; - hsStub->totalCost = hsStub->costFromStart + hsStub->heuristic; - - open.push_back(hsStub); - hsStub->open = true; - portNodes.push_back(portNode); - } - } - - // Mage teleport spells: 3561 Stormwind, 3562 Ironforge, 3563 Undercity, - // 3565 Darnassus, 3566 Thunder Bluff, 3567 Orgrimmar, 18960 Moonglade. - // Inject one synthetic teleport edge per known + ready spell. - static const uint32 teleSpells[] = {3561, 3562, 3563, 3565, 3566, 3567, 18960}; - for (uint32 spellId : teleSpells) - { - if (!bot->IsAlive() || bot->IsInCombat()) - break; - if (!bot->HasSpell(spellId)) - continue; - if (bot->HasSpellCooldown(spellId)) - continue; - - SpellTargetPosition const* stp = - sSpellMgr->GetSpellTargetPosition(spellId, EFFECT_0); - if (!stp) - continue; - - WorldPosition telePos(stp->target_mapId, stp->target_X, - stp->target_Y, stp->target_Z, 0.0f); - std::vector dummy; - TravelNode* destNode = sTravelNodeMap.getNode(telePos, dummy, nullptr, 10.0f); - if (!destNode || destNode == start) - continue; - - PortalNode* portNode = new PortalNode(start); - portNode->SetPortal(start, destNode, spellId); - - TravelNodeStub* tsStub = &m_stubs.insert(std::make_pair( - static_cast(portNode), TravelNodeStub(portNode))).first->second; - - tsStub->costFromStart = MINUTE; // cheaper than ~1-min walk - tsStub->heuristic = tsStub->dataNode->fDist(goal) / botSpeed; - tsStub->totalCost = tsStub->costFromStart + tsStub->heuristic; - - open.push_back(tsStub); - tsStub->open = true; - portNodes.push_back(portNode); - } } else startStub->currentGold = bot->GetMoney(); } - if (open.empty() && !start->hasRouteTo(goal)) - { - for (auto* p : portNodes) - delete p; + if (!start->hasRouteTo(goal)) return TravelNodeRoute(); - } // Min-heap: smallest f at front auto heapComp = [](TravelNodeStub* i, TravelNodeStub* j) { return i->totalCost > j->totalCost; }; open.push_back(startStub); startStub->open = true; - // Heapify all of open in one pass — covers both startStub and any - // PortalNode stubs injected above. - std::make_heap(open.begin(), open.end(), heapComp); + std::push_heap(open.begin(), open.end(), heapComp); while (!open.empty()) { @@ -1589,12 +1495,7 @@ TravelNodeRoute TravelNodeMap::GetNodeRoute(TravelNode* start, TravelNode* goal, } reverse(path.begin(), path.end()); - - // Successful route: hand off ownership of any synthetic - // PortalNodes injected at the head. Caller (GetFullPath) - // is expected to call cleanTempNodes() when done with the - // route — see the call site for the lifecycle. - return TravelNodeRoute(path, portNodes); + return TravelNodeRoute(path); } for (auto const& link : *currentNode->dataNode->getLinks()) // for each successor n' of n @@ -1633,9 +1534,6 @@ TravelNodeRoute TravelNodeMap::GetNodeRoute(TravelNode* start, TravelNode* goal, } } - // A* exhausted open without reaching goal. Clean up synthetic nodes. - for (auto* p : portNodes) - delete p; return TravelNodeRoute(); } @@ -1828,7 +1726,6 @@ TravelPath TravelNodeMap::GetFullPath(WorldPosition botPos, [[maybe_unused]] uin if (transportEntry) { path = route.BuildPath({botPos}, endProbe, bot); - route.cleanTempNodes(); return path; } @@ -1852,7 +1749,6 @@ TravelPath TravelNodeMap::GetFullPath(WorldPosition botPos, [[maybe_unused]] uin if (!startPathOk) { badStartNodes.push_back(s); - route.cleanTempNodes(); continue; } @@ -1861,50 +1757,10 @@ TravelPath TravelNodeMap::GetFullPath(WorldPosition botPos, [[maybe_unused]] uin // ResolveMovePath cycles can reuse it. beginPath = pathToStart; path = route.BuildPath(pathToStart, endProbe, bot); - route.cleanTempNodes(); return path; } } - // No graph route found. Last-resort hearthstone fallback (reference - // also does this): if bot has hearthstone item and is alive, treat - // the bot's current position as a one-off node and try routing from - // it to each endCandidate via the hearthstone PortalNode edge. - if (Player* player = dynamic_cast(bot)) - { - if (player->IsAlive() && player->HasItemCount(6948, 1)) - { - TravelNode* botNode = new TravelNode(botPos, "Bot Pos", false); - botNode->setPoint(botPos); - - for (TravelNode* e : endCandidates) - { - if (!e || std::find(badEndNodes.begin(), badEndNodes.end(), e) != badEndNodes.end()) - continue; - TravelNodeRoute route = GetNodeRoute(botNode, e, player); - if (route.isEmpty()) - continue; - - // Build the end-side path again for this candidate. - WorldPosition endNodePos = *e->getPosition(); - std::vector endProbe; - if (endNodePos.GetMapId() == destination.GetMapId()) - { - Unit* pathBot = (bot && bot->GetMapId() == destination.GetMapId()) ? bot : nullptr; - endProbe = endNodePos.getPathTo(destination, pathBot); - } - else - endProbe = {endNodePos, destination}; - - route.addTempNodes({botNode}); // transfer ownership of botNode - path = route.BuildPath({botPos}, endProbe, bot); - route.cleanTempNodes(); - return path; - } - delete botNode; - } - } - return path; // empty } diff --git a/src/Mgr/Travel/TravelNode.h b/src/Mgr/Travel/TravelNode.h index 83ec1fceb..e77cc5ffe 100644 --- a/src/Mgr/Travel/TravelNode.h +++ b/src/Mgr/Travel/TravelNode.h @@ -95,10 +95,7 @@ enum class TravelNodePathType : uint8 areaTrigger = 2, transport = 3, flightPath = 4, - // Teleport-spell edges (hearthstone, mage portals). Generated at A* - // search start via PortalNode injection; consumed by - // HandleSpecialMovement's NODE_TELEPORT case. - teleportSpell = 5, + // value 5 reserved (was teleportSpell — removed) staticPortal = 6 }; @@ -410,26 +407,6 @@ protected: // uint32 transportId = 0; }; -// Synthetic A* node injected at search start to represent a teleport-spell -// (hearthstone, mage portal, etc.) as an alternative travel edge. Owned -// by GetNodeRoute caller; deleted after the route is built. -class PortalNode : public TravelNode -{ -public: - PortalNode(TravelNode* baseNode) : TravelNode(baseNode) {} - - void SetPortal(TravelNode* baseNode, TravelNode* endNode, uint32 portalSpell) - { - nodeName = baseNode->getName(); - point = *baseNode->getPosition(); - paths.clear(); - links.clear(); - TravelNodePath path(0.1f, 0.1f, (uint8)TravelNodePathType::teleportSpell, - portalSpell, true); - setPathTo(endNode, path); - } -}; - // Route step type enum class PathNodeType : uint8 { @@ -439,10 +416,7 @@ enum class PathNodeType : uint8 NODE_AREA_TRIGGER = 3, NODE_TRANSPORT = 4, NODE_FLIGHTPATH = 5, - // Teleport-spell endpoint (hearthstone, mage portal). Emitted by - // TravelNodeRoute::BuildPath when traversing a teleportSpell-type - // edge; consumed by HandleSpecialMovement. - NODE_TELEPORT = 6, + // value 6 reserved (was NODE_TELEPORT — removed with teleportSpell) NODE_STATIC_PORTAL = 7 }; @@ -575,14 +549,6 @@ public: { nodes = nodes1; } - TravelNodeRoute(std::vector nodes1, - std::vector const& tempNodes_) - { - nodes = nodes1; - if (!tempNodes_.empty()) - addTempNodes(tempNodes_); - } - bool isEmpty() { return nodes.empty(); } bool hasNode(TravelNode* node) @@ -593,19 +559,6 @@ public: std::vector getNodes() { return nodes; } - // Take ownership of synthetic A* nodes (PortalNode etc.). Must call - // cleanTempNodes() to delete them when the route is no longer needed. - void addTempNodes(std::vector const& tempNodes_) - { - tempNodes.insert(tempNodes.end(), tempNodes_.begin(), tempNodes_.end()); - } - void cleanTempNodes() - { - for (auto* n : tempNodes) - delete n; - tempNodes.clear(); - } - TravelPath BuildPath( std::vector pathToStart = {}, std::vector pathToEnd = {}, @@ -619,7 +572,6 @@ private: return std::find(nodes.begin(), nodes.end(), node); } std::vector nodes; - std::vector tempNodes; // owned synthetic nodes (PortalNode etc.) }; // A node container to aid A* calculations with nodes.