mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
feat(Core/Travel): Exclude area-trigger, static-portal, teleport-spell path types from PR
This commit is contained in:
parent
905f550ca1
commit
c0e41e6ce1
@ -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",
|
||||
|
||||
@ -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<WorldPosition> 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<WorldPosition> 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");
|
||||
|
||||
@ -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();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user