compat: the uiMapID graph over the realm's own WorldMapArea, and C_Map's other half
1.12 addresses a map by a (continent, zone) pair of dropdown indices — a position in two lists, which moves when a zone is inserted — and publishes no map's rectangle on any other map at all, because its window draws one zone at a time into a fixed 1002x668 frame and never needs to know where that zone sits. A seamless, pannable map is exactly that missing relation. `script/uimap.rs` builds the tree as a pure function of the catalog `ui_world_map.rs` already pushes, rebuilt inside both setters so the graph and the catalog cannot disagree. No new DBC read, so nothing here can contradict what the stock map window itself draws. It is built from OctoWoW's own rows, which is the point: 175 against stock 1.12's 51, with 124 maps added and 49 shipped rects re-tuned. Era's uiMapIDs are reused where the map is the same map, joined on the WorldMapArea art folder rather than the zone name, because the folder is the column a realm has no reason to touch — this realm spells Azshara's `Aszhara` and Hillsbrad's `Hilsbrad` exactly as 1.12 does. A row Era has no counterpart for is minted from its own AreaTable id. One realm quirk is handled with its measurement: a third `areaID == 0` row whose art folder is `World` is the sheet's own row, which the reference's catalog walk files beside the two continents — `SetMapZoom(3, 0)` then `GetMapInfo()` answers `World`. Left in, it is a phantom continent carrying a second copy of all 39 Eastern Kingdoms zones. Art is answered as paths, so the chain serves the upscaled pack; the layer geometry is the logical 1002x668 / 256x256, because normalized fractions are invariant under a uniform upscale. The harness now seats the player a world position and the per-frame map feed, because every verb that answers *where* the player is reads the position, not the zone name. Corpus: 85 ok / 1 fail, roster and digest unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
@@ -458,6 +458,7 @@ a loot roll"; it does not, because that module never runs.** The call path was n
|
||||
| `C_LootHistory` (4 members) | not built, **unreachable** | Needs the three `LOOT_HISTORY_*` events as well as the namespace — the module's `FullUpdate` returns unless its frame is shown, and only those events show it. The data is on the wire and already parsed (`LootStartRoll`/`LootRoll`/`LootRollWon` carry the roll key, the roller, the dice value, the vote and the winner), so a real history is buildable: accumulate per `(looted_target, item_slot)`, resolve roller GUIDs to name/class, fire the three events. A feature worth doing on its own, not an error to patch |
|
||||
| `C_TaxiMap.GetTaxiNodesForMap` — **the flight-master layer** | done | `compat/classic_api/taxi_map.rs` over a new feed, `ui_taxi/map_nodes.rs`; supersedes this member in the "not built" row below, which stands for the rest of its list. The guard that row cites is exactly why it mattered: pfQuest *is* guarded, so the absence produced no error at all — `pfDatabase:SearchFlightNodes` returned an empty table, the user's "flight masters" option stayed ticked, and nothing was ever drawn. Same silent-by-construction shape as `C_Map.GetAreas` above, and the same consumer. This install's pfQuest has **deleted** its shipped `pfDB.meta.flight` table for this call (`db/meta.lua:181` is a stub whose comment says the data "always comes live from the client (ClassicAPI)"), so there is no fallback behind it. **The contract is half read off the DLL and half off the caller**, and the file says which is which: `C_TaxiMap`, `GetTaxiNodesForMap`, `nodeID`, `faction`, `reachable`, `state` and the `FlightPathFaction`/`FlightPathState` enum names are all in the bundled `ClassicAPI.dll`'s string pool; the `areaID`/`mapX`/`mapY` triple is **not** separately visible there (the pool holds one copy of each string and `C_Map.GetAreaTriggerInfo` already uses all three), so for those the authority is `SearchFlightNodes` itself — it reads all six fields, gates on `if show and node.areaID and node.mapX and node.mapY`, and feeds the pair into `pfMap:AddNode` as `meta.x`/`meta.y`, which `UpdateNodes` divides by 100, so they are **percentages of the zone rect**. Faction comes from the two `MountCreatureID` columns, which is the reference's own gate (the taxi window asks for the mount of the player's half, `0x4a2650`): one column filled is that faction, both is neutral. A row with **neither** is reported neutral rather than dropped, deliberately and with the reason in the code — OctoWoW ships custom flight points and a custom row with unfilled mount columns is still a flight master standing in the world, so a stray icon is the cheaper mistake than a missing feature; the seed logs that count so the choice stays visible. Zone and percentage come from `WorldMapUiData::zone_at`, which asks `map_proj::zone_uv` for containment rather than comparing rects itself, so a node is only ever attributed to a zone whose own map the window would place it inside; where rects nest (every city sits inside its parent zone) the **smallest** wins, because a flight master in Ironforge belongs on the Ironforge map, which is also the map the player is looking at while standing there. 1.12 has no verb that asks this question, so that choice is ours and is labelled as such. **Seeded before the interface loads**, beside the world-map catalog and after it (it reads those rects), and the deadline is tighter than file scope: pfQuest replays its saved tracking selections once on `PLAYER_ENTERING_WORLD` and then calls `this:UnregisterAllEvents()` (`database.lua:131-151`), so a push one frame later is a push after the only read and the layer stays empty for the session. **Not built, each for the same reason**: `GetTaxiRoute`, `GetTaxiPaths` and `GetTaxiPathWaypoints` are `TaxiPath`/`TaxiPathNode` data, which `ui_taxi/routing.rs` loads for the window's own route search and has never pushed into the VM, and nothing in the corpus calls them; `GetAllTaxiNodes` is in the pool with no caller to read its shape against, and guessing it would be inventing a contract rather than matching one. Also absent from each node, with the reason stated in the file: `reachable` and `state`, which are session facts the open window owns (`model.taxi`, which exists only while a flight master has the frame up) — answering `reachable = false` for all 60 nodes because no window is open would be a confident wrong answer, where an absent field is a truthful "this build does not say" |
|
||||
| `C_EncodingUtil` (8), `C_FriendList.SetFriendNotes`, `C_Item.GetCurrentItemLevel`, `C_Map.GetAreaTriggerInfo`, `C_SpellBook.GetSkillLineRank`, `C_TaxiMap.GetTaxiNodesForMap`, `C_Spell.IsSpellInRange`/`IsNextMeleeSpell`/`IsRangedAutoAttackSpell`/`ResetsMeleeSwing`, `C_Item.RequestLoadItemDataByID`, `C_LootHistory.*` | not built | Each either guarded by its caller (all of pfQuest's) or behind a user action not yet taken: `SetFriendNotes` on editing a friend note, `SerializeJSON` on sharing a config, `GetCurrentItemLevel` behind `if ShaguScore` (an addon not installed), the hunter range pair on a hunter's bar. None raises in ordinary play as installed |
|
||||
| `C_Map`'s uiMapID half (14 members), `C_MapExplorationInfo`, `Enum.UIMapType`, `CreateVector2D`, `UiMapPoint` — **the map graph** | done | `script/uimap.rs` builds the graph, `compat/classic_api/uimap.rs` publishes it; supersedes the "nine waypoint members, the layered-art trio, the four-member projection family" clause of the `C_Map.GetAreas`/`GetAreaTriggerInfo` row above. **What 1.12 withholds is not a getter, it is the tree.** Its map is addressed by a `(continent, zone)` pair of *dropdown indices* — a position in two lists, which moves when a zone is inserted — and it publishes no map's rectangle on any other map at all, because the window draws one zone at a time into a fixed 1002x668 frame and never needs to know where that zone sits. A seamless, pannable map is exactly that missing relation: one rectangle per map composed up to a common root. **Built from the realm's own catalog, which is the point.** OctoWoW's `WorldMapArea.dbc` carries 175 rows against stock 1.12's 51 — 124 added (`Alah'Thalas`, `Balor`, `Blackstone Isle`, `Gilneas`, `Grim Reaches`, `Icepoint`, `Lapidis Isle`, `Moonwhisper`, `Northwind`, `Tel'Abim`, `Thalassian Highlands`, the Timbermaw tunnels, Hyjal) and 49 of the shipped rows' world rects re-tuned, measured with `dbc_diff`. A graph transcribed from Classic Era would be the right shape describing a different world. No new DBC read: the graph is a pure function of the catalog `ui_world_map.rs` already pushes, rebuilt inside `set_world_map_catalog`/`set_world_map_direct_areas` so the two cannot disagree. **Era's ids are reused where the map is the same map**, keyed on the `WorldMapArea` **art folder** rather than the zone name, because the folder is the column a realm has no reason to touch — this realm spells Azshara's `Aszhara` and Hillsbrad's `Hilsbrad`, exactly as 1.12 does, while its localized names are the ordinary ones. That join is what lets an Era addon's own uiMapID-keyed data (level ranges, colour grading, flight paths) find this realm's zones. The ids are **read off the consumer's own data files**, which carry the zone name in a comment beside each (`Data/Levels.lua`, `Data/Grade.lua`, `Data/Tones.lua` give the 40 outdoor zones) plus its source constants for the sheet (`WORLD = 947`), the two continents (`CONT_OF = { [1415] = 0, [1414] = 1 }`) and three cities; the other three cities are the gap that contiguous block leaves. A row with no Era counterpart is minted at `100000 + areaID` — unique because `AreaTable` ids are, stable because the id is the realm's own, and clear of anything Era can add. **One realm quirk had to be handled and it is measurable:** this install's `WorldMapArea.dbc` carries a *third* `areaID == 0` row whose art folder is `World` — the world sheet's own row — and the reference's catalog walk (`0x4a5d00`, which selects continents by exactly that column) files it beside Kalimdor and Azeroth. `SetMapZoom(3, 0)` then `GetMapInfo()` answers `World`, and because the row carries `Map.dbc` id 0 its zone list is Eastern Kingdoms' own re-collected, so left in it is a phantom continent holding a second copy of all 39 Eastern Kingdoms zones. It is kept out of the graph rather than merged into the sheet, because its `loc` rect is in map 0's coordinate space while the sheet spans both continents. **Art is answered as paths, not fileIDs**: `GetMapArtLayerTextures` hands back `Interface\WorldMap\<folder>\<folder>N`, so the asset chain serves whichever copy wins — on this install the upscaled pack, `patch-M.mpq`, three copies deep. The 1002x668 / 256x256 layer geometry is the **logical** size, not the shipped pixel size, because the drawn fractions are normalized and a uniform upscale leaves them unchanged; that is the same invariance `map.rs`'s `art_scale` has to *correct* for, and only because `GetMapOverlays` publishes absolute pixels. `C_MapExplorationInfo.GetExploredMapTextures` is the same `WorldMapOverlay` rows and the same reveal gate the stock window draws (`WorldMapFrame.lua:107-160`), in the shape a canvas reads. **Not built, listed rather than stubbed**: `GetMapPosFromWorldPos` (an inverse search over every rect, no caller), `GetMapGroupID`/`GetMapLinksForMap`/`GetBountySetIDForMap` (about maps this client has no concept of) and `C_MapExplorationInfo.GetExploredAreaIDsAtPosition`. A member answering a plausible-looking nothing is worse than an absent one, which `if C_Map.X then` can see. **One harness gap closed with it**: `seat_a_session` now seats the player a world *position* and the per-frame map feed the live client pushes every frame, because every verb that answers *where* the player is reads the position rather than the zone name — with none answering, a map addon draws its art and no player arrow, which the survey's "drew something" column cannot tell from drawing nothing. Corpus roster unchanged by both: 85 ok / 1 fail, digest identical |
|
||||
|
||||
|
||||
## The offline UI-load harness — why 349 tests were failing, and what is fixed
|
||||
|
||||
Reference in New Issue
Block a user