what
[QuestHelper.git] / AstrolabeQH / Astrolabe.lua
blobef6aed5cb7b9c110a8b9b327e97992403a23cc5e
1 --[[
2 Name: AstrolabeQH
3 Revision: $Rev: 92 $
4 $Date: 2008-10-05 17:22:44 -0700 (Sun, 05 Oct 2008) $
5 Author(s): Esamynn (esamynn at wowinterface.com), Zorba (see questhelper docs)
6 Inspired By: Gatherer by Norganna
7 MapLibrary by Kristofer Karlsson (krka at kth.se)
8 Documentation: http://wiki.esamynn.org/Astrolabe
9 SVN: http://svn.esamynn.org/astrolabe/
10 Description:
11 This is a library for the World of Warcraft UI system to place
12 icons accurately on both the Minimap and on Worldmaps.
13 This library also manages and updates the position of Minimap icons
14 automatically.
16 Modified to support Death Knight starting zone.
18 Copyright (C) 2006-2008 James Carrothers
20 License:
21 This library is free software; you can redistribute it and/or
22 modify it under the terms of the GNU Lesser General Public
23 License as published by the Free Software Foundation; either
24 version 2.1 of the License, or (at your option) any later version.
26 This library is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
29 Lesser General Public License for more details.
31 You should have received a copy of the GNU Lesser General Public
32 License along with this library; if not, write to the Free Software
33 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
35 Note:
36 This library's source code is specifically designed to work with
37 World of Warcraft's interpreted AddOn system. You have an implicit
38 licence to use this library with these facilities since that is its
39 designated purpose as per:
40 http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat
43 QuestHelper_File["AstrolabeQH/Astrolabe.lua"] = "Development Version"
44 QuestHelper_Loadtime["AstrolabeQH/Astrolabe.lua"] = GetTime()
46 -- WARNING!!!
47 -- DO NOT MAKE CHANGES TO THIS LIBRARY WITHOUT FIRST CHANGING THE LIBRARY_VERSION_MAJOR
48 -- STRING (to something unique) OR ELSE YOU MAY BREAK OTHER ADDONS THAT USE THIS LIBRARY!!!
49 local LIBRARY_VERSION_MAJOR = "Astrolabe-0.4-QuestHelper"
50 local LIBRARY_VERSION_MINOR = 105 -- this is a completely randomly chosen number, the only point being that it was larger than the original 92 and larger than the later 100
52 if not DongleStub then error(LIBRARY_VERSION_MAJOR .. " requires DongleStub.") end
53 if not DongleStub:IsNewerVersion(LIBRARY_VERSION_MAJOR, LIBRARY_VERSION_MINOR) then return end
55 local Astrolabe = {};
57 local Minimap = _G.Minimap
59 -- define local variables for Data Tables (defined at the end of this file)
60 local WorldMapSize, MinimapSize, ValidMinimapShapes, VirtualContinentIndexes;
62 function Astrolabe:GetVersion()
63 return LIBRARY_VERSION_MAJOR, LIBRARY_VERSION_MINOR;
64 end
67 --------------------------------------------------------------------------------------------------------------
68 -- Config Constants
69 --------------------------------------------------------------------------------------------------------------
71 local configConstants = {
72 MinimapUpdateMultiplier = true,
75 -- this constant is multiplied by the current framerate to determine
76 -- how many icons are updated each frame
77 Astrolabe.MinimapUpdateMultiplier = 1;
80 --------------------------------------------------------------------------------------------------------------
81 -- Working Tables
82 --------------------------------------------------------------------------------------------------------------
84 Astrolabe.LastPlayerPosition = { 0, 0, 0, 0 };
85 Astrolabe.MinimapIcons = {};
86 Astrolabe.IconsOnEdge = {};
87 Astrolabe.IconsOnEdge_GroupChangeCallbacks = {};
89 Astrolabe.MinimapIconCount = 0
90 Astrolabe.ForceNextUpdate = false;
91 Astrolabe.IconsOnEdgeChanged = false;
93 -- This variable indicates whether we know of a visible World Map or not.
94 -- The state of this variable is controlled by the AstrolabeMapMonitor library.
95 Astrolabe.WorldMapVisible = false;
97 local AddedOrUpdatedIcons = {}
98 local MinimapIconsMetatable = { __index = AddedOrUpdatedIcons }
101 --------------------------------------------------------------------------------------------------------------
102 -- Local Pointers for often used API functions
103 --------------------------------------------------------------------------------------------------------------
105 local twoPi = math.pi * 2;
106 local atan2 = math.atan2;
107 local sin = math.sin;
108 local cos = math.cos;
109 local abs = math.abs;
110 local sqrt = math.sqrt;
111 local min = math.min
112 local max = math.max
113 local yield = coroutine.yield
114 local GetFramerate = GetFramerate
117 --------------------------------------------------------------------------------------------------------------
118 -- Internal Utility Functions
119 --------------------------------------------------------------------------------------------------------------
121 local function assert(level,condition,message)
122 if not condition then
123 error(message,level)
127 local function argcheck(value, num, ...)
128 assert(1, type(num) == "number", "Bad argument #2 to 'argcheck' (number expected, got " .. type(level) .. ")")
130 for i=1,select("#", ...) do
131 if type(value) == select(i, ...) then return end
134 local types = strjoin(", ", ...)
135 local name = string.match(debugstack(2,2,0), ": in function [`<](.-)['>]")
136 error(string.format("Bad argument #%d to 'Astrolabe.%s' (%s expected, got %s)", num, name, types, type(value)), 3)
139 local function getContPosition( zoneData, z, x, y )
140 if ( z ~= 0 ) then
141 zoneData = zoneData[z];
142 if not zoneData then return end
143 x = x * zoneData.width + zoneData.xOffset;
144 y = y * zoneData.height + zoneData.yOffset;
145 else
146 x = x * zoneData.width;
147 y = y * zoneData.height;
149 return x, y;
152 --------------------------------------------------------------------------------------------------------------
153 -- Virtual Continent Functions
154 --------------------------------------------------------------------------------------------------------------
156 function Astrolabe:GetCurrentVirtualMapCZ()
157 local C, Z = GetCurrentMapContinent(), GetCurrentMapZone();
158 if C == -1 and Z == 0 then
159 -- welllllp
160 local mapname = GetMapInfo()
161 if VirtualContinentIndexes[mapname] and GetCurrentMapDungeonLevel() == 0 then
162 C = VirtualContinentIndexes[mapname]
163 Z = 1
164 elseif mapname and VirtualContinentIndexes[mapname .. GetCurrentMapDungeonLevel()] then
165 C = VirtualContinentIndexes[mapname .. GetCurrentMapDungeonLevel()]
166 Z = 1
167 elseif mapname == "CoTStratholme" and GetCurrentMapDungeonLevel() == 0 then
168 -- why do you gotta make me angry, baby
169 C = VirtualContinentIndexes["CoTStratholme2"]
170 Z = 1
173 return C, Z
176 function Astrolabe:GetCurrentVirtualMapContinent() local C, Z = self:GetCurrentVirtualMapCZ() return C end
177 function Astrolabe:GetCurrentVirtualMapZone() local C, Z = self:GetCurrentVirtualMapCZ() return Z end
179 -- Does much the same as GetMapContinents, but returns as an array and includes the virtual continents in the mix
180 function Astrolabe:GetMapVirtualContinents()
181 local rv = {GetMapContinents()}
182 for k, v in pairs(VirtualContinentIndexes) do
183 rv[v] = k .. "_Continent"
185 return rv
188 -- Does much the same as GetMapContinents, but returns as an array and includes the virtual continents in the mix
189 function Astrolabe:GetMapVirtualZones(zone)
190 for k, v in pairs(VirtualContinentIndexes) do
191 if v == zone then
192 return {[1] = k}
196 return {GetMapZones(zone)}
199 function Astrolabe:GetMapTexture(c, z)
200 for k, v in pairs(VirtualContinentIndexes) do
201 if v == c and z == 0 then
202 return k .. "_Continent"
203 elseif v == c and z == 1 then
204 return k
208 SetMapZoom(c, z)
209 return (GetMapInfo())
212 --------------------------------------------------------------------------------------------------------------
213 -- General Utility Functions
214 --------------------------------------------------------------------------------------------------------------
216 function Astrolabe:ComputeDistance( c1, z1, x1, y1, c2, z2, x2, y2 )
217 QuestHelper: Assert(c1 and z1 and x1 and y1 and c2 and z2 and x2 and y2)
218 --[[
219 argcheck(c1, 2, "number");
220 assert(3, c1 >= 0, "ComputeDistance: Illegal continent index to c1: "..c1);
221 argcheck(z1, 3, "number", "nil");
222 argcheck(x1, 4, "number");
223 argcheck(y1, 5, "number");
224 argcheck(c2, 6, "number");
225 assert(3, c2 >= 0, "ComputeDistance: Illegal continent index to c2: "..c2);
226 argcheck(z2, 7, "number", "nil");
227 argcheck(x2, 8, "number");
228 argcheck(y2, 9, "number");
229 --]]
231 z1 = z1 or 0;
232 z2 = z2 or 0;
234 local dist, xDelta, yDelta;
235 if ( c1 == c2 and z1 == z2 ) then
236 -- points in the same zone
237 local zoneData = WorldMapSize[c1];
238 if ( z1 ~= 0 ) then
239 zoneData = zoneData[z1];
241 xDelta = (x2 - x1) * zoneData.width;
242 yDelta = (y2 - y1) * zoneData.height;
244 elseif ( c1 == c2 ) then
245 -- points on the same continent
246 local zoneData = WorldMapSize[c1];
247 x1, y1 = getContPosition(zoneData, z1, x1, y1);
248 x2, y2 = getContPosition(zoneData, z2, x2, y2);
249 xDelta = (x2 - x1);
250 yDelta = (y2 - y1);
252 elseif ( c1 and c2 ) then
253 local cont1 = WorldMapSize[c1];
254 local cont2 = WorldMapSize[c2];
255 if ( cont1.parentContinent == cont2.parentContinent ) then
256 x1, y1 = getContPosition(cont1, z1, x1, y1);
257 x2, y2 = getContPosition(cont2, z2, x2, y2);
258 if ( c1 ~= cont1.parentContinent ) then
259 x1 = x1 + cont1.xOffset;
260 y1 = y1 + cont1.yOffset;
262 if ( c2 ~= cont2.parentContinent ) then
263 x2 = x2 + cont2.xOffset;
264 y2 = y2 + cont2.yOffset;
267 xDelta = x2 - x1;
268 yDelta = y2 - y1;
272 if ( xDelta and yDelta ) then
273 dist = sqrt(xDelta*xDelta + yDelta*yDelta);
275 return dist, xDelta, yDelta;
278 function Astrolabe:TranslateWorldMapPosition( C, Z, xPos, yPos, nC, nZ )
279 --[[
280 argcheck(C, 2, "number");
281 argcheck(Z, 3, "number", "nil");
282 argcheck(xPos, 4, "number");
283 argcheck(yPos, 5, "number");
284 argcheck(nC, 6, "number");
285 argcheck(nZ, 7, "number", "nil");
286 --]]
288 Z = Z or 0;
289 nZ = nZ or 0;
290 if ( nC < 0 and nC > -77 ) then
291 return;
294 local zoneData;
295 if ( C == nC and Z == nZ ) then
296 return xPos, yPos;
298 elseif ( C == nC ) then
299 -- points on the same continent
300 zoneData = WorldMapSize[C];
301 xPos, yPos = getContPosition(zoneData, Z, xPos, yPos);
302 if ( nZ ~= 0 ) then
303 zoneData = WorldMapSize[C][nZ];
304 xPos = xPos - zoneData.xOffset;
305 yPos = yPos - zoneData.yOffset;
308 elseif ( C and nC ) and ( WorldMapSize[C].parentContinent == WorldMapSize[nC].parentContinent ) then
309 -- different continents, same world
310 zoneData = WorldMapSize[C];
311 local parentContinent = zoneData.parentContinent;
312 xPos, yPos = getContPosition(zoneData, Z, xPos, yPos);
313 if not xPos or not yPos then return end -- there is no such zone. why are you asking me such silly things? you are a terrible person. leave me in my despair.
314 if ( C ~= parentContinent ) then
315 -- translate up to world map if we aren't there already
316 xPos = xPos + zoneData.xOffset;
317 yPos = yPos + zoneData.yOffset;
318 zoneData = WorldMapSize[parentContinent];
320 if ( nC ~= parentContinent ) then
321 -- translate down to the new continent
322 zoneData = WorldMapSize[nC];
323 xPos = xPos - zoneData.xOffset;
324 yPos = yPos - zoneData.yOffset;
325 if ( nZ ~= 0 ) then
326 zoneData = zoneData[nZ];
327 xPos = xPos - zoneData.xOffset;
328 yPos = yPos - zoneData.yOffset;
332 else
333 return;
336 return (xPos / zoneData.width), (yPos / zoneData.height);
339 function Astrolabe:GetAbsoluteContinentPosition( C, Z, xPos, yPos )
340 if C == -1 then -- We're in a battleground that doesn't have a virtual continent, we're just kind of fucked.
341 return
344 assert(0, type(WorldMapSize[C].parentContinent) == "number")
346 local x, y = Astrolabe:TranslateWorldMapPosition(C, Z, xPos, yPos, WorldMapSize[C].parentContinent, 0)
347 if not x or not y then return end
348 local zoneData = WorldMapSize[WorldMapSize[C].parentContinent]
349 return WorldMapSize[C].parentContinent, (x * zoneData.width), (y * zoneData.height)
352 function Astrolabe:FromAbsoluteContinentPosition(C, xPos, yPos)
353 return C, 0, xPos / WorldMapSize[C].width, yPos / WorldMapSize[C].height
356 function Astrolabe:GetZoneWidth(c, z)
357 if z ~= 0 then
358 return WorldMapSize[c][z].width
359 else
360 return WorldMapSize[c].width
364 --*****************************************************************************
365 -- This function will do its utmost to retrieve some sort of valid position
366 -- for the specified unit, including changing the current map zoom (if needed).
367 -- Map Zoom is returned to its previous setting before this function returns.
368 --*****************************************************************************
369 function Astrolabe:GetUnitPosition( unit, noMapChange )
370 local x, y = GetPlayerMapPosition(unit);
371 if ( x <= 0 and y <= 0 ) then
372 if ( noMapChange ) then
373 -- no valid position on the current map, and we aren't allowed
374 -- to change map zoom, so return
375 return;
377 local lastCont, lastZone = GetCurrentMapContinent(), GetCurrentMapZone();
378 SetMapToCurrentZone();
379 x, y = GetPlayerMapPosition(unit);
380 if ( x <= 0 and y <= 0 ) then
381 SetMapZoom(GetCurrentMapContinent());
382 x, y = GetPlayerMapPosition(unit);
383 if ( x <= 0 and y <= 0 ) then
384 -- we are in an instance or otherwise off the continent map
385 return;
388 local C, Z = GetCurrentMapContinent(), GetCurrentMapZone();
389 if ( C ~= lastCont or Z ~= lastZone ) then
390 SetMapZoom(lastCont, lastZone); -- set map zoom back to what it was before
392 return C, Z, x, y;
394 return self:GetCurrentVirtualMapContinent(), self:GetCurrentVirtualMapZone(), x, y;
397 --*****************************************************************************
398 -- This function will do its utmost to retrieve some sort of valid position
399 -- for the specified unit, including changing the current map zoom (if needed).
400 -- However, if a monitored WorldMapFrame (See AstrolabeMapMonitor.lua) is
401 -- visible, then will simply return nil if the current zoom does not provide
402 -- a valid position for the player unit. Map Zoom is returned to its previous
403 -- setting before this function returns, if it was changed.
404 --*****************************************************************************
405 function Astrolabe:GetCurrentPlayerPosition()
406 local x, y = GetPlayerMapPosition("player");
407 if ( x <= 0 and y <= 0 ) then
408 if ( self.WorldMapVisible ) then
409 -- we know there is a visible world map, so don't cause
410 -- WORLD_MAP_UPDATE events by changing map zoom
411 return;
413 local lastCont, lastZone = GetCurrentMapContinent(), GetCurrentMapZone();
414 SetMapToCurrentZone();
415 x, y = GetPlayerMapPosition("player");
416 if ( x <= 0 and y <= 0 ) then
417 SetMapZoom(GetCurrentMapContinent());
418 x, y = GetPlayerMapPosition("player");
419 if ( x <= 0 and y <= 0 ) then
420 -- we are in an instance or otherwise off the continent map
421 return;
424 local C, Z = GetCurrentMapContinent(), GetCurrentMapZone();
426 if ( C ~= lastCont or Z ~= lastZone ) then
427 SetMapZoom(lastCont, lastZone); --set map zoom back to what it was before
429 return C, Z, x, y;
432 return self:GetCurrentVirtualMapContinent(), self:GetCurrentVirtualMapZone(), x, y;
436 --------------------------------------------------------------------------------------------------------------
437 -- Working Table Cache System
438 --------------------------------------------------------------------------------------------------------------
440 local tableCache = {};
441 tableCache["__mode"] = "v";
442 setmetatable(tableCache, tableCache);
444 local function GetWorkingTable( icon )
445 if ( tableCache[icon] ) then
446 return tableCache[icon];
447 else
448 local T = {};
449 tableCache[icon] = T;
450 return T;
455 --------------------------------------------------------------------------------------------------------------
456 -- Minimap Icon Placement
457 --------------------------------------------------------------------------------------------------------------
459 --*****************************************************************************
460 -- local variables specifically for use in this section
461 --*****************************************************************************
462 local minimapRotationEnabled = false;
463 local minimapShape = false;
465 local MinimapCompassTexture = MinimapCompassTexture;
466 local MinimapCompassRing = MiniMapCompassRing;
467 function Astrolabe:GetFacing()
468 if MinimapCompassRing then -- 3.1 hackery
469 return MinimapCompassRing:GetFacing()
470 else
471 return -GetPlayerFacing()
474 local minimapRotationOffset = -Astrolabe.GetFacing()
476 function Astrolabe:GetMapDiameter()
477 local minimapZoom = Minimap:GetZoom()
478 if ( Astrolabe.minimapOutside ) then
479 return MinimapSize.outdoor[minimapZoom];
480 else
481 return MinimapSize.indoor[minimapZoom];
485 local function placeIconOnMinimap( minimap, minimapZoom, mapWidth, mapHeight, icon, dist, xDist, yDist )
486 local mapDiameter;
487 if ( Astrolabe.minimapOutside ) then
488 mapDiameter = MinimapSize.outdoor[minimapZoom];
489 else
490 mapDiameter = MinimapSize.indoor[minimapZoom];
492 local mapRadius = mapDiameter / 2;
493 local xScale = mapDiameter / mapWidth;
494 local yScale = mapDiameter / mapHeight;
495 local iconDiameter = ((icon:GetWidth() / 2) + 3) * xScale;
496 local iconOnEdge = nil;
497 local isRound = true;
499 if ( minimapRotationEnabled ) then
500 local sinTheta = sin(minimapRotationOffset)
501 local cosTheta = cos(minimapRotationOffset)
502 --[[
503 Math Note
504 The math that is acutally going on in the next 3 lines is:
505 local dx, dy = xDist, -yDist
506 xDist = (dx * cosTheta) + (dy * sinTheta)
507 yDist = -((-dx * sinTheta) + (dy * cosTheta))
509 This is because the origin for map coordinates is the top left corner
510 of the map, not the bottom left, and so we have to reverse the vertical
511 distance when doing the our rotation, and then reverse the result vertical
512 distance because this rotation formula gives us a result with the origin based
513 in the bottom left corner (of the (+, +) quadrant).
514 The actual code is a simplification of the above.
516 local dx, dy = xDist, yDist
517 xDist = (dx * cosTheta) - (dy * sinTheta)
518 yDist = (dx * sinTheta) + (dy * cosTheta)
521 if ( minimapShape and not (xDist == 0 or yDist == 0) ) then
522 isRound = (xDist < 0) and 1 or 3;
523 if ( yDist < 0 ) then
524 isRound = minimapShape[isRound];
525 else
526 isRound = minimapShape[isRound + 1];
530 -- for non-circular portions of the Minimap edge
531 if not ( isRound ) then
532 dist = max(abs(xDist), abs(yDist))
535 if ( (dist + iconDiameter) > mapRadius ) then
536 -- position along the outside of the Minimap
537 iconOnEdge = true;
538 local factor = (mapRadius - iconDiameter) / dist;
539 xDist = xDist * factor;
540 yDist = yDist * factor;
543 if ( Astrolabe.IconsOnEdge[icon] ~= iconOnEdge ) then
544 Astrolabe.IconsOnEdge[icon] = iconOnEdge;
545 Astrolabe.IconsOnEdgeChanged = true;
548 icon:ClearAllPoints();
549 icon:SetPoint("CENTER", minimap, "CENTER", xDist/xScale, -yDist/yScale);
552 function Astrolabe:PlaceIconOnMinimap( icon, continent, zone, xPos, yPos )
553 -- check argument types
554 argcheck(icon, 2, "table");
555 assert(3, icon.SetPoint and icon.ClearAllPoints, "Usage Message");
556 argcheck(continent, 3, "number");
557 argcheck(zone, 4, "number", "nil");
558 argcheck(xPos, 5, "number");
559 argcheck(yPos, 6, "number");
561 local lC, lZ, lx, ly = unpack(self.LastPlayerPosition);
562 local dist, xDist, yDist = self:ComputeDistance(lC, lZ, lx, ly, continent, zone, xPos, yPos);
563 if not ( dist ) then
564 --icon's position has no meaningful position relative to the player's current location
565 return -1;
568 local iconData = GetWorkingTable(icon);
569 if ( self.MinimapIcons[icon] ) then
570 self.MinimapIcons[icon] = nil;
571 else
572 self.MinimapIconCount = self.MinimapIconCount + 1
575 -- We know this icon's position is valid, so we need to make sure the icon placement
576 -- system is active. We call this here so that if this is the first icon being added to
577 -- an empty buffer, the full recalc will not completely redo the work done by this function
578 -- because the icon has not yet actually been placed in the buffer.
579 self.processingFrame:Show()
581 AddedOrUpdatedIcons[icon] = iconData
582 iconData.continent = continent;
583 iconData.zone = zone;
584 iconData.xPos = xPos;
585 iconData.yPos = yPos;
586 iconData.dist = dist;
587 iconData.xDist = xDist;
588 iconData.yDist = yDist;
590 minimapRotationEnabled = GetCVar("rotateMinimap") ~= "0"
591 if ( minimapRotationEnabled ) then
592 minimapRotationOffset = -Astrolabe.GetFacing()
595 -- check Minimap Shape
596 minimapShape = GetMinimapShape and ValidMinimapShapes[GetMinimapShape()];
598 -- place the icon on the Minimap and :Show() it
599 local map = Minimap
600 placeIconOnMinimap(map, map:GetZoom(), map:GetWidth(), map:GetHeight(), icon, dist, xDist, yDist);
601 icon:Show()
603 return 0;
606 function Astrolabe:RemoveIconFromMinimap( icon )
607 if not ( self.MinimapIcons[icon] ) then
608 return 1;
610 AddedOrUpdatedIcons[icon] = nil
611 self.MinimapIcons[icon] = nil;
612 self.IconsOnEdge[icon] = nil;
613 icon:Hide();
615 local MinimapIconCount = self.MinimapIconCount - 1
616 if ( MinimapIconCount <= 0 ) then
617 -- no icons left to manage
618 self.processingFrame:Hide()
619 MinimapIconCount = 0 -- because I'm paranoid
621 self.MinimapIconCount = MinimapIconCount
623 return 0;
626 function Astrolabe:RemoveAllMinimapIcons()
627 self:DumpNewIconsCache()
628 local MinimapIcons = self.MinimapIcons;
629 local IconsOnEdge = self.IconsOnEdge;
630 for k, v in pairs(MinimapIcons) do
631 MinimapIcons[k] = nil;
632 IconsOnEdge[k] = nil;
633 k:Hide();
635 self.MinimapIconCount = 0
636 self.processingFrame:Hide()
639 local lastZoom; -- to remember the last seen Minimap zoom level
641 -- local variables to track the status of the two update coroutines
642 local fullUpdateInProgress = true
643 local resetIncrementalUpdate = false
644 local resetFullUpdate = false
646 -- Incremental Update Code
648 -- local variables to track the incremental update coroutine
649 local incrementalUpdateCrashed = true
650 local incrementalUpdateThread
652 local function UpdateMinimapIconPositions( self )
653 yield()
655 while ( true ) do
656 self:DumpNewIconsCache() -- put new/updated icons into the main datacache
658 resetIncrementalUpdate = false -- by definition, the incremental update is reset if it is here
660 local C, Z, x, y = self:GetCurrentPlayerPosition();
661 if ( C and C ~= -1 ) then
662 local lastPosition = self.LastPlayerPosition;
663 local lC, lZ, lx, ly = unpack(lastPosition);
665 minimapRotationEnabled = GetCVar("rotateMinimap") ~= "0"
666 if ( minimapRotationEnabled ) then
667 minimapRotationOffset = -Astrolabe.GetFacing()
670 -- check current frame rate
671 local numPerCycle = min(50, GetFramerate() * (self.MinimapUpdateMultiplier or 1))
673 -- check Minimap Shape
674 minimapShape = GetMinimapShape and ValidMinimapShapes[GetMinimapShape()];
676 if ( lC == C and lZ == Z and lx == x and ly == y ) then
677 -- player has not moved since the last update
678 if ( lastZoom ~= Minimap:GetZoom() or self.ForceNextUpdate or minimapRotationEnabled ) then
679 local currentZoom = Minimap:GetZoom();
680 lastZoom = currentZoom;
681 local mapWidth = Minimap:GetWidth();
682 local mapHeight = Minimap:GetHeight();
683 numPerCycle = numPerCycle * 2
684 local count = 0
685 for icon, data in pairs(self.MinimapIcons) do
686 placeIconOnMinimap(Minimap, currentZoom, mapWidth, mapHeight, icon, data.dist, data.xDist, data.yDist);
688 count = count + 1
689 if ( count > numPerCycle ) then
690 count = 0
691 yield()
692 -- check if the incremental update cycle needs to be reset
693 -- because a full update has been run
694 if ( resetIncrementalUpdate ) then
695 break;
699 self.ForceNextUpdate = false;
701 else
702 local dist, xDelta, yDelta = self:ComputeDistance(lC, lZ, lx, ly, C, Z, x, y);
703 if ( dist ) then
704 local currentZoom = Minimap:GetZoom();
705 lastZoom = currentZoom;
706 local mapWidth = Minimap:GetWidth();
707 local mapHeight = Minimap:GetHeight();
708 local count = 0
709 for icon, data in pairs(self.MinimapIcons) do
710 local xDist = data.xDist - xDelta;
711 local yDist = data.yDist - yDelta;
712 local dist = sqrt(xDist*xDist + yDist*yDist);
713 placeIconOnMinimap(Minimap, currentZoom, mapWidth, mapHeight, icon, dist, xDist, yDist);
715 data.dist = dist;
716 data.xDist = xDist;
717 data.yDist = yDist;
719 count = count + 1
720 if ( count >= numPerCycle ) then
721 count = 0
722 yield()
723 -- check if the incremental update cycle needs to be reset
724 -- because a full update has been run
725 if ( resetIncrementalUpdate ) then
726 break;
730 if not ( resetIncrementalUpdate ) then
731 lastPosition[1] = C;
732 lastPosition[2] = Z;
733 lastPosition[3] = x;
734 lastPosition[4] = y;
736 else
737 self:RemoveAllMinimapIcons()
738 lastPosition[1] = C;
739 lastPosition[2] = Z;
740 lastPosition[3] = x;
741 lastPosition[4] = y;
744 else
745 if not ( self.WorldMapVisible ) then
746 self.processingFrame:Hide();
750 -- if we've been reset, then we want to start the new cycle immediately
751 if not ( resetIncrementalUpdate ) then
752 yield()
757 function Astrolabe:UpdateMinimapIconPositions()
758 if ( fullUpdateInProgress ) then
759 -- if we're in the middle a a full update, we want to finish that first
760 self:CalculateMinimapIconPositions()
761 else
762 if ( incrementalUpdateCrashed ) then
763 incrementalUpdateThread = coroutine.wrap(UpdateMinimapIconPositions)
764 incrementalUpdateThread(self) --initialize the thread
766 incrementalUpdateCrashed = true
767 incrementalUpdateThread()
768 incrementalUpdateCrashed = false
773 -- Full Update Code
775 -- local variables to track the full update coroutine
776 local fullUpdateCrashed = true
777 local fullUpdateThread
779 local function CalculateMinimapIconPositions( self )
780 yield()
782 while ( true ) do
783 self:DumpNewIconsCache() -- put new/updated icons into the main datacache
785 resetFullUpdate = false -- by definition, the full update is reset if it is here
787 fullUpdateInProgress = true -- set the flag the says a full update is in progress
789 local C, Z, x, y = self:GetCurrentPlayerPosition();
790 if ( C and C ~= -1 ) then
791 minimapRotationEnabled = GetCVar("rotateMinimap") ~= "0"
792 if ( minimapRotationEnabled ) then
793 minimapRotationOffset = Astrolabe.GetFacing()
796 -- check current frame rate
797 local numPerCycle = GetFramerate() * (self.MinimapUpdateMultiplier or 1) * 2
799 -- check Minimap Shape
800 minimapShape = GetMinimapShape and ValidMinimapShapes[GetMinimapShape()];
802 local currentZoom = Minimap:GetZoom();
803 lastZoom = currentZoom;
804 local mapWidth = Minimap:GetWidth();
805 local mapHeight = Minimap:GetHeight();
806 local count = 0
807 for icon, data in pairs(self.MinimapIcons) do
808 local dist, xDist, yDist = self:ComputeDistance(C, Z, x, y, data.continent, data.zone, data.xPos, data.yPos);
809 if ( dist ) then
810 placeIconOnMinimap(Minimap, currentZoom, mapWidth, mapHeight, icon, dist, xDist, yDist);
812 data.dist = dist;
813 data.xDist = xDist;
814 data.yDist = yDist;
815 else
816 self:RemoveIconFromMinimap(icon)
819 count = count + 1
820 if ( count >= numPerCycle ) then
821 count = 0
822 yield()
823 -- check if we need to restart due to the full update being reset
824 if ( resetFullUpdate ) then
825 break;
830 if not ( resetFullUpdate ) then
831 local lastPosition = self.LastPlayerPosition;
832 lastPosition[1] = C;
833 lastPosition[2] = Z;
834 lastPosition[3] = x;
835 lastPosition[4] = y;
837 resetIncrementalUpdate = true
839 else
840 if not ( self.WorldMapVisible ) then
841 self.processingFrame:Hide();
845 -- if we've been reset, then we want to start the new cycle immediately
846 if not ( resetFullUpdate ) then
847 fullUpdateInProgress = false
848 yield()
853 function Astrolabe:CalculateMinimapIconPositions( reset )
854 if ( fullUpdateCrashed ) then
855 fullUpdateThread = coroutine.wrap(CalculateMinimapIconPositions)
856 fullUpdateThread(self) --initialize the thread
857 elseif ( reset ) then
858 resetFullUpdate = true
860 fullUpdateCrashed = true
861 fullUpdateThread()
862 fullUpdateCrashed = false
864 -- return result flag
865 if ( fullUpdateInProgress ) then
866 return 1 -- full update started, but did not complete on this cycle
868 else
869 if ( resetIncrementalUpdate ) then
870 return 0 -- update completed
871 else
872 return -1 -- full update did no occur for some reason
879 function Astrolabe:GetDistanceToIcon( icon )
880 local data = self.MinimapIcons[icon];
881 if ( data ) then
882 return data.dist, data.xDist, data.yDist;
886 function Astrolabe:IsIconOnEdge( icon )
887 return self.IconsOnEdge[icon];
890 function Astrolabe:GetDirectionToIcon( icon )
891 local data = self.MinimapIcons[icon];
892 if ( data ) then
893 local dir = atan2(data.xDist, -(data.yDist))
894 if ( dir > 0 ) then
895 return twoPi - dir;
896 else
897 return -dir;
902 function Astrolabe:Register_OnEdgeChanged_Callback( func, ident )
903 -- check argument types
904 argcheck(func, 2, "function");
906 self.IconsOnEdge_GroupChangeCallbacks[func] = ident;
909 --*****************************************************************************
910 -- INTERNAL USE ONLY PLEASE!!!
911 -- Calling this function at the wrong time can cause errors
912 --*****************************************************************************
913 function Astrolabe:DumpNewIconsCache()
914 local MinimapIcons = self.MinimapIcons
915 for icon, data in pairs(AddedOrUpdatedIcons) do
916 MinimapIcons[icon] = data
917 AddedOrUpdatedIcons[icon] = nil
919 -- we now need to restart any updates that were in progress
920 resetIncrementalUpdate = true
921 resetFullUpdate = true
925 --------------------------------------------------------------------------------------------------------------
926 -- World Map Icon Placement
927 --------------------------------------------------------------------------------------------------------------
929 function Astrolabe:PlaceIconOnWorldMap( worldMapFrame, icon, continent, zone, xPos, yPos )
930 -- check argument types
931 argcheck(worldMapFrame, 2, "table");
932 assert(3, worldMapFrame.GetWidth and worldMapFrame.GetHeight, "Usage Message");
933 argcheck(icon, 3, "table");
934 assert(3, icon.SetPoint and icon.ClearAllPoints, "Usage Message");
935 argcheck(continent, 4, "number");
936 argcheck(zone, 5, "number", "nil");
937 argcheck(xPos, 6, "number");
938 argcheck(yPos, 7, "number");
940 local C, Z = self:GetCurrentVirtualMapCZ();
941 local nX, nY = self:TranslateWorldMapPosition(continent, zone, xPos, yPos, C, Z);
943 -- anchor and :Show() the icon if it is within the boundry of the current map, :Hide() it otherwise
944 if ( nX and nY and (0 < nX and nX <= 1) and (0 < nY and nY <= 1) ) then
945 icon:ClearAllPoints();
946 icon:SetPoint("CENTER", worldMapFrame, "TOPLEFT", nX * worldMapFrame:GetWidth(), -nY * worldMapFrame:GetHeight());
947 icon:Show();
948 else
949 icon:Hide();
951 return nX, nY;
955 --------------------------------------------------------------------------------------------------------------
956 -- Handler Scripts
957 --------------------------------------------------------------------------------------------------------------
959 function Astrolabe:OnEvent( frame, event )
960 if ( event == "MINIMAP_UPDATE_ZOOM" ) then
961 -- update minimap zoom scale
962 local curZoom = Minimap:GetZoom();
963 if ( GetCVar("minimapZoom") == GetCVar("minimapInsideZoom") ) then
964 if ( curZoom < 2 ) then
965 Minimap:SetZoom(curZoom + 1);
966 else
967 Minimap:SetZoom(curZoom - 1);
970 if ( GetCVar("minimapZoom")+0 == Minimap:GetZoom() ) then
971 self.minimapOutside = true;
972 else
973 self.minimapOutside = false;
975 Minimap:SetZoom(curZoom);
977 -- re-calculate all Minimap Icon positions
978 if ( frame:IsVisible() ) then
979 self:CalculateMinimapIconPositions(true);
982 elseif ( event == "PLAYER_LEAVING_WORLD" ) then
983 frame:Hide(); -- yes, I know this is redunant
984 self:RemoveAllMinimapIcons(); --dump all minimap icons
986 elseif ( event == "PLAYER_ENTERING_WORLD" ) then
987 frame:Show();
988 if not ( frame:IsVisible() ) then
989 -- do the minimap recalculation anyways if the OnShow script didn't execute
990 -- this is done to ensure the accuracy of information about icons that were
991 -- inserted while the Player was in the process of zoning
992 self:CalculateMinimapIconPositions(true);
995 elseif ( event == "ZONE_CHANGED_NEW_AREA" ) then
996 frame:Hide();
997 frame:Show();
1002 function Astrolabe:OnUpdate( frame, elapsed )
1003 -- on-edge group changed call-backs
1004 if ( self.IconsOnEdgeChanged ) then
1005 self.IconsOnEdgeChanged = false;
1006 for func in pairs(self.IconsOnEdge_GroupChangeCallbacks) do
1007 pcall(func);
1011 self:UpdateMinimapIconPositions();
1014 function Astrolabe:OnShow( frame )
1015 -- set the world map to a zoom with a valid player position
1016 if not ( self.WorldMapVisible ) then
1017 SetMapToCurrentZone();
1019 local C, Z = Astrolabe:GetCurrentPlayerPosition();
1020 if ( C and C ~= -1 ) then
1021 if C >= 0 then -- If we're in Wackyland, we can't change the world map anyway, so at least it's probably right
1022 SetMapZoom(C, Z);
1024 else
1025 frame:Hide();
1026 return
1029 -- re-calculate minimap icon positions
1030 self:CalculateMinimapIconPositions(true);
1032 if ( self.MinimapIconCount <= 0 ) then
1033 -- no icons left to manage
1034 self.processingFrame:Hide()
1038 -- called by AstrolabMapMonitor when all world maps are hidden
1039 function Astrolabe:AllWorldMapsHidden()
1040 if ( IsLoggedIn() ) then
1041 self.processingFrame:Hide();
1042 self.processingFrame:Show();
1046 function Astrolabe:SetMinimapObject(minimap)
1047 Minimap = minimap
1048 self:UpdateMinimapIconPositions()
1050 function Astrolabe:GetMinimapObject()
1051 return Minimap
1054 --------------------------------------------------------------------------------------------------------------
1055 -- Library Registration
1056 --------------------------------------------------------------------------------------------------------------
1058 local function activate( newInstance, oldInstance )
1059 if ( oldInstance ) then -- this is an upgrade activate
1060 if ( oldInstance.DumpNewIconsCache ) then
1061 oldInstance:DumpNewIconsCache()
1063 for k, v in pairs(oldInstance) do
1064 if ( type(v) ~= "function" and (not configConstants[k]) ) then
1065 newInstance[k] = v;
1068 -- sync up the current MinimapIconCount value
1069 local iconCount = 0
1070 for _ in pairs(newInstance.MinimapIcons) do
1071 iconCount = iconCount + 1
1073 newInstance.MinimapIconCount = iconCount
1075 Astrolabe = oldInstance;
1076 else
1077 local frame = CreateFrame("Frame");
1078 newInstance.processingFrame = frame;
1080 newInstance.ContinentList = Astrolabe:GetMapVirtualContinents();
1082 for C in pairs(newInstance.ContinentList) do
1083 local zones = Astrolabe:GetMapVirtualZones(C);
1084 newInstance.ContinentList[C] = zones;
1085 for Z in ipairs(zones) do
1086 zones[Z] = Astrolabe:GetMapTexture(C, Z);
1090 configConstants = nil -- we don't need this anymore
1092 local frame = newInstance.processingFrame;
1093 frame:Hide();
1094 frame:SetParent("Minimap");
1095 frame:UnregisterAllEvents();
1096 frame:RegisterEvent("MINIMAP_UPDATE_ZOOM");
1097 frame:RegisterEvent("PLAYER_LEAVING_WORLD");
1098 frame:RegisterEvent("PLAYER_ENTERING_WORLD");
1099 frame:RegisterEvent("ZONE_CHANGED_NEW_AREA");
1100 frame:SetScript("OnEvent",
1101 function( frame, event, ... )
1102 Astrolabe:OnEvent(frame, event, ...);
1105 frame:SetScript("OnUpdate",
1106 function( frame, elapsed )
1107 Astrolabe:OnUpdate(frame, elapsed);
1110 frame:SetScript("OnShow",
1111 function( frame )
1112 Astrolabe:OnShow(frame);
1116 setmetatable(Astrolabe.MinimapIcons, MinimapIconsMetatable)
1119 --------------------------------------------------------------------------------------------------------------
1120 -- Data
1121 --------------------------------------------------------------------------------------------------------------
1123 -- diameter of the Minimap in game yards at
1124 -- the various possible zoom levels
1125 MinimapSize = {
1126 indoor = {
1127 [0] = 300, -- scale
1128 [1] = 240, -- 1.25
1129 [2] = 180, -- 5/3
1130 [3] = 120, -- 2.5
1131 [4] = 80, -- 3.75
1132 [5] = 50, -- 6
1134 outdoor = {
1135 [0] = 466 + 2/3, -- scale
1136 [1] = 400, -- 7/6
1137 [2] = 333 + 1/3, -- 1.4
1138 [3] = 266 + 2/6, -- 1.75
1139 [4] = 200, -- 7/3
1140 [5] = 133 + 1/3, -- 3.5
1144 ValidMinimapShapes = {
1145 -- { upper-left, lower-left, upper-right, lower-right }
1146 ["SQUARE"] = { false, false, false, false },
1147 ["CORNER-TOPLEFT"] = { true, false, false, false },
1148 ["CORNER-TOPRIGHT"] = { false, false, true, false },
1149 ["CORNER-BOTTOMLEFT"] = { false, true, false, false },
1150 ["CORNER-BOTTOMRIGHT"] = { false, false, false, true },
1151 ["SIDE-LEFT"] = { true, true, false, false },
1152 ["SIDE-RIGHT"] = { false, false, true, true },
1153 ["SIDE-TOP"] = { true, false, true, false },
1154 ["SIDE-BOTTOM"] = { false, true, false, true },
1155 ["TRICORNER-TOPLEFT"] = { true, true, true, false },
1156 ["TRICORNER-TOPRIGHT"] = { true, false, true, true },
1157 ["TRICORNER-BOTTOMLEFT"] = { true, true, false, true },
1158 ["TRICORNER-BOTTOMRIGHT"] = { false, true, true, true },
1161 -- distances across and offsets of the world maps
1162 -- in game yards
1163 WorldMapSize = {
1164 -- World Map of Azeroth
1165 [0] = {
1166 parentContinent = 0,
1167 height = 29688.932932224,
1168 width = 44537.340058402,
1170 -- Kalimdor
1171 { -- [1]
1172 parentContinent = 0,
1173 height = 24533.025279205,
1174 width = 36800.210572494,
1175 xOffset = -8311.793923510446,
1176 yOffset = 1815.215685280706,
1177 zoneData = {
1178 Ashenvale = {
1179 height = 3843.722811451077,
1180 width = 5766.728884700476,
1181 xOffset = 15366.76755576002,
1182 yOffset = 8126.925260781192,
1183 mapID = 331,
1185 Aszhara = {
1186 height = 3381.225696279877,
1187 width = 5070.888165752819,
1188 xOffset = 20343.90485013144,
1189 yOffset = 7458.180046130774,
1190 mapID = 16,
1192 AzuremystIsle = {
1193 height = 2714.561862167815,
1194 width = 4070.883253576282,
1195 xOffset = 9966.70736478994,
1196 yOffset = 5460.278138661794,
1197 mapID = 3524,
1199 Barrens = {
1200 height = 6756.202067150937,
1201 width = 10133.44343943073,
1202 xOffset = 14443.84117394525,
1203 yOffset = 11187.32013604393,
1204 mapID = 17,
1206 BloodmystIsle = {
1207 height = 2174.984710698752,
1208 width = 3262.517428121028,
1209 xOffset = 9541.713418184554,
1210 yOffset = 3424.874558234072,
1211 mapID = 3525,
1213 Darkshore = {
1214 height = 4366.636219106706,
1215 width = 6550.06962983463,
1216 xOffset = 14125.08809600818,
1217 yOffset = 4466.534412478246,
1218 mapID = 148,
1220 Darnassis = {
1221 height = 705.7248633938184,
1222 width = 1058.342927027606,
1223 xOffset = 14128.39258617903,
1224 yOffset = 2561.565012455802,
1225 mapID = 1657,
1227 Desolace = {
1228 height = 2997.895174253872,
1229 width = 4495.882023201739,
1230 xOffset = 12833.40729836031,
1231 yOffset = 12347.72848626745,
1232 mapID = 405,
1234 Durotar = {
1235 height = 3524.975114832228,
1236 width = 5287.558038649864,
1237 xOffset = 19029.30699887344,
1238 yOffset = 10991.48801260963,
1239 mapID = 14,
1241 Dustwallow = {
1242 height = 3499.975146240067,
1243 width = 5250.057259791282,
1244 xOffset = 18041.79657043901,
1245 yOffset = 14833.12751666842,
1246 mapID = 15,
1248 Felwood = {
1249 height = 3833.305958270781,
1250 width = 5750.062034325837,
1251 xOffset = 15425.10163773161,
1252 yOffset = 5666.526367166872,
1253 mapID = 361,
1255 Feralas = {
1256 height = 4633.30011661694,
1257 width = 6950.075260353015,
1258 xOffset = 11625.06045254075,
1259 yOffset = 15166.45834829251,
1260 mapID = 357,
1262 Moonglade = {
1263 height = 1539.572509508711,
1264 width = 2308.356845256911,
1265 xOffset = 18448.05172159372,
1266 yOffset = 4308.20254319874,
1267 mapID = 493,
1269 Mulgore = {
1270 height = 3424.975945100366,
1271 width = 5137.555355060729,
1272 xOffset = 15018.84750987729,
1273 yOffset = 13072.72336630089,
1274 mapID = 215,
1276 Ogrimmar = {
1277 height = 935.4100697456119,
1278 width = 1402.621211455915,
1279 xOffset = 20747.42666130799,
1280 yOffset = 10525.94769396873,
1281 mapID = 1637,
1283 Silithus = {
1284 height = 2322.899061688691,
1285 width = 3483.371975265956,
1286 xOffset = 14529.25864164056,
1287 yOffset = 18758.10068625832,
1288 mapID = 1377,
1290 StonetalonMountains = {
1291 height = 3256.226691571251,
1292 width = 4883.385977951072,
1293 xOffset = 13820.91773479217,
1294 yOffset = 9883.162892509636,
1295 mapID = 406,
1297 Tanaris = {
1298 height = 4599.965662459992,
1299 width = 6900.073766103516,
1300 xOffset = 17285.539010128,
1301 yOffset = 18674.7673661939,
1302 mapID = 440,
1304 Teldrassil = {
1305 height = 3393.726923234355,
1306 width = 5091.720903621394,
1307 xOffset = 13252.16205313556,
1308 yOffset = 968.6418744503761,
1309 mapID = 141,
1311 TheExodar = {
1312 height = 704.6826864472878,
1313 width = 1056.781131437323,
1314 xOffset = 10533.08314172693,
1315 yOffset = 6276.205331713322,
1316 mapID = 3557,
1318 ThousandNeedles = {
1319 height = 2933.312180524323,
1320 width = 4400.046681282484,
1321 xOffset = 17500.12437633161,
1322 yOffset = 16766.44698282704,
1323 mapID = 400,
1325 ThunderBluff = {
1326 height = 695.8282721105132,
1327 width = 1043.761263579803,
1328 xOffset = 16550.11410485969,
1329 yOffset = 13649.80260929285,
1330 mapID = 1638,
1332 UngoroCrater = {
1333 height = 2466.647220780505,
1334 width = 3700.040077455555,
1335 xOffset = 16533.44712326324,
1336 yOffset = 18766.4334494793,
1337 mapID = 490,
1339 Winterspring = {
1340 height = 4733.299561046713,
1341 width = 7100.077599808275,
1342 xOffset = 17383.45606038691,
1343 yOffset = 4266.536453420381,
1344 mapID = 618,
1348 -- Eastern Kingdoms
1349 { -- [2]
1350 parentContinent = 0,
1351 height = 27149.795290881,
1352 width = 40741.175327834,
1353 xOffset = 14407.1086092051,
1354 yOffset = 290.3230897653046,
1355 zoneData = {
1356 Alterac = {
1357 height = 1866.673586850316,
1358 width = 2800.000436369314,
1359 xOffset = 17388.63313899802,
1360 yOffset = 9676.382605411302,
1361 mapID = 2597,
1363 Arathi = {
1364 height = 2400.0092446309,
1365 width = 3599.999380663208,
1366 xOffset = 19038.63328411639,
1367 yOffset = 11309.72201070757,
1368 mapID = 3358,
1370 Badlands = {
1371 height = 1658.340965090961,
1372 width = 2487.498490907989,
1373 xOffset = 20251.1337564772,
1374 yOffset = 17065.99404487956,
1375 mapID = 3,
1377 BlastedLands = {
1378 height = 2233.343415116865,
1379 width = 3349.999381676505,
1380 xOffset = 19413.63362865575,
1381 yOffset = 21743.09582955139,
1382 mapID = 4,
1384 BurningSteppes = {
1385 height = 1952.091972408385,
1386 width = 2929.16694293186,
1387 xOffset = 18438.633261567,
1388 yOffset = 18207.66513379744,
1389 mapID = 46,
1391 DeadwindPass = {
1392 height = 1666.673818905317,
1393 width = 2499.999888210889,
1394 xOffset = 19005.29993968603,
1395 yOffset = 21043.0932328648,
1396 mapID = 41,
1398 DunMorogh = {
1399 height = 3283.345779814337,
1400 width = 4924.998791911572,
1401 xOffset = 16369.8840376619,
1402 yOffset = 15053.48695195484,
1403 mapID = 1,
1405 Duskwood = {
1406 height = 1800.007653419076,
1407 width = 2699.999669551933,
1408 xOffset = 17338.63354148773,
1409 yOffset = 20893.09259181909,
1410 mapID = 10,
1412 EasternPlaguelands = {
1413 height = 2687.510360231216,
1414 width = 4031.249051993366,
1415 xOffset = 20459.46801235962,
1416 yOffset = 7472.207045901617,
1417 mapID = 139,
1419 Elwynn = {
1420 height = 2314.591970284716,
1421 width = 3470.831971412848,
1422 xOffset = 16636.55099386465,
1423 yOffset = 19116.0027890283,
1424 mapID = 12,
1426 EversongWoods = {
1427 height = 3283.346366715794,
1428 width = 4924.998483501337,
1429 xOffset = 20259.46725884782,
1430 yOffset = 2534.687567863296,
1431 mapID = 3430,
1433 Ghostlands = {
1434 height = 2200.008945183733,
1435 width = 3300.002855743766,
1436 xOffset = 21055.29786070095,
1437 yOffset = 5309.698546426793,
1438 mapID = 3433,
1440 Hilsbrad = {
1441 height = 2133.341840477916,
1442 width = 3200.000391416799,
1443 xOffset = 17105.29968281043,
1444 yOffset = 10776.38652289269,
1445 mapID = 267,
1447 Hinterlands = {
1448 height = 2566.676323518885,
1449 width = 3849.998492380244,
1450 xOffset = 19746.96704279287,
1451 yOffset = 9709.715966757984,
1452 mapID = 47,
1454 Ironforge = {
1455 height = 527.6056771582851,
1456 width = 790.6252518322632,
1457 xOffset = 18885.55815177769,
1458 yOffset = 15745.64795436116,
1459 mapID = 1537,
1461 LochModan = {
1462 height = 1839.590356444166,
1463 width = 2758.33360594204,
1464 xOffset = 20165.71623436714,
1465 yOffset = 15663.90573348468,
1466 mapID = 38,
1468 Redridge = {
1469 height = 1447.922213393415,
1470 width = 2170.833229570681,
1471 xOffset = 19742.79960560691,
1472 yOffset = 19751.42209395218,
1473 mapID = 44,
1475 SearingGorge = {
1476 height = 1487.505203229038,
1477 width = 2231.250200533406,
1478 xOffset = 18494.88325409831,
1479 yOffset = 17276.41231120941,
1480 mapID = 51,
1482 SilvermoonCity = {
1483 height = 806.7751969249011,
1484 width = 1211.458551923779,
1485 xOffset = 22172.71573747824,
1486 yOffset = 3422.647395021269,
1487 mapID = 3487,
1489 Silverpine = {
1490 height = 2800.011187621704,
1491 width = 4200.000573479695,
1492 xOffset = 14721.96646274185,
1493 yOffset = 9509.714741967448,
1494 mapID = 130,
1496 Stormwind = {
1497 height = 1158.33686894901,
1498 width = 1737.498058940429,
1499 xOffset = 16449.05164642256,
1500 yOffset = 19172.25350774846,
1501 mapID = 1519,
1503 Stranglethorn = {
1504 height = 4254.18312444072,
1505 width = 6381.248484543122,
1506 xOffset = 15951.13375783437,
1507 yOffset = 22345.18258706305,
1508 mapID = 33,
1510 Sunwell = {
1511 height = 2218.756638064149,
1512 width = 3327.084777999942,
1513 xOffset = 21074.0484502027,
1514 yOffset = 7.595267688679496,
1515 mapID = 4080,
1517 SwampOfSorrows = {
1518 height = 1529.173695058727,
1519 width = 2293.753807610138,
1520 xOffset = 20394.88183258176,
1521 yOffset = 20797.25913588854,
1522 mapID = 8,
1524 Tirisfal = {
1525 height = 3012.510490816506,
1526 width = 4518.749381850256,
1527 xOffset = 15138.63417865412,
1528 yOffset = 7338.874503644808,
1529 mapID = 85,
1531 Undercity = {
1532 height = 640.1067253394195,
1533 width = 959.3752013853186,
1534 xOffset = 17298.77399735696,
1535 yOffset = 9298.435338905521,
1536 mapID = 1497,
1538 WesternPlaguelands = {
1539 height = 2866.677213191588,
1540 width = 4299.998717025251,
1541 xOffset = 17755.30067544475,
1542 yOffset = 7809.708745090687,
1543 mapID = 28,
1545 Westfall = {
1546 height = 2333.342039971409,
1547 width = 3500.001170481545,
1548 xOffset = 15155.29922254704,
1549 yOffset = 20576.42557120998,
1550 mapID = 40,
1552 Wetlands = {
1553 height = 2756.260286844545,
1554 width = 4135.414389381328,
1555 xOffset = 18561.55091405621,
1556 yOffset = 13324.31339403164,
1557 mapID = 11,
1561 -- Outland
1562 { -- [3]
1563 parentContinent = 3,
1564 height = 11642.355227091,
1565 width = 17463.987300595,
1566 zoneData = {
1567 BladesEdgeMountains = {
1568 height = 3616.553511321226,
1569 width = 5424.972055480694,
1570 xOffset = 4150.184214583454,
1571 yOffset = 1412.98225932006,
1572 mapID = 3522,
1574 Hellfire = {
1575 height = 3443.642450656037,
1576 width = 5164.556104714847,
1577 xOffset = 7456.417230912641,
1578 yOffset = 4339.973750274888,
1579 mapID = 3483,
1581 Nagrand = {
1582 height = 3683.218538167106,
1583 width = 5524.971495006054,
1584 xOffset = 2700.192018521809,
1585 yOffset = 5779.511974812862,
1586 mapID = 3518,
1588 Netherstorm = {
1589 height = 3716.550608724641,
1590 width = 5574.970083688359,
1591 xOffset = 7512.667416095402,
1592 yOffset = 365.0979827402549,
1593 mapID = 3523,
1595 ShadowmoonValley = {
1596 height = 3666.552070430093,
1597 width = 5499.971770418525,
1598 xOffset = 8770.993458280615,
1599 yOffset = 7769.033264592288,
1600 mapID = 3520,
1602 ShattrathCity = {
1603 height = 870.8059516186869,
1604 width = 1306.242821388422,
1605 xOffset = 6860.744740098593,
1606 yOffset = 7295.086120456203,
1607 mapID = 3703,
1609 TerokkarForest = {
1610 height = 3599.887783533737,
1611 width = 5399.971351016305,
1612 xOffset = 5912.675516998205,
1613 yOffset = 6821.146319031154,
1614 mapID = 3519,
1616 Zangarmarsh = {
1617 height = 3351.978710181591,
1618 width = 5027.057650868489,
1619 xOffset = 3521.020638264577,
1620 yOffset = 3885.821278366336,
1621 mapID = 3521,
1627 --- WotLK Adjustments, now permanently enabled. Someday I should probably merge these in.
1628 if true then
1629 WorldMapSize[0].height = 31809.64859753034;
1630 WorldMapSize[0].width = 47714.27770954026;
1632 WorldMapSize[1].xOffset = -8590.409362625034;
1633 WorldMapSize[1].yOffset = 5628.694276155668;
1635 WorldMapSize[2].xOffset = 18542.31268111796;
1636 WorldMapSize[2].yOffset = 3585.574682467752;
1639 WorldMapSize[4] = {
1640 parentContinent = 0,
1641 height = 11834.31067391958,
1642 width = 17751.3936186856,
1643 xOffset = 16020.94093549576,
1644 yOffset = 454.2464807713226,
1645 zoneData = {
1646 BoreanTundra = {
1647 height = 3843.765503862232,
1648 width = 5764.58206497758,
1649 xOffset = 646.3186767730767,
1650 yOffset = 5695.480016983896,
1651 mapID = 3537,
1653 CrystalsongForest = {
1654 height = 1814.590053385046,
1655 width = 2722.916164555434,
1656 xOffset = 7773.400227973558,
1657 yOffset = 4091.307437548815,
1658 mapID = 2817,
1660 Dalaran = {
1661 height = 553.3419356683534,
1662 width = 830.014625253355,
1663 xOffset = 8164.640128758279,
1664 yOffset = 4526.722218200071,
1665 mapID = 4395,
1667 Dragonblight = {
1668 height = 3739.597759999098,
1669 width = 5608.331259502691,
1670 xOffset = 5590.067753073641,
1671 yOffset = 5018.394106536425,
1672 mapID = 65,
1674 GrizzlyHills = {
1675 height = 3500.013349296343,
1676 width = 5249.9986179934,
1677 xOffset = 10327.56614428777,
1678 yOffset = 5076.727864214266,
1679 mapID = 394,
1681 HowlingFjord = {
1682 height = 4031.266275060274,
1683 width = 6045.831339550668,
1684 xOffset = 10615.0658552538,
1685 yOffset = 7476.736868262738,
1686 mapID = 495,
1688 IcecrownGlacier = {
1689 height = 4181.266519840844,
1690 width = 6270.832975322177,
1691 xOffset = 3773.401695036191,
1692 yOffset = 1166.296622984233,
1693 mapID = 210,
1695 LakeWintergrasp = {
1696 height = 1983.342901980711,
1697 width = 2974.999377667768,
1698 xOffset = 4887.984320612982,
1699 yOffset = 4876.725348039468,
1700 mapID = 4197,
1702 SholazarBasin = {
1703 height = 2904.177559586215,
1704 width = 4356.248328680455,
1705 xOffset = 2287.985279107324,
1706 yOffset = 3305.887993444818,
1707 mapID = 3711,
1709 TheStormPeaks = {
1710 height = 4741.684940421732,
1711 width = 7112.498205872217,
1712 xOffset = 7375.483315518691,
1713 yOffset = 395.4596828327046,
1714 mapID = 67,
1716 ZulDrak = {
1717 height = 3329.179510740043,
1718 width = 4993.747919923504,
1719 xOffset = 9817.150055203074,
1720 yOffset = 2924.636381254688,
1721 mapID = 66,
1723 HrothgarsLanding = {
1724 height = 2452.7,
1725 width = 2452.7*1.5,
1726 xOffset = 23967.599 - 17549.182,
1727 yOffset = 1027.392 - 1215.431,
1732 local function VContinent(index, name, size)
1733 assert(1, not WorldMapSize[index], "denied")
1735 WorldMapSize[index] = {
1736 parentContinent = index,
1737 height = size,
1738 width = size*1.5,
1739 zoneData = { },
1741 WorldMapSize[index].zoneData[name] = {
1742 height = size,
1743 width = size*1.5,
1744 xOffset = 0,
1745 yOffset = 0,
1749 VContinent(-77, "ScarletEnclave", 2125)
1752 VContinent(-80, "UtgardeKeep1", 100) -- temporary value
1753 VContinent(-81, "UtgardeKeep2", 100) -- temporary value
1754 VContinent(-82, "UtgardeKeep3", 100) -- temporary value
1756 VContinent(-83, "TheNexus", 734.2)
1758 VContinent(-84, "AzjolNerub1", 100) -- temporary value
1759 VContinent(-85, "AzjolNerub2", 100) -- temporary value
1760 VContinent(-86, "AzjolNerub3", 100) -- temporary value
1762 VContinent(-87, "Ahnkahet", 648.3)
1764 VContinent(-88, "DrakTharonKeep1", 100) -- temporary value
1765 VContinent(-89, "DrakTharonKeep2", 100) -- temporary value
1767 VContinent(-90, "VioletHold", 170.83)
1769 VContinent(-91, "Gundrak", 603.35)
1771 VContinent(-92, "Ulduar77", 613.5) -- Halls of Stone
1773 VContinent(-93, "HallsofLightning1", 100) -- temporary value
1774 VContinent(-94, "HallsofLightning2", 100) -- temporary value
1776 VContinent(-95, "Nexus801", 100) -- temporary value -- Oculus
1777 VContinent(-96, "Nexus802", 100) -- temporary value
1778 VContinent(-97, "Nexus803", 100) -- temporary value
1779 VContinent(-98, "Nexus804", 100) -- temporary value
1781 VContinent(-99, "CoTStratholme1", 750.2)
1782 VContinent(-100, "CoTStratholme2", 1216.66)
1784 VContinent(-101, "UtgardePinnacle1", 100) -- temporary value -- hey they spelled it right
1785 VContinent(-102, "UtgardePinnacle2", 100) -- temporary value
1787 VContinent(-103, "VaultofArchavon", 603.25) -- temporary value -- Weirdly, Emalon is actually within the "Vault of Archavon"
1789 VContinent(-104, "Naxxramas1", 1237.5) -- construct quarter
1790 VContinent(-105, "Naxxramas2", 1237.5) -- arachnid quarter
1791 VContinent(-106, "Naxxramas3", 1237.5) -- military quarter
1792 VContinent(-107, "Naxxramas4", 1237.5) -- plague quarter
1793 VContinent(-108, "Naxxramas5", 1379.9) -- overview
1794 VContinent(-109, "Naxxramas6", 437.3) -- lair
1796 VContinent(-110, "TheObsidianSanctum", 775.1)
1798 VContinent(-111, "TheEyeOfEternity", 286.7)
1800 VContinent(-112, "Ulduar", 2191.7) -- temporary value
1801 VContinent(-113, "Ulduar1", 446.5) -- temporary value
1802 VContinent(-114, "Ulduar2", 885.6) -- temporary value
1803 VContinent(-115, "Ulduar3", 100) -- temporary value
1804 VContinent(-116, "Ulduar4", 100) -- temporary value
1806 VContinent(-117, "TheForgeofSouls", 965.4) -- temporary value
1807 VContinent(-118, "PitofSaron", 1022.3)
1810 VirtualContinentIndexes = { -- Don't change values here, since programs might want to store them
1811 ["ScarletEnclave"] = -77,
1813 ["UtgardeKeep1"] = -80,
1814 ["UtgardeKeep2"] = -81,
1815 ["UtgardeKeep3"] = -82,
1817 ["TheNexus"] = -83,
1819 ["AzjolNerub1"] = -84,
1820 ["AzjolNerub2"] = -85,
1821 ["AzjolNerub3"] = -86,
1823 ["Ahnkahet"] = -87,
1825 ["DrakTharonKeep1"] = -88,
1826 ["DrakTharonKeep2"] = -89,
1828 ["VioletHold"] = -90,
1830 ["Gundrak"] = -91,
1832 ["Ulduar77"] = -92, -- Halls of Stone
1834 ["HallsofLightning1"] = -93,
1835 ["HallsofLightning2"] = -94,
1837 ["Nexus801"] = -95, -- Oculus
1838 ["Nexus802"] = -96,
1839 ["Nexus803"] = -97,
1840 ["Nexus804"] = -98,
1842 ["CoTStratholme1"] = -99,
1843 ["CoTStratholme2"] = -100,
1845 ["UtgardePinnacle1"] = -101, -- hey they spelled it right
1846 ["UtgardePinnacle2"] = -102,
1848 ["VaultofArchavon"] = -103, -- Weirdly, Emalon is actually within the "Vault of Archavon"
1850 ["Naxxramas1"] = -104,
1851 ["Naxxramas2"] = -105,
1852 ["Naxxramas3"] = -106,
1853 ["Naxxramas4"] = -107,
1854 ["Naxxramas5"] = -108,
1855 ["Naxxramas6"] = -109,
1857 ["TheObsidianSanctum"] = -110,
1859 ["TheEyeOfEternity"] = -111,
1861 ["Ulduar"] = -112,
1862 ["Ulduar1"] = -113,
1863 ["Ulduar2"] = -114,
1864 ["Ulduar3"] = -115,
1865 ["Ulduar4"] = -116,
1867 ["TheForgeofSouls"] = -117,
1868 ["PitofSaron"] = -118,
1871 DongleStub:Register(Astrolabe, activate)
1873 local zeroData;
1874 zeroData = { xOffset = 0, height = 0, yOffset = 0, width = 0, __index = function() return zeroData end };
1875 setmetatable(zeroData, zeroData);
1876 setmetatable(WorldMapSize, zeroData);
1878 for continent, zones in pairs(Astrolabe.ContinentList) do
1879 local mapData = WorldMapSize[continent];
1880 for index, mapName in pairs(zones) do
1881 if not ( mapData.zoneData[mapName] ) then
1882 --WE HAVE A PROBLEM!!!
1883 ChatFrame1:AddMessage("Astrolabe is missing data for "..select(index, GetMapZones(continent))..".");
1884 mapData.zoneData[mapName] = zeroData;
1886 mapData[index] = mapData.zoneData[mapName];
1887 mapData.zoneData[mapName] = nil;
1892 -- register this library with AstrolabeMapMonitor, this will cause a full update if PLAYER_LOGIN has already fired
1893 local AstrolabeMapMonitor = DongleStub("AstrolabeMapMonitor");
1894 AstrolabeMapMonitor:RegisterAstrolabeLibrary(Astrolabe, LIBRARY_VERSION_MAJOR);
1897 QH_Astrolabe_Ready = true