From c0e41e6ce1ea470b8f2fc952de3f4bf89a81238a Mon Sep 17 00:00:00 2001 From: bash Date: Sat, 30 May 2026 18:57:42 +0200 Subject: [PATCH] feat(Core/Travel): Exclude area-trigger, static-portal, teleport-spell path types from PR --- src/Ai/Base/Actions/MovementActions.cpp | 116 ---------------------- src/Mgr/Travel/TravelNode.cpp | 123 +++--------------------- src/Mgr/Travel/TravelNode.h | 32 ++---- 3 files changed, 21 insertions(+), 250 deletions(-) diff --git a/src/Ai/Base/Actions/MovementActions.cpp b/src/Ai/Base/Actions/MovementActions.cpp index 5c238ee86..e9d0f0f8b 100644 --- a/src/Ai/Base/Actions/MovementActions.cpp +++ b/src/Ai/Base/Actions/MovementActions.cpp @@ -3508,36 +3508,6 @@ bool MovementAction::ExecuteTravelPlan(TravelPlan& state) return true; } - case PathNodeType::NODE_PORTAL: - { - // Pair: source (pointIdx) + dest (pointIdx+1) - if (state.stepIdx + 1 >= state.steps.size()) - { - state.Reset(); - return false; - } - - const PathNodePoint& src = state.steps[state.stepIdx]; - const PathNodePoint& dst = state.steps[state.stepIdx + 1]; - - // Already on destination map? - if (bot->GetMapId() == dst.point.GetMapId()) - { - state.stepIdx += 2; - return true; - } - // Walk to portal source - float dist = bot->GetExactDist(src.point.GetPositionX(), src.point.GetPositionY(), src.point.GetPositionZ()); - if (dist > INTERACTION_DISTANCE) - return MoveTo(src.point.GetMapId(), src.point.GetPositionX(), src.point.GetPositionY(), src.point.GetPositionZ()); - - // At portal but didn't cross — natural collision missed. - // Abort the plan; stuck-recovery in MoveFarTo will decide - // whether to retry or teleport the bot. - state.Reset(); - return false; - } - case PathNodeType::NODE_TRANSPORT: { if (state.stepIdx + 1 >= state.steps.size()) @@ -3657,92 +3627,6 @@ bool MovementAction::ExecuteTravelPlan(TravelPlan& state) return true; } - case PathNodeType::NODE_TELEPORT: - { - // Teleport-spell node: hearthstone (item 6948 → spell 8690) - // or class teleport spells (mage portals, druid teleport). - // entry holds the spell ID; 8690 is the canonical hearthstone. - uint32 spellId = pt.entry; - if (!spellId) - { - state.Reset(); - return false; - } - - // Can't cast mid-flight or mid-cast; bail and retry next tick. - if (bot->IsInFlight() || bot->IsNonMeleeSpellCast(false)) - return true; - - if (bot->IsMounted()) - bot->Dismount(); - botAI->RemoveShapeshift(); - - // 8690 is Hearthstone — the AI has a dedicated action for it - // that handles cooldown and inventory checks. Other teleport - // spells go through the generic cast path. - bool cast = false; - if (spellId == 8690) - cast = botAI->DoSpecificAction("hearthstone", Event(), true); - else if (bot->HasSpell(spellId) && !bot->HasSpellCooldown(spellId)) - cast = botAI->CastSpell(spellId, bot); - - if (!cast) - { - state.Reset(); - return false; - } - - // Cast started — advance past the teleport step; the spell - // will move the bot, next tick picks up from wherever it lands. - state.stepIdx++; - return true; - } - - case PathNodeType::NODE_AREA_TRIGGER: - { - // Walk into an area trigger, server handles teleport on entry. - // Pair: trigger (pointIdx) + dest (pointIdx+1). - if (state.stepIdx + 1 >= state.steps.size()) - { - state.Reset(); - return false; - } - - const PathNodePoint& trigger = state.steps[state.stepIdx]; - const PathNodePoint& dest = state.steps[state.stepIdx + 1]; - - // Already on destination map — area trigger fired, advance. - if (bot->GetMapId() == dest.point.GetMapId()) - { - state.stepIdx += 2; - return true; - } - - // Walk to the trigger; entering its radius teleports us. - float dist = bot->GetExactDist(trigger.point.GetPositionX(), - trigger.point.GetPositionY(), - trigger.point.GetPositionZ()); - if (dist > INTERACTION_DISTANCE) - return MoveTo(trigger.point.GetMapId(), - trigger.point.GetPositionX(), - trigger.point.GetPositionY(), - trigger.point.GetPositionZ()); - - // At trigger but didn't teleport — likely missing required - // quest/key. Abort; stuck-recovery in MoveFarTo decides next. - state.Reset(); - return false; - } - - case PathNodeType::NODE_FLYING_MOUNT: - { - // Flying-mount node not implemented — abort. The graph - // generator produces these but their execution is - // server-specific; we treat them as unreachable rather - // than papering over with a teleport. - state.Reset(); - return false; - } default: { LOG_ERROR("playerbots", diff --git a/src/Mgr/Travel/TravelNode.cpp b/src/Mgr/Travel/TravelNode.cpp index 01ecea09b..1bf46fb45 100644 --- a/src/Mgr/Travel/TravelNode.cpp +++ b/src/Mgr/Travel/TravelNode.cpp @@ -161,18 +161,8 @@ float TravelNodePath::getCost(Player* bot, uint32 cGold) if (factionAnnoyance > 0) modifier += 0.3 * factionAnnoyance; // For each level the whole path takes 10% longer. } - if (getPathType() == TravelNodePathType::flyingMount) - { - if (!bot->IsAlive() || bot->GetLevel() < 70 || !bot->CanFly()) - return -1.0f; - - float flySpeed = bot->GetSpeed(MOVE_FLIGHT); - if (flySpeed < 1.0f) - flySpeed = 20.0f; // 280% base flying speed fallback - return (distance / flySpeed) * modifier; - } } - else if (getPathType() == TravelNodePathType::flightPath || getPathType() == TravelNodePathType::flyingMount) + else if (getPathType() == TravelNodePathType::flightPath) return -1.0f; if (getPathType() != TravelNodePathType::walk) @@ -885,33 +875,18 @@ TravelPath TravelNodeRoute::BuildPath(std::vector pathToStart, st continue; } - if (nodePath->getPathType() == TravelNodePathType::portal || - nodePath->getPathType() == TravelNodePathType::staticPortal) // Teleport to next node. + if (nodePath->getPathType() == TravelNodePathType::transport) { - travelPath.addPoint(*prevNode->getPosition(), PathNodeType::NODE_PORTAL, nodePath->getPathObject()); // Entry point - travelPath.addPoint(*node->getPosition(), PathNodeType::NODE_PORTAL, nodePath->getPathObject()); // Exit point + // Emit the transport's full waypoint route, not just board+exit. + // Intermediate points carry NODE_TRANSPORT type so the executor + // sees consecutive transport waypoints as one block (board at + // first, disembark at last). + travelPath.addPath(nodePath->GetPath(), PathNodeType::NODE_TRANSPORT, nodePath->getPathObject()); } - else if (nodePath->getPathType() == TravelNodePathType::transport) // Move onto transport + else if (nodePath->getPathType() == TravelNodePathType::flightPath) { - travelPath.addPoint(*prevNode->getPosition(), PathNodeType::NODE_TRANSPORT, - nodePath->getPathObject()); // Departure point - travelPath.addPoint(*node->getPosition(), PathNodeType::NODE_TRANSPORT, nodePath->getPathObject()); // Arrival point - } - else if (nodePath->getPathType() == TravelNodePathType::flightPath) // Use the flightpath - { - travelPath.addPoint(*prevNode->getPosition(), PathNodeType::NODE_FLIGHTPATH, - nodePath->getPathObject()); // Departure point - travelPath.addPoint(*node->getPosition(), PathNodeType::NODE_FLIGHTPATH, nodePath->getPathObject()); // Arrival point - } - else if (nodePath->getPathType() == TravelNodePathType::teleportSpell) - { - travelPath.addPoint(*prevNode->getPosition(), PathNodeType::NODE_TELEPORT, nodePath->getPathObject()); - travelPath.addPoint(*node->getPosition(), PathNodeType::NODE_TELEPORT, nodePath->getPathObject()); - } - else if (nodePath->getPathType() == TravelNodePathType::flyingMount) - { - travelPath.addPoint(*prevNode->getPosition(), PathNodeType::NODE_FLYING_MOUNT, 0); - travelPath.addPoint(*node->getPosition(), PathNodeType::NODE_FLYING_MOUNT, 0); + // Full taxi waypoint route; same reasoning as transport. + travelPath.addPath(nodePath->GetPath(), PathNodeType::NODE_FLIGHTPATH, nodePath->getPathObject()); } else { @@ -921,15 +896,8 @@ TravelPath TravelNodeRoute::BuildPath(std::vector pathToStart, st node != nodes.back()) // Remove the last point since that will also be the start of the next path. path.pop_back(); - if (path.size() > 1 && prevNode->isPortal() && - nodePath->getPathType() != TravelNodePathType::portal && - nodePath->getPathType() != TravelNodePathType::staticPortal) // Do not move to the area trigger if we - // don't plan to take the portal. - path.erase(path.begin()); - if (path.size() > 1 && prevNode->isTransport() && - nodePath->getPathType() != - TravelNodePathType::transport) // Do not move to the transport if we aren't going to take it. + nodePath->getPathType() != TravelNodePathType::transport) path.erase(path.begin()); travelPath.addPath(path, PathNodeType::NODE_PATH); @@ -1504,73 +1472,6 @@ void TravelNodeMap::generateStartNodes() } } -void TravelNodeMap::generateAreaTriggerNodes() -{ - // Entrance nodes - - for (auto const& itr : sObjectMgr->GetAllAreaTriggerTeleports()) - { - AreaTriggerTeleport const& atEntry = itr.second; - - AreaTrigger const* at = sObjectMgr->GetAreaTrigger(itr.first); - if (!at) - continue; - - WorldPosition inPos = WorldPosition(at->map, at->x, at->y, at->z, at->orientation); - WorldPosition outPos = WorldPosition(atEntry.target_mapId, atEntry.target_X, atEntry.target_Y, atEntry.target_Z, - atEntry.target_Orientation); - - std::string nodeName; - - if (!outPos.isOverworld()) - nodeName = outPos.getAreaName(false) + " entrance"; - else if (!inPos.isOverworld()) - nodeName = inPos.getAreaName(false) + " exit"; - else - nodeName = inPos.getAreaName(false) + " portal"; - - TravelNodeMap::instance().addNode(inPos, nodeName, true, true); - } - - // Exit nodes - - for (auto const& itr : sObjectMgr->GetAllAreaTriggerTeleports()) - { - AreaTriggerTeleport const& atEntry = itr.second; - - AreaTrigger const* at = sObjectMgr->GetAreaTrigger(itr.first); - if (!at) - continue; - - WorldPosition inPos = WorldPosition(at->map, at->x, at->y, at->z, at->orientation); - WorldPosition outPos = WorldPosition(atEntry.target_mapId, atEntry.target_X, atEntry.target_Y, atEntry.target_Z, - atEntry.target_Orientation); - - std::string nodeName; - - if (!outPos.isOverworld()) - nodeName = outPos.getAreaName(false) + " entrance"; - else if (!inPos.isOverworld()) - nodeName = inPos.getAreaName(false) + " exit"; - else - nodeName = inPos.getAreaName(false) + " portal"; - - //TravelNode* entryNode = TravelNodeMap::instance().getNode(outPos, nullptr, 20.0f); // Entry side, portal exit. //not used, line marked for removal. - - TravelNode* outNode = TravelNodeMap::instance().addNode(outPos, nodeName, true, true); // Exit size, portal exit. - - TravelNode* inNode = TravelNodeMap::instance().getNode(inPos, nullptr, 5.0f); // Entry side, portal center. - - // Portal link from area trigger to area trigger destination. - if (outNode && inNode) - { - TravelNodePath travelPath(0.1f, 3.0f, (uint8)TravelNodePathType::portal, itr.first, true); - travelPath.setPath({*inNode->getPosition(), *outNode->getPosition()}); - inNode->setPathTo(outNode, travelPath); - } - } -} - void TravelNodeMap::generateTransportNodes() { for (auto const& itr : *sObjectMgr->GetGameObjectTemplates()) @@ -1674,8 +1575,6 @@ void TravelNodeMap::generateNodes() generateStartNodes(); LOG_INFO("playerbots", "-Generating npc nodes"); generateNpcNodes(); - LOG_INFO("playerbots", "-Generating area trigger nodes"); - generateAreaTriggerNodes(); LOG_INFO("playerbots", "-Generating transport nodes"); generateTransportNodes(); LOG_INFO("playerbots", "-Generating zone mean nodes"); diff --git a/src/Mgr/Travel/TravelNode.h b/src/Mgr/Travel/TravelNode.h index 8ac99ced4..2b02756d7 100644 --- a/src/Mgr/Travel/TravelNode.h +++ b/src/Mgr/Travel/TravelNode.h @@ -39,12 +39,11 @@ // // Edge types (TravelNodePathType): // walk(1) — Walk via navmesh waypoints (stored in DB) -// portal(2) — AreaTrigger teleport (auto-discovered at startup) +// areaTrigger(2) — AreaTrigger teleport (auto-discovered at startup) // transport(3) — Boat/zeppelin (auto-discovered from MO_TRANSPORT) // flightPath(4) — Taxi flight between flight masters // teleportSpell(5) — Spell-based teleport (e.g. mage portals) // staticPortal(6) — Manually defined teleport link (DB only, not pruned by generation) -// flyingMount (7) — Use Bots Flying mount to travel (Not currently enabled) // // On server start saved nodes and links are loaded via TravelNodeMap::Init(). An index of nodes by zone is prepared // (instead of scanning all ~4000 nodes), precomputes connected components for O(1) reachability checks, and builds @@ -91,12 +90,10 @@ enum class TravelNodePathType : uint8 { none = 0, walk = 1, - portal = 2, + // values 2 (areaTrigger), 5 (teleportSpell), 6 (staticPortal) + // reserved for future use — generation/execution not yet wired up. transport = 3, - flightPath = 4, - teleportSpell = 5, - staticPortal = 6, - flyingMount = 7 + flightPath = 4 }; // A connection between two nodes. @@ -264,15 +261,9 @@ public: return false; } - bool isPortal() - { - for (auto const& link : *getLinks()) - if (link.second->getPathType() == TravelNodePathType::portal || - link.second->getPathType() == TravelNodePathType::staticPortal) - return true; - - return false; - } + // Portal-style link types (areaTrigger, staticPortal) are not + // generated in this PR. Stub retained so consumers compile. + bool isPortal() { return false; } bool isWalking() { @@ -414,12 +405,10 @@ enum class PathNodeType : uint8 NODE_PREPATH = 0, NODE_PATH = 1, NODE_NODE = 2, - NODE_PORTAL = 3, + // values 3 (NODE_AREA_TRIGGER), 6 (NODE_TELEPORT), 7 (NODE_STATIC_PORTAL) + // reserved for future use — handlers not yet wired up. NODE_TRANSPORT = 4, - NODE_FLIGHTPATH = 5, - NODE_TELEPORT = 6, - NODE_FLYING_MOUNT = 7, - NODE_AREA_TRIGGER = 8 + NODE_FLIGHTPATH = 5 }; struct PathNodePoint @@ -673,7 +662,6 @@ public: void generateNpcNodes(); void generateStartNodes(); - void generateAreaTriggerNodes(); void generateNodes(); void generateTransportNodes(); void generateZoneMeanNodes();