Skip to content

Commit 33c8c3e

Browse files
committed
Add C_TaxiMap.GetTaxiRoute(slotIndex)
Exposes the client's computed flight route to a taxi-map slot as an array of TaxiNodes.dbc IDs in flight order { currentNodeID, ..., destNodeID } -- the exact chain TakeTaxiNode sends the server. Mirrors the TakeTaxiNode worker (FUN_004dc8d0): a single direct hop when a TaxiPath edge exists (via the FUN_004dbad0 adjacency matrix), otherwise the client's precomputed multi-hop route array (slot +0x14, length +0x18). This lets addons identify every hop of a per-character route without matching flight-map coordinates back to nodes. Offsets added: per-slot route array/count/flag, the current-node record pointer, and the node adjacency matrix. Documented in docs/API.md + README.
1 parent c5d5415 commit 33c8c3e

4 files changed

Lines changed: 125 additions & 1 deletion

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ Full per-function reference: **[docs/API.md](docs/API.md)**.
6363
| [State](docs/API.md#state) | `CancelShapeshiftForm`, `Dismount`, `GetMirrorTimerInfo`, `GetMirrorTimerProgress`, `GetShapeshiftFormID`, `IsAssistingRitual`, `IsFalling`, `IsInGroup`, `IsInRaid`, `IsLoggedIn`, `IsMounted`, `IsStealthed`, `IsSwimming` |
6464
| [Talent](docs/API.md#talent) | `GetTalentIDByIndex`, `GetTalentSpellID` |
6565
| [Targeting](docs/API.md#targeting) | `GetPlayerFacing`, `TargetDirectionEnemy`, `TargetDirectionFriend`, `TargetNearest`, `TargetNearestEnemyPlayer`, `TargetNearestFriendPlayer` |
66-
| [TaxiMap](docs/API.md#taximap) | `C_TaxiMap.GetTaxiNodesForMap`, `C_TaxiMap.GetAllTaxiNodes`, `C_TaxiMap.GetTaxiPaths`, `C_TaxiMap.GetTaxiPathWaypoints` |
66+
| [TaxiMap](docs/API.md#taximap) | `C_TaxiMap.GetTaxiNodesForMap`, `C_TaxiMap.GetAllTaxiNodes`, `C_TaxiMap.GetTaxiPaths`, `C_TaxiMap.GetTaxiPathWaypoints`, `C_TaxiMap.GetTaxiRoute` |
6767
| [Time](docs/API.md#time) | `C_DateAndTime.AdjustTimeByDays`, `C_DateAndTime.AdjustTimeByMinutes`, `C_DateAndTime.CompareCalendarTime`, `C_DateAndTime.GetCalendarTimeFromEpoch`, `C_DateAndTime.GetCurrentCalendarTime`, `C_DateAndTime.GetSecondsUntilDailyReset`, `C_DateAndTime.GetServerTimeLocal`, `C_Timer.After`, `C_Timer.NewTicker`, `C_Timer.NewTimer`, `GetServerTime` |
6868
| [Totem](docs/API.md#totem) | `GetTotemInfo`, `GetTotemTimeLeft`, `GetTotemDuration`, `TargetTotem` |
6969
| [TradeSkillUI](docs/API.md#tradeskillui) | `C_TradeSkillUI.GetTradeSkillListLink`, `C_TradeSkillUI.GetCraftListLink`, `C_TradeSkillUI.GetTradeSkillListRecipes` |

docs/API.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,7 @@ build instructions.
462462
- [`C_TaxiMap.GetAllTaxiNodes([uiMapID])`](#c_taximapgetalltaxinodesuimapid)
463463
- [`C_TaxiMap.GetTaxiPaths()`](#c_taximapgettaxipaths)
464464
- [`C_TaxiMap.GetTaxiPathWaypoints(pathID)`](#c_taximapgettaxipathwaypointspathid)
465+
- [`C_TaxiMap.GetTaxiRoute(slotIndex)`](#c_taximapgettaxiroutepslotindex)
465466

466467
- [Time](#time)
467468
- [`GetServerTime()`](#getservertime)
@@ -11232,6 +11233,30 @@ speed is 32 yd/s, so `length / 32 ≈ flight seconds` for a single hop.
1123211233
the corner at each intermediate flight master via data not present in any
1123311234
client file, so a client-side estimate of a chained route is an upper bound.)
1123411235

11236+
### `C_TaxiMap.GetTaxiRoute(slotIndex)`
11237+
11238+
Returns the node chain the client would actually fly to a taxi-map slot, as an
11239+
array of `TaxiNodes.dbc` IDs in flight order (`{ currentNodeID, …, destNodeID }`).
11240+
`slotIndex` is the legacy `1..NumTaxiNodes()` index (the same one
11241+
`TaxiNodeName`/`TakeTaxiNode` take). **Only meaningful while the taxi map is
11242+
open.**
11243+
11244+
```lua
11245+
local route = C_TaxiMap.GetTaxiRoute(slotIndex)
11246+
-- route = { 8, 16, 43, 66 } (Thelsamar -> Refuge Pointe -> Aerie Peak -> Chillwind)
11247+
```
11248+
11249+
This is the exact chain `TakeTaxiNode(slotIndex)` would send to the server —
11250+
mirrors the client's own routing decision: a single direct hop when a `TaxiPath`
11251+
edge exists (`{ current, dest }`), otherwise the client's precomputed multi-hop
11252+
route (which, per character, may detour around flight points you haven't
11253+
discovered). Returns nothing when the map is closed, the slot is out of range,
11254+
it's the current node, or no route exists.
11255+
11256+
Pairs with `GetTaxiPaths` (each consecutive `(from, to)` names a `pathID`) and
11257+
`GetTaxiPathWaypoints` (that path's geometry) to measure a chained trip without
11258+
matching flight-map pixel coordinates back to nodes.
11259+
1123511260
## Time
1123611261

1123711262
### `GetServerTime()`

src/Offsets.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2438,6 +2438,29 @@ enum Offsets {
24382438
// "DISTANT" / other) — the node's reachability for the open session.
24392439
FUN_TAXI_NODE_TYPE = 0x004DC4E0,
24402440

2441+
// Per-slot computed route (the chain that TakeTaxiNode sends to the
2442+
// server). Derived from FUN_004dc8d0 (the TakeTaxiNode worker) and the
2443+
// route builder FUN_004dbce0:
2444+
// +0x0C (byte) nonzero when the multi-hop route array below is built
2445+
// +0x14 (int*) array of TaxiNodes.dbc IDs, current->dest in flight
2446+
// order, length at +0x18
2447+
// +0x18 (int) route length (node count; hops = count - 1)
2448+
// A direct hop (a TaxiPath edge exists current->dest) is flown as just
2449+
// {current, dest} and does NOT populate +0x14 — see VAR_TAXI_ADJACENCY.
2450+
OFF_TAXI_SLOT_MULTIHOP = 0x0C,
2451+
OFF_TAXI_SLOT_ROUTE = 0x14,
2452+
OFF_TAXI_SLOT_ROUTE_COUNT = 0x18,
2453+
2454+
// Pointer to the CURRENT flight master's TaxiNodes.dbc record (id @ +0);
2455+
// the origin of every route. Used by FUN_004dc8d0 / FUN_004dbce0.
2456+
VAR_TAXI_CURRENT_REC = 0x00BB4A80,
2457+
2458+
// Node x node -> pathID adjacency matrix (256-wide rows, int cells; cell
2459+
// [from*0x100 + to] is the direct TaxiPath id, or 0 if none). Backs the
2460+
// direct-hop test FUN_004dbad0. IDs are 1..256.
2461+
VAR_TAXI_ADJACENCY = 0x00B7365C,
2462+
TAXI_ADJACENCY_DIM = 0x100,
2463+
24412464
// TaxiPath.dbc (4 fields, 0x10 bytes): {id, fromNode@+0x04, toNode@+0x08,
24422465
// cost}. A real flight master is a node some flight path connects to;
24432466
// Northshire Abbey / spurious duplicate rows / standalone markers are not

src/taxi/Map.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,80 @@ int __fastcall Script_GetAllTaxiNodes(void *L) {
366366
return 1;
367367
}
368368

369+
// Direct-edge test — mirrors FUN_004dbad0: a 256x256 [from][to] matrix of
370+
// TaxiPath IDs (0 = no direct flight path). IDs outside 1..256 are never direct.
371+
bool DirectEdge(int from, int to) {
372+
if (from <= 0 || from > Offsets::TAXI_ADJACENCY_DIM ||
373+
to <= 0 || to > Offsets::TAXI_ADJACENCY_DIM)
374+
return false;
375+
const int *adj = reinterpret_cast<const int *>(Offsets::VAR_TAXI_ADJACENCY);
376+
return adj[from * Offsets::TAXI_ADJACENCY_DIM + to] != 0;
377+
}
378+
379+
// `C_TaxiMap.GetTaxiRoute(slotIndex)` — the TaxiNodes.dbc ID chain the client
380+
// would fly to the taxi-map slot `slotIndex` (1..NumTaxiNodes), in flight
381+
// order { currentNodeID, ..., destNodeID }. This mirrors TakeTaxiNode's own
382+
// decision (FUN_004dc8d0): a direct hop when a TaxiPath edge exists, otherwise
383+
// the client's precomputed multi-hop route array. It is the exact node list
384+
// TakeTaxiNode would hand the server, so an addon can identify every hop
385+
// without matching flight-map coordinates back to nodes. Returns nothing when
386+
// no taxi map is open, the slot is out of range, it is the current node, or no
387+
// route exists.
388+
int __fastcall Script_GetTaxiRoute(void *L) {
389+
if (!Game::Lua::IsNumber(L, 1))
390+
return 0;
391+
const int slot1 = static_cast<int>(Game::Lua::ToNumber(L, 1));
392+
const int count =
393+
*reinterpret_cast<const int *>(Offsets::VAR_TAXI_NODE_COUNT);
394+
if (count <= 0 || slot1 < 1 || slot1 > count)
395+
return 0;
396+
397+
const auto *arrayBase =
398+
reinterpret_cast<const uint8_t *>(Offsets::VAR_TAXI_NODE_ARRAY);
399+
const uint8_t *slotRec =
400+
arrayBase + (slot1 - 1) * Offsets::TAXI_NODE_STRIDE;
401+
402+
const uint8_t *curRec = *reinterpret_cast<const uint8_t *const *>(
403+
Offsets::VAR_TAXI_CURRENT_REC);
404+
const uint8_t *dstRec = *reinterpret_cast<const uint8_t *const *>(
405+
slotRec + Offsets::OFF_TAXI_SLOT_RECORD);
406+
if (curRec == nullptr || dstRec == nullptr)
407+
return 0;
408+
const int currentID = IntField(curRec, 0);
409+
const int destID = IntField(dstRec, 0);
410+
if (currentID == 0 || destID == 0 || currentID == destID)
411+
return 0;
412+
413+
// Collect the chain exactly as FUN_004dc8d0 (TakeTaxiNode) would send it.
414+
int ids[64];
415+
int n = 0;
416+
if (DirectEdge(currentID, destID)) {
417+
ids[n++] = currentID;
418+
ids[n++] = destID;
419+
} else if (*(slotRec + Offsets::OFF_TAXI_SLOT_MULTIHOP) != 0) {
420+
const int *route = *reinterpret_cast<const int *const *>(
421+
slotRec + Offsets::OFF_TAXI_SLOT_ROUTE);
422+
const int rc = *reinterpret_cast<const int *>(
423+
slotRec + Offsets::OFF_TAXI_SLOT_ROUTE_COUNT);
424+
if (route == nullptr || rc < 2)
425+
return 0;
426+
const int cap = rc < 64 ? rc : 64;
427+
for (int i = 0; i < cap; ++i)
428+
ids[n++] = route[i];
429+
} else {
430+
return 0; // reachable node with no computed route (shouldn't happen)
431+
}
432+
433+
Game::Lua::SetTop(L, 0);
434+
Game::Lua::NewTable(L);
435+
for (int i = 0; i < n; ++i) {
436+
Game::Lua::PushNumber(L, static_cast<double>(i + 1));
437+
Game::Lua::PushNumber(L, static_cast<double>(ids[i]));
438+
Game::Lua::SetTable(L, -3);
439+
}
440+
return 1;
441+
}
442+
369443
void RegisterLuaFunctions() {
370444
static const Game::Lua::EnumIntegerEntry kFaction[] = {
371445
{"Neutral", FACTION_NEUTRAL},
@@ -385,6 +459,8 @@ void RegisterLuaFunctions() {
385459
&Script_GetTaxiNodesForMap);
386460
Game::Lua::RegisterTableFunction("C_TaxiMap", "GetAllTaxiNodes",
387461
&Script_GetAllTaxiNodes);
462+
Game::Lua::RegisterTableFunction("C_TaxiMap", "GetTaxiRoute",
463+
&Script_GetTaxiRoute);
388464
}
389465

390466
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};

0 commit comments

Comments
 (0)