mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 23:49:25 +02:00
feat(Core/Travel): Re-enable area-trigger, static-portal, and teleport-spell nodes
This commit is contained in:
parent
ed9e7227fb
commit
a0e21d9f38
@ -3508,6 +3508,141 @@ bool MovementAction::ExecuteTravelPlan(TravelPlan& state)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case PathNodeType::NODE_AREA_TRIGGER:
|
||||||
|
{
|
||||||
|
// Pair: trigger (pointIdx) + dest (pointIdx+1).
|
||||||
|
// Bot walks into the area trigger volume; server teleports
|
||||||
|
// on entry. Bot may need quest/key prereqs to actually cross.
|
||||||
|
if (state.stepIdx + 1 >= state.steps.size())
|
||||||
|
{
|
||||||
|
state.Reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PathNodePoint& trigger = state.steps[state.stepIdx];
|
||||||
|
const PathNodePoint& dst = state.steps[state.stepIdx + 1];
|
||||||
|
|
||||||
|
// Already on destination map — trigger fired, advance.
|
||||||
|
if (bot->GetMapId() == dst.point.GetMapId())
|
||||||
|
{
|
||||||
|
state.stepIdx += 2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk to the trigger position; collision with the trigger
|
||||||
|
// volume 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 quest/key.
|
||||||
|
// Abort; the do-quest yield-to-grind multiplier or next
|
||||||
|
// POI pick can reroute.
|
||||||
|
state.Reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PathNodeType::NODE_STATIC_PORTAL:
|
||||||
|
{
|
||||||
|
// Pair: portal-GO position (pointIdx) + dest (pointIdx+1).
|
||||||
|
// Bot walks within interact range of the portal GameObject
|
||||||
|
// and sends CMSG_GAMEOBJ_USE to trigger its teleport spell.
|
||||||
|
if (state.stepIdx + 1 >= state.steps.size())
|
||||||
|
{
|
||||||
|
state.Reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PathNodePoint& portal = state.steps[state.stepIdx];
|
||||||
|
const PathNodePoint& dst = state.steps[state.stepIdx + 1];
|
||||||
|
|
||||||
|
if (bot->GetMapId() == dst.point.GetMapId())
|
||||||
|
{
|
||||||
|
state.stepIdx += 2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk to portal GO position
|
||||||
|
float dist = bot->GetExactDist(portal.point.GetPositionX(),
|
||||||
|
portal.point.GetPositionY(),
|
||||||
|
portal.point.GetPositionZ());
|
||||||
|
if (dist > INTERACTION_DISTANCE)
|
||||||
|
return MoveTo(portal.point.GetMapId(),
|
||||||
|
portal.point.GetPositionX(),
|
||||||
|
portal.point.GetPositionY(),
|
||||||
|
portal.point.GetPositionZ());
|
||||||
|
|
||||||
|
// In range — find the portal GameObject and interact
|
||||||
|
if (!portal.entry)
|
||||||
|
{
|
||||||
|
state.Reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->IsMounted())
|
||||||
|
bot->Dismount();
|
||||||
|
botAI->RemoveShapeshift();
|
||||||
|
|
||||||
|
GuidVector nearGOs = AI_VALUE(GuidVector, "nearest game objects");
|
||||||
|
for (ObjectGuid const& guid : nearGOs)
|
||||||
|
{
|
||||||
|
GameObject* go = botAI->GetGameObject(guid);
|
||||||
|
if (!go || go->GetEntry() != portal.entry)
|
||||||
|
continue;
|
||||||
|
if (!bot->GetGameObjectIfCanInteractWith(guid, MAX_GAMEOBJECT_TYPE))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
WorldPacket packet(CMSG_GAMEOBJ_USE);
|
||||||
|
packet << guid;
|
||||||
|
bot->GetSession()->QueuePacket(new WorldPacket(packet));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GO not found nearby — abort and let next tick try again
|
||||||
|
state.Reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PathNodeType::NODE_TELEPORT:
|
||||||
|
{
|
||||||
|
// Teleport-spell node: hearthstone (spell 8690) or class
|
||||||
|
// teleport spells (mage/druid). `entry` holds the spell ID.
|
||||||
|
uint32 spellId = pt.entry;
|
||||||
|
if (!spellId)
|
||||||
|
{
|
||||||
|
state.Reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->IsInFlight() || bot->IsNonMeleeSpellCast(false))
|
||||||
|
return true; // wait
|
||||||
|
|
||||||
|
if (bot->IsMounted())
|
||||||
|
bot->Dismount();
|
||||||
|
botAI->RemoveShapeshift();
|
||||||
|
|
||||||
|
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.
|
||||||
|
state.stepIdx++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
case PathNodeType::NODE_TRANSPORT:
|
case PathNodeType::NODE_TRANSPORT:
|
||||||
{
|
{
|
||||||
if (state.stepIdx + 1 >= state.steps.size())
|
if (state.stepIdx + 1 >= state.steps.size())
|
||||||
|
|||||||
@ -875,7 +875,17 @@ TravelPath TravelNodeRoute::BuildPath(std::vector<WorldPosition> pathToStart, st
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodePath->getPathType() == TravelNodePathType::transport)
|
if (nodePath->getPathType() == TravelNodePathType::areaTrigger)
|
||||||
|
{
|
||||||
|
travelPath.addPoint(*prevNode->getPosition(), PathNodeType::NODE_AREA_TRIGGER, nodePath->getPathObject());
|
||||||
|
travelPath.addPoint(*node->getPosition(), PathNodeType::NODE_AREA_TRIGGER, nodePath->getPathObject());
|
||||||
|
}
|
||||||
|
else if (nodePath->getPathType() == TravelNodePathType::staticPortal)
|
||||||
|
{
|
||||||
|
travelPath.addPoint(*prevNode->getPosition(), PathNodeType::NODE_STATIC_PORTAL, nodePath->getPathObject());
|
||||||
|
travelPath.addPoint(*node->getPosition(), PathNodeType::NODE_STATIC_PORTAL, nodePath->getPathObject());
|
||||||
|
}
|
||||||
|
else if (nodePath->getPathType() == TravelNodePathType::transport)
|
||||||
{
|
{
|
||||||
// Emit the transport's full waypoint route, not just board+exit.
|
// Emit the transport's full waypoint route, not just board+exit.
|
||||||
// Intermediate points carry NODE_TRANSPORT type so the executor
|
// Intermediate points carry NODE_TRANSPORT type so the executor
|
||||||
@ -888,6 +898,11 @@ TravelPath TravelNodeRoute::BuildPath(std::vector<WorldPosition> pathToStart, st
|
|||||||
// Full taxi waypoint route; same reasoning as transport.
|
// Full taxi waypoint route; same reasoning as transport.
|
||||||
travelPath.addPath(nodePath->GetPath(), PathNodeType::NODE_FLIGHTPATH, nodePath->getPathObject());
|
travelPath.addPath(nodePath->GetPath(), PathNodeType::NODE_FLIGHTPATH, nodePath->getPathObject());
|
||||||
}
|
}
|
||||||
|
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
|
else
|
||||||
{
|
{
|
||||||
std::vector<WorldPosition> path = nodePath->GetPath();
|
std::vector<WorldPosition> path = nodePath->GetPath();
|
||||||
@ -896,6 +911,11 @@ 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.
|
node != nodes.back()) // Remove the last point since that will also be the start of the next path.
|
||||||
path.pop_back();
|
path.pop_back();
|
||||||
|
|
||||||
|
if (path.size() > 1 && prevNode->isPortal() &&
|
||||||
|
nodePath->getPathType() != TravelNodePathType::areaTrigger &&
|
||||||
|
nodePath->getPathType() != TravelNodePathType::staticPortal)
|
||||||
|
path.erase(path.begin());
|
||||||
|
|
||||||
if (path.size() > 1 && prevNode->isTransport() &&
|
if (path.size() > 1 && prevNode->isTransport() &&
|
||||||
nodePath->getPathType() != TravelNodePathType::transport)
|
nodePath->getPathType() != TravelNodePathType::transport)
|
||||||
path.erase(path.begin());
|
path.erase(path.begin());
|
||||||
@ -1558,6 +1578,63 @@ 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 + area-trigger link
|
||||||
|
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* outNode = TravelNodeMap::instance().addNode(outPos, nodeName, true, true);
|
||||||
|
TravelNode* inNode = TravelNodeMap::instance().getNode(inPos, nullptr, 5.0f);
|
||||||
|
|
||||||
|
if (outNode && inNode)
|
||||||
|
{
|
||||||
|
TravelNodePath travelPath(0.1f, 3.0f, (uint8)TravelNodePathType::areaTrigger, itr.first, true);
|
||||||
|
travelPath.setPath({*inNode->getPosition(), *outNode->getPosition()});
|
||||||
|
inNode->setPathTo(outNode, travelPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TravelNodeMap::generateTransportNodes()
|
void TravelNodeMap::generateTransportNodes()
|
||||||
{
|
{
|
||||||
for (auto const& itr : *sObjectMgr->GetGameObjectTemplates())
|
for (auto const& itr : *sObjectMgr->GetGameObjectTemplates())
|
||||||
@ -1661,6 +1738,8 @@ void TravelNodeMap::generateNodes()
|
|||||||
generateStartNodes();
|
generateStartNodes();
|
||||||
LOG_INFO("playerbots", "-Generating npc nodes");
|
LOG_INFO("playerbots", "-Generating npc nodes");
|
||||||
generateNpcNodes();
|
generateNpcNodes();
|
||||||
|
LOG_INFO("playerbots", "-Generating area trigger nodes");
|
||||||
|
generateAreaTriggerNodes();
|
||||||
LOG_INFO("playerbots", "-Generating transport nodes");
|
LOG_INFO("playerbots", "-Generating transport nodes");
|
||||||
generateTransportNodes();
|
generateTransportNodes();
|
||||||
LOG_INFO("playerbots", "-Generating zone mean nodes");
|
LOG_INFO("playerbots", "-Generating zone mean nodes");
|
||||||
|
|||||||
@ -90,10 +90,11 @@ enum class TravelNodePathType : uint8
|
|||||||
{
|
{
|
||||||
none = 0,
|
none = 0,
|
||||||
walk = 1,
|
walk = 1,
|
||||||
// values 2 (areaTrigger), 5 (teleportSpell), 6 (staticPortal)
|
areaTrigger = 2,
|
||||||
// reserved for future use — generation/execution not yet wired up.
|
|
||||||
transport = 3,
|
transport = 3,
|
||||||
flightPath = 4
|
flightPath = 4,
|
||||||
|
teleportSpell = 5,
|
||||||
|
staticPortal = 6
|
||||||
};
|
};
|
||||||
|
|
||||||
// A connection between two nodes.
|
// A connection between two nodes.
|
||||||
@ -261,9 +262,14 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Portal-style link types (areaTrigger, staticPortal) are not
|
bool isPortal()
|
||||||
// generated in this PR. Stub retained so consumers compile.
|
{
|
||||||
bool isPortal() { return false; }
|
for (auto const& link : *getLinks())
|
||||||
|
if (link.second->getPathType() == TravelNodePathType::areaTrigger ||
|
||||||
|
link.second->getPathType() == TravelNodePathType::staticPortal)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool isWalking()
|
bool isWalking()
|
||||||
{
|
{
|
||||||
@ -405,10 +411,11 @@ enum class PathNodeType : uint8
|
|||||||
NODE_PREPATH = 0,
|
NODE_PREPATH = 0,
|
||||||
NODE_PATH = 1,
|
NODE_PATH = 1,
|
||||||
NODE_NODE = 2,
|
NODE_NODE = 2,
|
||||||
// values 3 (NODE_AREA_TRIGGER), 6 (NODE_TELEPORT), 7 (NODE_STATIC_PORTAL)
|
NODE_AREA_TRIGGER = 3,
|
||||||
// reserved for future use — handlers not yet wired up.
|
|
||||||
NODE_TRANSPORT = 4,
|
NODE_TRANSPORT = 4,
|
||||||
NODE_FLIGHTPATH = 5
|
NODE_FLIGHTPATH = 5,
|
||||||
|
NODE_TELEPORT = 6,
|
||||||
|
NODE_STATIC_PORTAL = 7
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PathNodePoint
|
struct PathNodePoint
|
||||||
@ -662,6 +669,7 @@ public:
|
|||||||
|
|
||||||
void generateNpcNodes();
|
void generateNpcNodes();
|
||||||
void generateStartNodes();
|
void generateStartNodes();
|
||||||
|
void generateAreaTriggerNodes();
|
||||||
void generateNodes();
|
void generateNodes();
|
||||||
void generateTransportNodes();
|
void generateTransportNodes();
|
||||||
void generateZoneMeanNodes();
|
void generateZoneMeanNodes();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user