Skip to content

Commit 9e51141

Browse files
committed
map: add C_Map.GetMapWorldSize; extract shared Map::Area resolver
Backport the retail C_Map.GetMapWorldSize (9.0): return a zone's world size in yards as width, height, read from the WorldMapArea.dbc loc rect (width = locLeft - locRight east-west, height = locTop - locBottom north-south). Takes an AreaTable areaID, or defaults to the world map's current view like GetMapOverlays. Verified: (85) -> 4518.75, 3012.5. Extract the two WorldMapArea row resolvers (areaID -> row, current view -> row) out of map/Overlays.cpp into a shared Map::Area helper; both GetMapOverlays and GetMapWorldSize key off the same rows, so the two ways of naming a zone now live in one place.
1 parent 6be25d7 commit 9e51141

6 files changed

Lines changed: 210 additions & 43 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Full per-function reference: **[docs/API.md](docs/API.md)**.
4747
| [LossOfControl](docs/API.md#lossofcontrol) | `C_LossOfControl.GetActiveLossOfControlData`, `C_LossOfControl.GetActiveLossOfControlDataCount` |
4848
| [Macros](docs/API.md#macros) | `GetLooseMacroIcons`, `GetLooseMacroItemIcons`, `GetMacroIcons`, `GetMacroItemIcons`, `GetMacroSpell` |
4949
| [Mail](docs/API.md#mail) | `GetInboxItemLink`, `GetSendMailItemLink` |
50-
| [Map](docs/API.md#map) | `C_Map.GetAreaTriggerInfo`, `C_Map.GetAreaTriggers`, `C_Map.GetBestMapForUnit`, `C_Map.GetMapOverlays` |
50+
| [Map](docs/API.md#map) | `C_Map.GetAreaTriggerInfo`, `C_Map.GetAreaTriggers`, `C_Map.GetBestMapForUnit`, `C_Map.GetMapOverlays`, `C_Map.GetMapWorldSize` |
5151
| [MerchantFrame](docs/API.md#merchantframe) | `C_MerchantFrame.GetBuybackItemID`, `C_MerchantFrame.GetItemInfo`, `C_MerchantFrame.GetNumJunkItems`, `C_MerchantFrame.IsMerchantItemRefundable`, `C_MerchantFrame.IsSellAllJunkEnabled`, `C_MerchantFrame.SellAllJunkItems` |
5252
| [NamePlate](docs/API.md#nameplate) | `C_NamePlate.GetNamePlateForGUID`, `C_NamePlate.GetNamePlateForUnit`, `C_NamePlate.GetNamePlateGUIDs`, `C_NamePlate.GetNamePlates` |
5353
| [NameCache](docs/API.md#namecache) | `C_PlayerCache.GetPlayerInfoByName`, `C_PlayerCache.IsEnabled`, `C_PlayerCache.IsScanEnabled`, `C_PlayerCache.RememberPlayer`, `C_PlayerCache.SetEnabled`, `C_PlayerCache.SetScanEnabled`, `GetPlayerInfoByGUID`, `UnitNameFromGUID` |

docs/API.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ build instructions.
285285
- [`C_Map.GetAreaTriggerInfo(triggerID)` / `C_Map.GetAreaTriggers([mapID])`](#c_mapgetareatriggerinfotriggerid--c_mapgetareatriggersmapid)
286286
- [`C_Map.GetBestMapForUnit(unitToken)`](#c_mapgetbestmapforunitunittoken)
287287
- [`C_Map.GetMapOverlays([areaID])`](#c_mapgetmapoverlaysareaid)
288+
- [`C_Map.GetMapWorldSize([areaID])`](#c_mapgetmapworldsizeareaid)
288289

289290
- [MerchantFrame](#merchantframe)
290291
- [`C_MerchantFrame.GetItemInfo(slot)`](#c_merchantframegetiteminfoslot)
@@ -6848,6 +6849,31 @@ end
68486849
> reliable for placement; it's only the tile count they imply that
68496850
> lies.
68506851
6852+
### `C_Map.GetMapWorldSize([areaID])`
6853+
6854+
Returns a zone's world size in yards as `width, height` — the physical extent the
6855+
map represents, read straight from the `WorldMapArea.dbc` placement rect.
6856+
Addons use it to turn map-relative percents into yard distances (range
6857+
rings, "N yards away" readouts, proximity checks).
6858+
6859+
Retail takes a `uiMapID`; here it's an `areaID` (`AreaTable.dbc` id — the
6860+
same identity
6861+
[`C_Map.GetBestMapForUnit`](#c_mapgetbestmapforunitunittoken) returns and
6862+
[`C_Map.GetMapOverlays`](#c_mapgetmapoverlaysareaid) accepts), returns that
6863+
zone's size; with no argument, the world map's currently **viewed** zone
6864+
(same resolution `GetMapOverlays()` uses with no arg). Returns `nil, nil`
6865+
when the zone can't be resolved.
6866+
6867+
`width` is the east-west span (world Y), `height` the north-south span
6868+
(world X) — the same denominators behind the
6869+
[`C_Map.GetAreaTriggerInfo`](#c_mapgetareatriggerinfotriggerid--c_mapgetareatriggersmapid)
6870+
map-percent transform, so `mapX/100 * width` and `mapY/100 * height` give a
6871+
point's offset in yards within the zone.
6872+
6873+
```lua
6874+
local w, h = C_Map.GetMapWorldSize(85) -- Tirisfal: ~4518.7, 3012.5
6875+
```
6876+
68516877
### `C_Map.GetAreaTriggerInfo(triggerID)` / `C_Map.GetAreaTriggers([mapID])`
68526878

68536879
ClassicAPI extension. Exposes `AreaTrigger.dbc` — the client's 517

src/map/Area.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// This file is part of ClassicAPI.
2+
//
3+
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
4+
// of the GNU General Public License as published by the Free Software Foundation, either
5+
// version 3 of the License, or (at your option) any later version.
6+
//
7+
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
8+
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
9+
// PURPOSE. See the GNU General Public License for more details.
10+
//
11+
// You should have received a copy of the GNU General Public License along with
12+
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
13+
14+
#include "map/Area.h"
15+
16+
#include "Offsets.h"
17+
#include "dbc/Lookup.h"
18+
19+
namespace Map::Area {
20+
21+
int RowForAreaID(uint32_t areaID) {
22+
const int count =
23+
*reinterpret_cast<const int *>(Offsets::VAR_WORLDMAP_AREA_COUNT);
24+
for (int id = 1; id <= count; ++id) {
25+
const uint8_t *rec = DBC::Record(Offsets::VAR_WORLDMAP_AREA_RECORDS,
26+
Offsets::VAR_WORLDMAP_AREA_COUNT,
27+
static_cast<uint32_t>(id));
28+
if (rec != nullptr &&
29+
*reinterpret_cast<const uint32_t *>(rec + Offsets::OFF_WMA_AREA_ID) ==
30+
areaID)
31+
return id;
32+
}
33+
return -1;
34+
}
35+
36+
int CurrentViewRow() {
37+
const int continent =
38+
*reinterpret_cast<const int *>(Offsets::VAR_WORLDMAP_CONTINENT_INDEX);
39+
if (continent < 0)
40+
return *reinterpret_cast<const int *>(
41+
Offsets::VAR_WORLDMAP_DEFAULT_AREA_ROW);
42+
const uint8_t *blob = *reinterpret_cast<const uint8_t *const *>(
43+
Offsets::VAR_WORLDMAP_CONTINENT_DATA);
44+
if (blob == nullptr)
45+
return -1;
46+
const uint8_t *entry = blob + continent * Offsets::WORLDMAP_CONTINENT_STRIDE;
47+
const int zone =
48+
*reinterpret_cast<const int *>(Offsets::VAR_WORLDMAP_ZONE_INDEX);
49+
if (zone < 0)
50+
return *reinterpret_cast<const int *>(entry + 0x04);
51+
const int *zoneRows = *reinterpret_cast<const int *const *>(entry + 0x10);
52+
return (zoneRows != nullptr) ? zoneRows[zone] : -1;
53+
}
54+
55+
} // namespace Map::Area

src/map/Area.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// This file is part of ClassicAPI.
2+
//
3+
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
4+
// of the GNU General Public License as published by the Free Software Foundation, either
5+
// version 3 of the License, or (at your option) any later version.
6+
//
7+
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
8+
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
9+
// PURPOSE. See the GNU General Public License for more details.
10+
//
11+
// You should have received a copy of the GNU General Public License along with
12+
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
13+
14+
#pragma once
15+
16+
#include <cstdint>
17+
18+
// `Map::Area` — shared `WorldMapArea.dbc` row resolution. A WorldMapArea
19+
// row is the client's per-zone map descriptor (directory name + the
20+
// world-coordinate rect used for world↔map transforms); every C_Map.*
21+
// reader that keys off "which zone" resolves a row first. Both
22+
// `Map::Overlays` (GetMapOverlays) and `Map::WorldSize` (GetMapWorldSize)
23+
// go through here so the two ways of naming a zone — an AreaTable areaID
24+
// argument, or the world map's current continent/zone view — live in one
25+
// place.
26+
namespace Map::Area {
27+
28+
// WorldMapArea row for an AreaTable `areaID` (the stable zone identity
29+
// `C_Map.GetBestMapForUnit` returns). Linear walk of the ~175-row table.
30+
// Returns -1 when no row carries that areaID.
31+
int RowForAreaID(uint32_t areaID);
32+
33+
// WorldMapArea row for the world map's currently *viewed* zone — the same
34+
// continent/zone selection the engine's map-name resolver (FUN_004a6cf0,
35+
// behind GetMapInfo) performs: continent index → per-continent blob, zone
36+
// index (-1 = whole continent) → that continent's zone-row array; no
37+
// continent selected falls back to the default-row global. Returns -1 when
38+
// the continent blob isn't resident.
39+
int CurrentViewRow();
40+
41+
} // namespace Map::Area

src/map/Overlays.cpp

Lines changed: 5 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
#include "Game.h"
9898
#include "Offsets.h"
9999
#include "dbc/Lookup.h"
100+
#include "map/Area.h"
100101

101102
#include <cstdint>
102103
#include <cstdio>
@@ -279,46 +280,8 @@ const ResolvedOverlay *CachedResolve(int overlayId, const char *basePath,
279280
return g_cache[overlayId];
280281
}
281282

282-
// WorldMapArea row id for the world map's current view — the same
283-
// resolution the engine's current-map-name getter (FUN_004a6cf0, behind
284-
// GetMapInfo) performs: continent/zone selection indexes into the
285-
// per-continent data blob; no continent selected falls back to the
286-
// default row global.
287-
int CurrentWorldMapAreaRow() {
288-
const int continent =
289-
*reinterpret_cast<const int *>(Offsets::VAR_WORLDMAP_CONTINENT_INDEX);
290-
if (continent < 0)
291-
return *reinterpret_cast<const int *>(
292-
Offsets::VAR_WORLDMAP_DEFAULT_AREA_ROW);
293-
const uint8_t *blob = *reinterpret_cast<const uint8_t *const *>(
294-
Offsets::VAR_WORLDMAP_CONTINENT_DATA);
295-
if (blob == nullptr)
296-
return -1;
297-
const uint8_t *entry = blob + continent * Offsets::WORLDMAP_CONTINENT_STRIDE;
298-
const int zone =
299-
*reinterpret_cast<const int *>(Offsets::VAR_WORLDMAP_ZONE_INDEX);
300-
if (zone < 0)
301-
return *reinterpret_cast<const int *>(entry + 0x04);
302-
const int *zoneRows = *reinterpret_cast<const int *const *>(entry + 0x10);
303-
return (zoneRows != nullptr) ? zoneRows[zone] : -1;
304-
}
305-
306-
// WorldMapArea row id for an AreaTable areaID. Linear walk — the table
307-
// has ~175 rows; called once per Lua call.
308-
int WorldMapAreaRowForAreaID(uint32_t areaID) {
309-
const int count =
310-
*reinterpret_cast<const int *>(Offsets::VAR_WORLDMAP_AREA_COUNT);
311-
for (int id = 1; id <= count; ++id) {
312-
const uint8_t *rec = DBC::Record(Offsets::VAR_WORLDMAP_AREA_RECORDS,
313-
Offsets::VAR_WORLDMAP_AREA_COUNT,
314-
static_cast<uint32_t>(id));
315-
if (rec != nullptr &&
316-
*reinterpret_cast<const uint32_t *>(rec + Offsets::OFF_WMA_AREA_ID) ==
317-
areaID)
318-
return id;
319-
}
320-
return -1;
321-
}
283+
// WorldMapArea row resolution (areaID → row, current view → row) lives in
284+
// the shared `Map::Area` helper — `GetMapWorldSize` keys off the same rows.
322285

323286
// Pushes the `tiles` array for one overlay (leaves it on the stack top).
324287
void PushTiles(void *L, const ResolvedOverlay *r, const char *basePath,
@@ -405,10 +368,10 @@ void PushTiles(void *L, const ResolvedOverlay *r, const char *basePath,
405368
int __fastcall Script_GetMapOverlays(void *L) {
406369
int wmaRow = -1;
407370
if (Game::Lua::IsNumber(L, 1)) {
408-
wmaRow = WorldMapAreaRowForAreaID(
371+
wmaRow = Map::Area::RowForAreaID(
409372
static_cast<uint32_t>(Game::Lua::ToNumber(L, 1)));
410373
} else {
411-
wmaRow = CurrentWorldMapAreaRow();
374+
wmaRow = Map::Area::CurrentViewRow();
412375
}
413376

414377
Game::Lua::SetTop(L, 0);

src/map/WorldSize.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// This file is part of ClassicAPI.
2+
//
3+
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
4+
// of the GNU General Public License as published by the Free Software Foundation, either
5+
// version 3 of the License, or (at your option) any later version.
6+
//
7+
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
8+
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
9+
// PURPOSE. See the GNU General Public License for more details.
10+
//
11+
// You should have received a copy of the GNU General Public License along with
12+
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
13+
14+
// `C_Map.GetMapWorldSize([areaID])` — the world size (in yards) of a zone,
15+
// as `width, height`. This is the physical extent the map represents: the
16+
// span of the `WorldMapArea.dbc` placement rect. Modern addons use it to
17+
// convert map-relative percents to yard distances (e.g. tracking-range
18+
// rings, "X yards away" readouts).
19+
//
20+
// With a numeric `areaID` (AreaTable id — the same identity
21+
// `C_Map.GetBestMapForUnit` returns and `C_Map.GetMapOverlays` accepts),
22+
// returns that zone's size. With no argument, returns the world map's
23+
// currently viewed zone (same resolution `GetMapOverlays()` uses with no
24+
// arg). Returns nothing (→ nil, nil) when the zone can't be resolved.
25+
//
26+
// WoW's world axes are +X north, +Y west; the WorldMapArea rect bounds
27+
// them as locTop/locBottom (world X) and locLeft/locRight (world Y). Map
28+
// width is the horizontal (east-west) extent and height the vertical
29+
// (north-south):
30+
// width = locLeft - locRight (world Y span)
31+
// height = locTop - locBottom (world X span)
32+
// The same spans are the denominators in the world→map percent transform
33+
// (see `Map::AreaTriggers`), so a point's `mapX% * width / 100` is its
34+
// offset in yards from the zone's east edge, and likewise for height.
35+
36+
#include "Game.h"
37+
#include "Offsets.h"
38+
#include "dbc/Lookup.h"
39+
#include "map/Area.h"
40+
41+
#include <cstdint>
42+
43+
namespace Map::WorldSize {
44+
45+
namespace {
46+
47+
int __fastcall Script_GetMapWorldSize(void *L) {
48+
const int row = Game::Lua::IsNumber(L, 1)
49+
? Map::Area::RowForAreaID(
50+
static_cast<uint32_t>(Game::Lua::ToNumber(L, 1)))
51+
: Map::Area::CurrentViewRow();
52+
53+
Game::Lua::SetTop(L, 0);
54+
if (row <= 0)
55+
return 0; // no values -> nil, nil
56+
57+
const uint8_t *rec = DBC::Record(Offsets::VAR_WORLDMAP_AREA_RECORDS,
58+
Offsets::VAR_WORLDMAP_AREA_COUNT,
59+
static_cast<uint32_t>(row));
60+
if (rec == nullptr)
61+
return 0;
62+
63+
const float left = *reinterpret_cast<const float *>(rec + Offsets::OFF_WMA_LOC_LEFT);
64+
const float right = *reinterpret_cast<const float *>(rec + Offsets::OFF_WMA_LOC_RIGHT);
65+
const float top = *reinterpret_cast<const float *>(rec + Offsets::OFF_WMA_LOC_TOP);
66+
const float bottom = *reinterpret_cast<const float *>(rec + Offsets::OFF_WMA_LOC_BOTTOM);
67+
68+
Game::Lua::PushNumber(L, static_cast<double>(left) - right); // width (world Y span)
69+
Game::Lua::PushNumber(L, static_cast<double>(top) - bottom); // height (world X span)
70+
return 2;
71+
}
72+
73+
void RegisterLuaFunctions() {
74+
Game::Lua::RegisterTableFunction("C_Map", "GetMapWorldSize",
75+
&Script_GetMapWorldSize);
76+
}
77+
78+
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
79+
80+
} // namespace
81+
82+
} // namespace Map::WorldSize

0 commit comments

Comments
 (0)