AzerothGPS
AzerothGPS › For addon authors

Public API

The AzerothGPS global table for companion addons: geometry, map overlays, options pages and more.

Api.lua publishes a global table AzerothGPS (version 15) so other addons, such as AzerothGPS-StreetView, can read the map's geometry and draw on the map. It stays display only, like the rest of AzerothGPS. Declare ## Dependencies: AzerothGPS in your toc.

World coordinates are yards on a continent: x points north, y points west, in the order UnitPosition returns them (see coordinates.md). Map offsets are UI units from the map's center, +x right and +y up.

Geometry#

Function Returns
PlayerWorld() x, y, continent, z; nil in instances or when the game hides it. Always where the game says the player is, even while AzerothGPS's developer tools show the map from a simulated spot
Facing() radians, counter-clockwise from north (the game's, like PlayerWorld)
BaseContinent(cont) the continent an underground city level is drawn on
ToContinent(from, x, y, to) (x, y) on continent from in continent to's coordinates, by where both sit on the world map (a spot on one continent drawn over the other's world map); nil when either has no world frame. A map shown as an inset on the world map (Zephras Isle, continent 2991) lands on its inset (version 7)
LocateWorld(cont, x, y) uiMapID, zone name, u (east), v (south) of the smallest zone there
MapToWorld(uiMapID, u, v) x, y, continent
Roads(cont) the shipped road network, read only (Data/Roads.lua format)
NearestRoad(cont, x, y) x, y, distance, edge index of the closest road point routing uses; nil while that continent's roads are still being built in the background (a moment after a /reload)
RoadEdge(cont, edge) that edge's table, read only; its points start at index 5

The map window#

Function Returns
MapFrame() AzerothGPSFrame
MapCanvas() the clipped map area
MapButtonParent() a frame above the map for your buttons (anchor inside it)
MapShown() whether the map is visible
MapButton(name) the map's "recenter" button (Back to your position; shown only while panned away) or "import" button (always shown), to place your buttons around them; or (version 13) "toppanel", the directions panel at the map's top (shown while there's a route): hook its OnShow, OnHide and OnSizeChanged to keep a panel of yours under it; or (version 14) "logo", the window frame's logo plate (hidden with the frame while it's off, TopPanelInset() 4; nil where the client has no window frame template), over every part of the frame. The frame's border is at frame level 500 (the client's template): a frame of yours over the border goes above that, under the plate's level to go behind the logo
View() center x, y, continent, rotation, scale (UI units per yard), half width
SaveView() the whole map view, to put back later (version 8): following the player, or the map browsed (a continent's, the world's, a zone's, a dungeon's and its floor) with its zoom and middle, or the terrain view looked around. An opaque table
RestoreView(state) puts back a view from SaveView (right-click still goes back where it did); nil, following, or no longer valid: follows the player (version 8)
CursorWorld() x, y, continent under the mouse pointer; nil unless it's over the map. On a dungeon's map: its level as the continent, and a 4th value, z: the height there on the floor shown
Instance() the dungeon or raid whose map is in view: level (20000 + MapID: its coordinates' "continent"), MapID, name, the floor shown (0: all, else counted from the top), how many floors; nil when none is. The game hides the player's position inside, so pick spots here by browsing its map (its entrance's icon, or it's shown while the player is in it) with the + / - floor buttons
InstanceFloors(level) its floors, lowest first: { { lowest z, highest z }, ... }
WorldToMap(x, y) the point's offset from the map's center as drawn now

Drawing on the map#

  • SetOverlay(name, fn): fn(ctx) runs on every redraw inside a pcall. ctx.cont is the continent in view and ctx.zoom the yards from center to edge. Draw with ctx.Line(x1, y1, x2, y2, {r, g, b}, width, alpha, dotted) and ctx.Dot(x, y, {r, g, b}, size, alpha) in world yards; ctx.ToScreen(x, y) converts. ctx.Icon(x, y, texture, size, alpha) (version 10) puts an icon there: a file id, a path or "atlas:<name>", size UI units across. The lines join the map's own: clipped, rotated with the map, drawn above the route, never faded. Pass nil to remove the overlay. Errors are logged like the map's own.
  • ShowRoads(owner, on, {r, g, b}): shows the road network in that color while any owner asks. The player's own road option wins.
  • HoldMap(owner, on, onDoubleClick): while any owner holds the map (a game on it), the route (still followed; the arrow window is unchanged), the stops' pins, the crosshair with Confirm Route and the top panel aren't shown, and a double-click on the map or one of its icons calls onDoubleClick(x, y, continent) instead of making a stop. on = false lets go.
  • HoldMap(owner, on, onDoubleClick, opts) (version 9): opts.style draws the map in that style while it's held, at every zoom as for a player who picked it, whatever the player's own setting (which isn't changed, and is back on release): "minimap" (the terrain view), "zone" (the world map's art, every area shown), "unrevealed" (the world map's art with no area shown at all: the same for every character), or "nospoiler" (the areas this character explored). Holding again with another style updates it. Meanwhile the Map Style buttons and /agps style don't change it (they say the game on the map sets it). No opts, or no style: as before. An older AzerothGPS ignores the 4th argument: check AzerothGPS.version >= 9.
  • LookAt(cont, x, y, zoom): centers the map on that spot, north up, zoom yards from the middle to the edge. "Back to your position" or Follow() returns to the player.
  • ShowWorld(): the world map, as right-clicking out to the top level (from the terrain view, a click on a continent and then on a spot comes back to the terrain view there).
  • Follow(): back to following the player.
  • Window(name, width, height, title, strata) (version 11): a popup window in AzerothGPS's style, the map window's frame without the logo (the game's metal border, title bar and close button, a dark inside, the title centered), movable and closed by Escape; a plain dark box with a border where the client lacks the template. It starts hidden; put its content from f.top (negative) down, and change the title with f:SetWindowTitle(text).
  • OnIconShiftClick(owner, fn, hint) (version 12): a Shift-click on a boss icon in a dungeon's map is offered to fn(info) first (info: kind = "boss", name, x, y, z, cont = the dungeon's level); fn returns true when it used it. hint (a string, or hint(info)) says what it does: the boss's tooltip shows "Shift-click: " and, inside the dungeon, the map's top line "Shift-click a boss: ". fn = nil removes it.
  • ShowMap(): shows the map window when the player has it hidden.
  • TopPanelInset(): the left inset (pixels) the top panel starts at: past the window frame's portrait when the frame is on, else 4. Line up a panel of your own there with it.
  • OnLayout(owner, fn): fn(inset) is called when that inset changes (the window frame turned on or off); fn = nil stops.
  • Redraw(): the map redraws on its next frame. The map skips redraws when nothing it knows about changed, so call this after changing what your overlay draws.

Your options page and Save Now (version 13)#

  • AddOptionsPage(owner, title, build): a page called title in AzerothGPS's options window. After AzerothGPS's own pages, each addon gets a section of its own in the list, headed with its name less "AzerothGPS" (owner "AzerothGPS_Example" or "AzerothGPS Example" -> Example), its pages under it in the order added ("General", "History"...); the page itself is headed "Example: General". build(ui) runs once when the window is built, or at once if it's built already; calling again with the same owner and title replaces that page. It runs in pcall: an error stays in your page (the window says so in chat). Returns true when added. ui has the helpers AzerothGPS's own pages use, each placing its control under the last:

    • ui.header(text, note), ui.note(text)
    • ui.check(label, tip, get, set)
    • ui.slider(label, min, max, step, fmt, get, set, enabled): fmt a format ("%d") or a function(v) returning the text ("7 days", "Always")
    • ui.button(label, width, onClick): returns the button
    • ui.choice(label, options, get, set, enabled): radio buttons, options = { { value =, text =, tip = }, ... }
    • ui.dropdown(label, options, get, set, enabled) (version 15): the same arguments as ui.choice, as a box showing the one picked; a click opens the list under it (12 rows at most, the mouse wheel scrolls the rest, each option's tip on its row), a click on a row picks it, a click anywhere else closes it. Returns the box. For more than a few choices, where radio buttons take a row each.
    • ui.text(getText): a line whose text is read again on every refresh
    • ui.place(frame, height, indent): a frame of your own, placed like the rest; ui.page: the page's frame

    get(), enabled() and getText() are read again whenever the window refreshes (it opens, a setting changes, RefreshOptions()), each in pcall.

  • RefreshOptions(): read them all again (your own state changed).

  • ShowOptions(title, owner): open the options window at the page title: owner's when given (pass yours: "General" without an owner is AzerothGPS's own), else one of AzerothGPS's. False when there's no such page.

  • SetUnsaved(owner, count, text): the game saves addons' data only at logout, quitting and /reload; a crash loses what came since. Tell AzerothGPS how many of your things are waiting (count; 0 clears it) and your line for the tooltip (text, e.g. "Example: 6.2 mi of paths"): the map's Save and Dismiss buttons (over Clear Route) show while AzerothGPS or any companion has something waiting, with the total.

  • SaveNow(): the same as clicking Save: a /reload, which saves every addon's data. Never in combat: false then.

Looks and arrivals (version 14)#

Only how the map looks, never what it does. Each owner sets its own look (nil takes it away); the look set last shows, and AzerothGPS's own without any.

  • SetPlayerArrow(owner, look): the player's arrow on the map. look.texture (a path or file id) pointing up; it's turned with the facing as the game's arrow is. width, height (UI units; 32 by default), coords = { left, right, top, bottom } (texture coordinates: one frame of a sheet; call again with others to animate), color = { r, g, b } (a tint).
  • SetRouteLook(owner, look): the route. color = { r, g, b } (the roads and the legs off them; rides a darker shade; a trip of several stops keeps its stops' colors), rainbow = true (the rainbow's colors along it, every stop's legs), width (times the usual, 0.5 to 3), glow = true (a wide faint line under it), dotted = true (every leg dotted). The way back to your body stays red.
  • SetFrameLook(owner, look): the window frame (while it's on). border = { r, g, b } (a tint over its metal), title = { r, g, b } (the title bar's color), text = { r, g, b } (the title's).
  • OnArrive(owner, fn): fn(info) when a stop is reached, in pcall: info = { x, y, cont, name, last, kind }, last true for the trip's last stop, kind "stop" or "boss" (a boss stop is reached when it dies). fn = nil stops.

The title bar (version 15)#

  • TitleBarSlot(owner, width): a place for a widget of yours on the map window frame's title bar, at its right, left of the close button. Returns the slot, a frame width UI units wide and 20 high (the title bar's middle is its middle), above every part of the frame (its border is at frame level 500), under the logo: parent your widget to it, at its level + 1 or more. Calling it again with the same owner resizes the same frame; width = nil lets it go (it's hidden) and returns nil. Several owners' slots go right to left in the order asked.
  • The slot shows only while the window frame does and the title bar has room for it: hidden while the frame is off (Options > General > Window frame), on a map too narrow for it and 50 units of the title, and until the map is built (a slot asked for before is placed then), and wherever the client has no window frame. Hook its OnShow and OnHide to put your widget elsewhere meanwhile, and check IsShown() when you first place it.
  • While a slot shows, the title (the place's name) sits left, right after the logo, and stops before the slots: one line, cut short with "..." on a narrow map, never under them. With none, it's centered as before.
  • The slot doesn't take the mouse, so dragging the title bar still moves the map; a widget of yours that takes the mouse covers that part. It fades with the map's frame while the player moves (the window frame's opacity).
  • Also in version 15: ui.dropdown for your options pages (above).

An import inbox (map data from a tool on the player's PC)#

Not a function: a global any loaded file may define, for a tool on the player's PC that has map data for them:

AzerothGPS_ImportInbox = {
  { made = 1791234567, text = [==[
H 0 2100.0,400.0 name=Peacebloom
P add 0 1790000003 2250.5,230.0 icon=134400 name=Old Tree
]==] },
}
  • made: a whole number of the entry's own (the time it was made works); text: AzerothGPS map data, the lines its Import and Share window reads (MapText.lua's: roads, walls, pins, herbs, ore, NPCs). /way lines aren't taken from an inbox: it never changes the route.
  • AzerothGPS reads it once every addon is loaded (at login, a /reload too), as data only: up to 50 entries, each text within the window's limits; anything else is passed over. The entries it hasn't handled are offered to the player once, with what they hold; Yes takes them in as the window's Import does (the player's own data, never AzerothGPS's), No declines them; unanswered, they're offered again next time.
  • Each entry handled is acknowledged in AzerothGPS's saved data (AzerothGPSDB.importInbox[made] = { at, added = { roads, walls, pins, herbs, ore, npcs }, had, rejected, declined }), written at the next /reload or logout: read it there and drop those entries from your file.