will this help? maybe
[QuestHelper.git] / AstrolabeQH / Astrolabe.lua
blob035e9aff3a027a128af900b66bf8930f46f30fd8
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, 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 local function placeIconOnMinimap( minimap, minimapZoom, mapWidth, mapHeight, icon, dist, xDist, yDist )
477 local mapDiameter;
478 if ( Astrolabe.minimapOutside ) then
479 mapDiameter = MinimapSize.outdoor[minimapZoom];
480 else
481 mapDiameter = MinimapSize.indoor[minimapZoom];
483 local mapRadius = mapDiameter / 2;
484 local xScale = mapDiameter / mapWidth;
485 local yScale = mapDiameter / mapHeight;
486 local iconDiameter = ((icon:GetWidth() / 2) + 3) * xScale;
487 local iconOnEdge = nil;
488 local isRound = true;
490 if ( minimapRotationEnabled ) then
491 local sinTheta = sin(minimapRotationOffset)
492 local cosTheta = cos(minimapRotationOffset)
493 --[[
494 Math Note
495 The math that is acutally going on in the next 3 lines is:
496 local dx, dy = xDist, -yDist
497 xDist = (dx * cosTheta) + (dy * sinTheta)
498 yDist = -((-dx * sinTheta) + (dy * cosTheta))
500 This is because the origin for map coordinates is the top left corner
501 of the map, not the bottom left, and so we have to reverse the vertical
502 distance when doing the our rotation, and then reverse the result vertical
503 distance because this rotation formula gives us a result with the origin based
504 in the bottom left corner (of the (+, +) quadrant).
505 The actual code is a simplification of the above.
507 local dx, dy = xDist, yDist
508 xDist = (dx * cosTheta) - (dy * sinTheta)
509 yDist = (dx * sinTheta) + (dy * cosTheta)
512 if ( minimapShape and not (xDist == 0 or yDist == 0) ) then
513 isRound = (xDist < 0) and 1 or 3;
514 if ( yDist < 0 ) then
515 isRound = minimapShape[isRound];
516 else
517 isRound = minimapShape[isRound + 1];
521 -- for non-circular portions of the Minimap edge
522 if not ( isRound ) then
523 dist = max(abs(xDist), abs(yDist))
526 if ( (dist + iconDiameter) > mapRadius ) then
527 -- position along the outside of the Minimap
528 iconOnEdge = true;
529 local factor = (mapRadius - iconDiameter) / dist;
530 xDist = xDist * factor;
531 yDist = yDist * factor;
534 if ( Astrolabe.IconsOnEdge[icon] ~= iconOnEdge ) then
535 Astrolabe.IconsOnEdge[icon] = iconOnEdge;
536 Astrolabe.IconsOnEdgeChanged = true;
539 icon:ClearAllPoints();
540 icon:SetPoint("CENTER", minimap, "CENTER", xDist/xScale, -yDist/yScale);
543 function Astrolabe:PlaceIconOnMinimap( icon, continent, zone, xPos, yPos )
544 -- check argument types
545 argcheck(icon, 2, "table");
546 assert(3, icon.SetPoint and icon.ClearAllPoints, "Usage Message");
547 argcheck(continent, 3, "number");
548 argcheck(zone, 4, "number", "nil");
549 argcheck(xPos, 5, "number");
550 argcheck(yPos, 6, "number");
552 local lC, lZ, lx, ly = unpack(self.LastPlayerPosition);
553 local dist, xDist, yDist = self:ComputeDistance(lC, lZ, lx, ly, continent, zone, xPos, yPos);
554 if not ( dist ) then
555 --icon's position has no meaningful position relative to the player's current location
556 return -1;
559 local iconData = GetWorkingTable(icon);
560 if ( self.MinimapIcons[icon] ) then
561 self.MinimapIcons[icon] = nil;
562 else
563 self.MinimapIconCount = self.MinimapIconCount + 1
566 -- We know this icon's position is valid, so we need to make sure the icon placement
567 -- system is active. We call this here so that if this is the first icon being added to
568 -- an empty buffer, the full recalc will not completely redo the work done by this function
569 -- because the icon has not yet actually been placed in the buffer.
570 self.processingFrame:Show()
572 AddedOrUpdatedIcons[icon] = iconData
573 iconData.continent = continent;
574 iconData.zone = zone;
575 iconData.xPos = xPos;
576 iconData.yPos = yPos;
577 iconData.dist = dist;
578 iconData.xDist = xDist;
579 iconData.yDist = yDist;
581 minimapRotationEnabled = GetCVar("rotateMinimap") ~= "0"
582 if ( minimapRotationEnabled ) then
583 minimapRotationOffset = -Astrolabe.GetFacing()
586 -- check Minimap Shape
587 minimapShape = GetMinimapShape and ValidMinimapShapes[GetMinimapShape()];
589 -- place the icon on the Minimap and :Show() it
590 local map = Minimap
591 placeIconOnMinimap(map, map:GetZoom(), map:GetWidth(), map:GetHeight(), icon, dist, xDist, yDist);
592 icon:Show()
594 return 0;
597 function Astrolabe:RemoveIconFromMinimap( icon )
598 if not ( self.MinimapIcons[icon] ) then
599 return 1;
601 AddedOrUpdatedIcons[icon] = nil
602 self.MinimapIcons[icon] = nil;
603 self.IconsOnEdge[icon] = nil;
604 icon:Hide();
606 local MinimapIconCount = self.MinimapIconCount - 1
607 if ( MinimapIconCount <= 0 ) then
608 -- no icons left to manage
609 self.processingFrame:Hide()
610 MinimapIconCount = 0 -- because I'm paranoid
612 self.MinimapIconCount = MinimapIconCount
614 return 0;
617 function Astrolabe:RemoveAllMinimapIcons()
618 self:DumpNewIconsCache()
619 local MinimapIcons = self.MinimapIcons;
620 local IconsOnEdge = self.IconsOnEdge;
621 for k, v in pairs(MinimapIcons) do
622 MinimapIcons[k] = nil;
623 IconsOnEdge[k] = nil;
624 k:Hide();
626 self.MinimapIconCount = 0
627 self.processingFrame:Hide()
630 local lastZoom; -- to remember the last seen Minimap zoom level
632 -- local variables to track the status of the two update coroutines
633 local fullUpdateInProgress = true
634 local resetIncrementalUpdate = false
635 local resetFullUpdate = false
637 -- Incremental Update Code
639 -- local variables to track the incremental update coroutine
640 local incrementalUpdateCrashed = true
641 local incrementalUpdateThread
643 local function UpdateMinimapIconPositions( self )
644 yield()
646 while ( true ) do
647 self:DumpNewIconsCache() -- put new/updated icons into the main datacache
649 resetIncrementalUpdate = false -- by definition, the incremental update is reset if it is here
651 local C, Z, x, y = self:GetCurrentPlayerPosition();
652 if ( C and C ~= -1 ) then
653 local lastPosition = self.LastPlayerPosition;
654 local lC, lZ, lx, ly = unpack(lastPosition);
656 minimapRotationEnabled = GetCVar("rotateMinimap") ~= "0"
657 if ( minimapRotationEnabled ) then
658 minimapRotationOffset = -Astrolabe.GetFacing()
661 -- check current frame rate
662 local numPerCycle = min(50, GetFramerate() * (self.MinimapUpdateMultiplier or 1))
664 -- check Minimap Shape
665 minimapShape = GetMinimapShape and ValidMinimapShapes[GetMinimapShape()];
667 if ( lC == C and lZ == Z and lx == x and ly == y ) then
668 -- player has not moved since the last update
669 if ( lastZoom ~= Minimap:GetZoom() or self.ForceNextUpdate or minimapRotationEnabled ) then
670 local currentZoom = Minimap:GetZoom();
671 lastZoom = currentZoom;
672 local mapWidth = Minimap:GetWidth();
673 local mapHeight = Minimap:GetHeight();
674 numPerCycle = numPerCycle * 2
675 local count = 0
676 for icon, data in pairs(self.MinimapIcons) do
677 placeIconOnMinimap(Minimap, currentZoom, mapWidth, mapHeight, icon, data.dist, data.xDist, data.yDist);
679 count = count + 1
680 if ( count > numPerCycle ) then
681 count = 0
682 yield()
683 -- check if the incremental update cycle needs to be reset
684 -- because a full update has been run
685 if ( resetIncrementalUpdate ) then
686 break;
690 self.ForceNextUpdate = false;
692 else
693 local dist, xDelta, yDelta = self:ComputeDistance(lC, lZ, lx, ly, C, Z, x, y);
694 if ( dist ) then
695 local currentZoom = Minimap:GetZoom();
696 lastZoom = currentZoom;
697 local mapWidth = Minimap:GetWidth();
698 local mapHeight = Minimap:GetHeight();
699 local count = 0
700 for icon, data in pairs(self.MinimapIcons) do
701 local xDist = data.xDist - xDelta;
702 local yDist = data.yDist - yDelta;
703 local dist = sqrt(xDist*xDist + yDist*yDist);
704 placeIconOnMinimap(Minimap, currentZoom, mapWidth, mapHeight, icon, dist, xDist, yDist);
706 data.dist = dist;
707 data.xDist = xDist;
708 data.yDist = yDist;
710 count = count + 1
711 if ( count >= numPerCycle ) then
712 count = 0
713 yield()
714 -- check if the incremental update cycle needs to be reset
715 -- because a full update has been run
716 if ( resetIncrementalUpdate ) then
717 break;
721 if not ( resetIncrementalUpdate ) then
722 lastPosition[1] = C;
723 lastPosition[2] = Z;
724 lastPosition[3] = x;
725 lastPosition[4] = y;
727 else
728 self:RemoveAllMinimapIcons()
729 lastPosition[1] = C;
730 lastPosition[2] = Z;
731 lastPosition[3] = x;
732 lastPosition[4] = y;
735 else
736 if not ( self.WorldMapVisible ) then
737 self.processingFrame:Hide();
741 -- if we've been reset, then we want to start the new cycle immediately
742 if not ( resetIncrementalUpdate ) then
743 yield()
748 function Astrolabe:UpdateMinimapIconPositions()
749 if ( fullUpdateInProgress ) then
750 -- if we're in the middle a a full update, we want to finish that first
751 self:CalculateMinimapIconPositions()
752 else
753 if ( incrementalUpdateCrashed ) then
754 incrementalUpdateThread = coroutine.wrap(UpdateMinimapIconPositions)
755 incrementalUpdateThread(self) --initialize the thread
757 incrementalUpdateCrashed = true
758 incrementalUpdateThread()
759 incrementalUpdateCrashed = false
764 -- Full Update Code
766 -- local variables to track the full update coroutine
767 local fullUpdateCrashed = true
768 local fullUpdateThread
770 local function CalculateMinimapIconPositions( self )
771 yield()
773 while ( true ) do
774 self:DumpNewIconsCache() -- put new/updated icons into the main datacache
776 resetFullUpdate = false -- by definition, the full update is reset if it is here
778 fullUpdateInProgress = true -- set the flag the says a full update is in progress
780 local C, Z, x, y = self:GetCurrentPlayerPosition();
781 if ( C and C ~= -1 ) then
782 minimapRotationEnabled = GetCVar("rotateMinimap") ~= "0"
783 if ( minimapRotationEnabled ) then
784 minimapRotationOffset = Astrolabe.GetFacing()
787 -- check current frame rate
788 local numPerCycle = GetFramerate() * (self.MinimapUpdateMultiplier or 1) * 2
790 -- check Minimap Shape
791 minimapShape = GetMinimapShape and ValidMinimapShapes[GetMinimapShape()];
793 local currentZoom = Minimap:GetZoom();
794 lastZoom = currentZoom;
795 local mapWidth = Minimap:GetWidth();
796 local mapHeight = Minimap:GetHeight();
797 local count = 0
798 for icon, data in pairs(self.MinimapIcons) do
799 local dist, xDist, yDist = self:ComputeDistance(C, Z, x, y, data.continent, data.zone, data.xPos, data.yPos);
800 if ( dist ) then
801 placeIconOnMinimap(Minimap, currentZoom, mapWidth, mapHeight, icon, dist, xDist, yDist);
803 data.dist = dist;
804 data.xDist = xDist;
805 data.yDist = yDist;
806 else
807 self:RemoveIconFromMinimap(icon)
810 count = count + 1
811 if ( count >= numPerCycle ) then
812 count = 0
813 yield()
814 -- check if we need to restart due to the full update being reset
815 if ( resetFullUpdate ) then
816 break;
821 if not ( resetFullUpdate ) then
822 local lastPosition = self.LastPlayerPosition;
823 lastPosition[1] = C;
824 lastPosition[2] = Z;
825 lastPosition[3] = x;
826 lastPosition[4] = y;
828 resetIncrementalUpdate = true
830 else
831 if not ( self.WorldMapVisible ) then
832 self.processingFrame:Hide();
836 -- if we've been reset, then we want to start the new cycle immediately
837 if not ( resetFullUpdate ) then
838 fullUpdateInProgress = false
839 yield()
844 function Astrolabe:CalculateMinimapIconPositions( reset )
845 if ( fullUpdateCrashed ) then
846 fullUpdateThread = coroutine.wrap(CalculateMinimapIconPositions)
847 fullUpdateThread(self) --initialize the thread
848 elseif ( reset ) then
849 resetFullUpdate = true
851 fullUpdateCrashed = true
852 fullUpdateThread()
853 fullUpdateCrashed = false
855 -- return result flag
856 if ( fullUpdateInProgress ) then
857 return 1 -- full update started, but did not complete on this cycle
859 else
860 if ( resetIncrementalUpdate ) then
861 return 0 -- update completed
862 else
863 return -1 -- full update did no occur for some reason
870 function Astrolabe:GetDistanceToIcon( icon )
871 local data = self.MinimapIcons[icon];
872 if ( data ) then
873 return data.dist, data.xDist, data.yDist;
877 function Astrolabe:IsIconOnEdge( icon )
878 return self.IconsOnEdge[icon];
881 function Astrolabe:GetDirectionToIcon( icon )
882 local data = self.MinimapIcons[icon];
883 if ( data ) then
884 local dir = atan2(data.xDist, -(data.yDist))
885 if ( dir > 0 ) then
886 return twoPi - dir;
887 else
888 return -dir;
893 function Astrolabe:Register_OnEdgeChanged_Callback( func, ident )
894 -- check argument types
895 argcheck(func, 2, "function");
897 self.IconsOnEdge_GroupChangeCallbacks[func] = ident;
900 --*****************************************************************************
901 -- INTERNAL USE ONLY PLEASE!!!
902 -- Calling this function at the wrong time can cause errors
903 --*****************************************************************************
904 function Astrolabe:DumpNewIconsCache()
905 local MinimapIcons = self.MinimapIcons
906 for icon, data in pairs(AddedOrUpdatedIcons) do
907 MinimapIcons[icon] = data
908 AddedOrUpdatedIcons[icon] = nil
910 -- we now need to restart any updates that were in progress
911 resetIncrementalUpdate = true
912 resetFullUpdate = true
916 --------------------------------------------------------------------------------------------------------------
917 -- World Map Icon Placement
918 --------------------------------------------------------------------------------------------------------------
920 function Astrolabe:PlaceIconOnWorldMap( worldMapFrame, icon, continent, zone, xPos, yPos )
921 -- check argument types
922 argcheck(worldMapFrame, 2, "table");
923 assert(3, worldMapFrame.GetWidth and worldMapFrame.GetHeight, "Usage Message");
924 argcheck(icon, 3, "table");
925 assert(3, icon.SetPoint and icon.ClearAllPoints, "Usage Message");
926 argcheck(continent, 4, "number");
927 argcheck(zone, 5, "number", "nil");
928 argcheck(xPos, 6, "number");
929 argcheck(yPos, 7, "number");
931 local C, Z = self:GetCurrentVirtualMapCZ();
932 local nX, nY = self:TranslateWorldMapPosition(continent, zone, xPos, yPos, C, Z);
934 -- anchor and :Show() the icon if it is within the boundry of the current map, :Hide() it otherwise
935 if ( nX and nY and (0 < nX and nX <= 1) and (0 < nY and nY <= 1) ) then
936 icon:ClearAllPoints();
937 icon:SetPoint("CENTER", worldMapFrame, "TOPLEFT", nX * worldMapFrame:GetWidth(), -nY * worldMapFrame:GetHeight());
938 icon:Show();
939 else
940 icon:Hide();
942 return nX, nY;
946 --------------------------------------------------------------------------------------------------------------
947 -- Handler Scripts
948 --------------------------------------------------------------------------------------------------------------
950 function Astrolabe:OnEvent( frame, event )
951 if ( event == "MINIMAP_UPDATE_ZOOM" ) then
952 -- update minimap zoom scale
953 local curZoom = Minimap:GetZoom();
954 if ( GetCVar("minimapZoom") == GetCVar("minimapInsideZoom") ) then
955 if ( curZoom < 2 ) then
956 Minimap:SetZoom(curZoom + 1);
957 else
958 Minimap:SetZoom(curZoom - 1);
961 if ( GetCVar("minimapZoom")+0 == Minimap:GetZoom() ) then
962 self.minimapOutside = true;
963 else
964 self.minimapOutside = false;
966 Minimap:SetZoom(curZoom);
968 -- re-calculate all Minimap Icon positions
969 if ( frame:IsVisible() ) then
970 self:CalculateMinimapIconPositions(true);
973 elseif ( event == "PLAYER_LEAVING_WORLD" ) then
974 frame:Hide(); -- yes, I know this is redunant
975 self:RemoveAllMinimapIcons(); --dump all minimap icons
977 elseif ( event == "PLAYER_ENTERING_WORLD" ) then
978 frame:Show();
979 if not ( frame:IsVisible() ) then
980 -- do the minimap recalculation anyways if the OnShow script didn't execute
981 -- this is done to ensure the accuracy of information about icons that were
982 -- inserted while the Player was in the process of zoning
983 self:CalculateMinimapIconPositions(true);
986 elseif ( event == "ZONE_CHANGED_NEW_AREA" ) then
987 frame:Hide();
988 frame:Show();
993 function Astrolabe:OnUpdate( frame, elapsed )
994 -- on-edge group changed call-backs
995 if ( self.IconsOnEdgeChanged ) then
996 self.IconsOnEdgeChanged = false;
997 for func in pairs(self.IconsOnEdge_GroupChangeCallbacks) do
998 pcall(func);
1002 self:UpdateMinimapIconPositions();
1005 function Astrolabe:OnShow( frame )
1006 -- set the world map to a zoom with a valid player position
1007 if not ( self.WorldMapVisible ) then
1008 SetMapToCurrentZone();
1010 local C, Z = Astrolabe:GetCurrentPlayerPosition();
1011 if ( C and C ~= -1 ) then
1012 if C >= 0 then -- If we're in Wackyland, we can't change the world map anyway, so at least it's probably right
1013 SetMapZoom(C, Z);
1015 else
1016 frame:Hide();
1017 return
1020 -- re-calculate minimap icon positions
1021 self:CalculateMinimapIconPositions(true);
1023 if ( self.MinimapIconCount <= 0 ) then
1024 -- no icons left to manage
1025 self.processingFrame:Hide()
1029 -- called by AstrolabMapMonitor when all world maps are hidden
1030 function Astrolabe:AllWorldMapsHidden()
1031 if ( IsLoggedIn() ) then
1032 self.processingFrame:Hide();
1033 self.processingFrame:Show();
1037 function Astrolabe:SetMinimapObject(minimap)
1038 Minimap = minimap
1039 self:UpdateMinimapIconPositions()
1042 --------------------------------------------------------------------------------------------------------------
1043 -- Library Registration
1044 --------------------------------------------------------------------------------------------------------------
1046 local function activate( newInstance, oldInstance )
1047 if ( oldInstance ) then -- this is an upgrade activate
1048 if ( oldInstance.DumpNewIconsCache ) then
1049 oldInstance:DumpNewIconsCache()
1051 for k, v in pairs(oldInstance) do
1052 if ( type(v) ~= "function" and (not configConstants[k]) ) then
1053 newInstance[k] = v;
1056 -- sync up the current MinimapIconCount value
1057 local iconCount = 0
1058 for _ in pairs(newInstance.MinimapIcons) do
1059 iconCount = iconCount + 1
1061 newInstance.MinimapIconCount = iconCount
1063 Astrolabe = oldInstance;
1064 else
1065 local frame = CreateFrame("Frame");
1066 newInstance.processingFrame = frame;
1068 newInstance.ContinentList = Astrolabe:GetMapVirtualContinents();
1070 for C in pairs(newInstance.ContinentList) do
1071 local zones = Astrolabe:GetMapVirtualZones(C);
1072 newInstance.ContinentList[C] = zones;
1073 for Z in ipairs(zones) do
1074 zones[Z] = Astrolabe:GetMapTexture(C, Z);
1078 configConstants = nil -- we don't need this anymore
1080 local frame = newInstance.processingFrame;
1081 frame:Hide();
1082 frame:SetParent("Minimap");
1083 frame:UnregisterAllEvents();
1084 frame:RegisterEvent("MINIMAP_UPDATE_ZOOM");
1085 frame:RegisterEvent("PLAYER_LEAVING_WORLD");
1086 frame:RegisterEvent("PLAYER_ENTERING_WORLD");
1087 frame:RegisterEvent("ZONE_CHANGED_NEW_AREA");
1088 frame:SetScript("OnEvent",
1089 function( frame, event, ... )
1090 Astrolabe:OnEvent(frame, event, ...);
1093 frame:SetScript("OnUpdate",
1094 function( frame, elapsed )
1095 Astrolabe:OnUpdate(frame, elapsed);
1098 frame:SetScript("OnShow",
1099 function( frame )
1100 Astrolabe:OnShow(frame);
1104 setmetatable(Astrolabe.MinimapIcons, MinimapIconsMetatable)
1107 --------------------------------------------------------------------------------------------------------------
1108 -- Data
1109 --------------------------------------------------------------------------------------------------------------
1111 -- diameter of the Minimap in game yards at
1112 -- the various possible zoom levels
1113 MinimapSize = {
1114 indoor = {
1115 [0] = 300, -- scale
1116 [1] = 240, -- 1.25
1117 [2] = 180, -- 5/3
1118 [3] = 120, -- 2.5
1119 [4] = 80, -- 3.75
1120 [5] = 50, -- 6
1122 outdoor = {
1123 [0] = 466 + 2/3, -- scale
1124 [1] = 400, -- 7/6
1125 [2] = 333 + 1/3, -- 1.4
1126 [3] = 266 + 2/6, -- 1.75
1127 [4] = 200, -- 7/3
1128 [5] = 133 + 1/3, -- 3.5
1132 ValidMinimapShapes = {
1133 -- { upper-left, lower-left, upper-right, lower-right }
1134 ["SQUARE"] = { false, false, false, false },
1135 ["CORNER-TOPLEFT"] = { true, false, false, false },
1136 ["CORNER-TOPRIGHT"] = { false, false, true, false },
1137 ["CORNER-BOTTOMLEFT"] = { false, true, false, false },
1138 ["CORNER-BOTTOMRIGHT"] = { false, false, false, true },
1139 ["SIDE-LEFT"] = { true, true, false, false },
1140 ["SIDE-RIGHT"] = { false, false, true, true },
1141 ["SIDE-TOP"] = { true, false, true, false },
1142 ["SIDE-BOTTOM"] = { false, true, false, true },
1143 ["TRICORNER-TOPLEFT"] = { true, true, true, false },
1144 ["TRICORNER-TOPRIGHT"] = { true, false, true, true },
1145 ["TRICORNER-BOTTOMLEFT"] = { true, true, false, true },
1146 ["TRICORNER-BOTTOMRIGHT"] = { false, true, true, true },
1149 -- distances across and offsets of the world maps
1150 -- in game yards
1151 WorldMapSize = {
1152 -- World Map of Azeroth
1153 [0] = {
1154 parentContinent = 0,
1155 height = 29688.932932224,
1156 width = 44537.340058402,
1158 -- Kalimdor
1159 { -- [1]
1160 parentContinent = 0,
1161 height = 24533.025279205,
1162 width = 36800.210572494,
1163 xOffset = -8311.793923510446,
1164 yOffset = 1815.215685280706,
1165 zoneData = {
1166 Ashenvale = {
1167 height = 3843.722811451077,
1168 width = 5766.728884700476,
1169 xOffset = 15366.76755576002,
1170 yOffset = 8126.925260781192,
1171 mapID = 331,
1173 Aszhara = {
1174 height = 3381.225696279877,
1175 width = 5070.888165752819,
1176 xOffset = 20343.90485013144,
1177 yOffset = 7458.180046130774,
1178 mapID = 16,
1180 AzuremystIsle = {
1181 height = 2714.561862167815,
1182 width = 4070.883253576282,
1183 xOffset = 9966.70736478994,
1184 yOffset = 5460.278138661794,
1185 mapID = 3524,
1187 Barrens = {
1188 height = 6756.202067150937,
1189 width = 10133.44343943073,
1190 xOffset = 14443.84117394525,
1191 yOffset = 11187.32013604393,
1192 mapID = 17,
1194 BloodmystIsle = {
1195 height = 2174.984710698752,
1196 width = 3262.517428121028,
1197 xOffset = 9541.713418184554,
1198 yOffset = 3424.874558234072,
1199 mapID = 3525,
1201 Darkshore = {
1202 height = 4366.636219106706,
1203 width = 6550.06962983463,
1204 xOffset = 14125.08809600818,
1205 yOffset = 4466.534412478246,
1206 mapID = 148,
1208 Darnassis = {
1209 height = 705.7248633938184,
1210 width = 1058.342927027606,
1211 xOffset = 14128.39258617903,
1212 yOffset = 2561.565012455802,
1213 mapID = 1657,
1215 Desolace = {
1216 height = 2997.895174253872,
1217 width = 4495.882023201739,
1218 xOffset = 12833.40729836031,
1219 yOffset = 12347.72848626745,
1220 mapID = 405,
1222 Durotar = {
1223 height = 3524.975114832228,
1224 width = 5287.558038649864,
1225 xOffset = 19029.30699887344,
1226 yOffset = 10991.48801260963,
1227 mapID = 14,
1229 Dustwallow = {
1230 height = 3499.975146240067,
1231 width = 5250.057259791282,
1232 xOffset = 18041.79657043901,
1233 yOffset = 14833.12751666842,
1234 mapID = 15,
1236 Felwood = {
1237 height = 3833.305958270781,
1238 width = 5750.062034325837,
1239 xOffset = 15425.10163773161,
1240 yOffset = 5666.526367166872,
1241 mapID = 361,
1243 Feralas = {
1244 height = 4633.30011661694,
1245 width = 6950.075260353015,
1246 xOffset = 11625.06045254075,
1247 yOffset = 15166.45834829251,
1248 mapID = 357,
1250 Moonglade = {
1251 height = 1539.572509508711,
1252 width = 2308.356845256911,
1253 xOffset = 18448.05172159372,
1254 yOffset = 4308.20254319874,
1255 mapID = 493,
1257 Mulgore = {
1258 height = 3424.975945100366,
1259 width = 5137.555355060729,
1260 xOffset = 15018.84750987729,
1261 yOffset = 13072.72336630089,
1262 mapID = 215,
1264 Ogrimmar = {
1265 height = 935.4100697456119,
1266 width = 1402.621211455915,
1267 xOffset = 20747.42666130799,
1268 yOffset = 10525.94769396873,
1269 mapID = 1637,
1271 Silithus = {
1272 height = 2322.899061688691,
1273 width = 3483.371975265956,
1274 xOffset = 14529.25864164056,
1275 yOffset = 18758.10068625832,
1276 mapID = 1377,
1278 StonetalonMountains = {
1279 height = 3256.226691571251,
1280 width = 4883.385977951072,
1281 xOffset = 13820.91773479217,
1282 yOffset = 9883.162892509636,
1283 mapID = 406,
1285 Tanaris = {
1286 height = 4599.965662459992,
1287 width = 6900.073766103516,
1288 xOffset = 17285.539010128,
1289 yOffset = 18674.7673661939,
1290 mapID = 440,
1292 Teldrassil = {
1293 height = 3393.726923234355,
1294 width = 5091.720903621394,
1295 xOffset = 13252.16205313556,
1296 yOffset = 968.6418744503761,
1297 mapID = 141,
1299 TheExodar = {
1300 height = 704.6826864472878,
1301 width = 1056.781131437323,
1302 xOffset = 10533.08314172693,
1303 yOffset = 6276.205331713322,
1304 mapID = 3557,
1306 ThousandNeedles = {
1307 height = 2933.312180524323,
1308 width = 4400.046681282484,
1309 xOffset = 17500.12437633161,
1310 yOffset = 16766.44698282704,
1311 mapID = 400,
1313 ThunderBluff = {
1314 height = 695.8282721105132,
1315 width = 1043.761263579803,
1316 xOffset = 16550.11410485969,
1317 yOffset = 13649.80260929285,
1318 mapID = 1638,
1320 UngoroCrater = {
1321 height = 2466.647220780505,
1322 width = 3700.040077455555,
1323 xOffset = 16533.44712326324,
1324 yOffset = 18766.4334494793,
1325 mapID = 490,
1327 Winterspring = {
1328 height = 4733.299561046713,
1329 width = 7100.077599808275,
1330 xOffset = 17383.45606038691,
1331 yOffset = 4266.536453420381,
1332 mapID = 618,
1336 -- Eastern Kingdoms
1337 { -- [2]
1338 parentContinent = 0,
1339 height = 27149.795290881,
1340 width = 40741.175327834,
1341 xOffset = 14407.1086092051,
1342 yOffset = 290.3230897653046,
1343 zoneData = {
1344 Alterac = {
1345 height = 1866.673586850316,
1346 width = 2800.000436369314,
1347 xOffset = 17388.63313899802,
1348 yOffset = 9676.382605411302,
1349 mapID = 2597,
1351 Arathi = {
1352 height = 2400.0092446309,
1353 width = 3599.999380663208,
1354 xOffset = 19038.63328411639,
1355 yOffset = 11309.72201070757,
1356 mapID = 3358,
1358 Badlands = {
1359 height = 1658.340965090961,
1360 width = 2487.498490907989,
1361 xOffset = 20251.1337564772,
1362 yOffset = 17065.99404487956,
1363 mapID = 3,
1365 BlastedLands = {
1366 height = 2233.343415116865,
1367 width = 3349.999381676505,
1368 xOffset = 19413.63362865575,
1369 yOffset = 21743.09582955139,
1370 mapID = 4,
1372 BurningSteppes = {
1373 height = 1952.091972408385,
1374 width = 2929.16694293186,
1375 xOffset = 18438.633261567,
1376 yOffset = 18207.66513379744,
1377 mapID = 46,
1379 DeadwindPass = {
1380 height = 1666.673818905317,
1381 width = 2499.999888210889,
1382 xOffset = 19005.29993968603,
1383 yOffset = 21043.0932328648,
1384 mapID = 41,
1386 DunMorogh = {
1387 height = 3283.345779814337,
1388 width = 4924.998791911572,
1389 xOffset = 16369.8840376619,
1390 yOffset = 15053.48695195484,
1391 mapID = 1,
1393 Duskwood = {
1394 height = 1800.007653419076,
1395 width = 2699.999669551933,
1396 xOffset = 17338.63354148773,
1397 yOffset = 20893.09259181909,
1398 mapID = 10,
1400 EasternPlaguelands = {
1401 height = 2687.510360231216,
1402 width = 4031.249051993366,
1403 xOffset = 20459.46801235962,
1404 yOffset = 7472.207045901617,
1405 mapID = 139,
1407 Elwynn = {
1408 height = 2314.591970284716,
1409 width = 3470.831971412848,
1410 xOffset = 16636.55099386465,
1411 yOffset = 19116.0027890283,
1412 mapID = 12,
1414 EversongWoods = {
1415 height = 3283.346366715794,
1416 width = 4924.998483501337,
1417 xOffset = 20259.46725884782,
1418 yOffset = 2534.687567863296,
1419 mapID = 3430,
1421 Ghostlands = {
1422 height = 2200.008945183733,
1423 width = 3300.002855743766,
1424 xOffset = 21055.29786070095,
1425 yOffset = 5309.698546426793,
1426 mapID = 3433,
1428 Hilsbrad = {
1429 height = 2133.341840477916,
1430 width = 3200.000391416799,
1431 xOffset = 17105.29968281043,
1432 yOffset = 10776.38652289269,
1433 mapID = 267,
1435 Hinterlands = {
1436 height = 2566.676323518885,
1437 width = 3849.998492380244,
1438 xOffset = 19746.96704279287,
1439 yOffset = 9709.715966757984,
1440 mapID = 47,
1442 Ironforge = {
1443 height = 527.6056771582851,
1444 width = 790.6252518322632,
1445 xOffset = 18885.55815177769,
1446 yOffset = 15745.64795436116,
1447 mapID = 1537,
1449 LochModan = {
1450 height = 1839.590356444166,
1451 width = 2758.33360594204,
1452 xOffset = 20165.71623436714,
1453 yOffset = 15663.90573348468,
1454 mapID = 38,
1456 Redridge = {
1457 height = 1447.922213393415,
1458 width = 2170.833229570681,
1459 xOffset = 19742.79960560691,
1460 yOffset = 19751.42209395218,
1461 mapID = 44,
1463 SearingGorge = {
1464 height = 1487.505203229038,
1465 width = 2231.250200533406,
1466 xOffset = 18494.88325409831,
1467 yOffset = 17276.41231120941,
1468 mapID = 51,
1470 SilvermoonCity = {
1471 height = 806.7751969249011,
1472 width = 1211.458551923779,
1473 xOffset = 22172.71573747824,
1474 yOffset = 3422.647395021269,
1475 mapID = 3487,
1477 Silverpine = {
1478 height = 2800.011187621704,
1479 width = 4200.000573479695,
1480 xOffset = 14721.96646274185,
1481 yOffset = 9509.714741967448,
1482 mapID = 130,
1484 Stormwind = {
1485 height = 1158.33686894901,
1486 width = 1737.498058940429,
1487 xOffset = 16449.05164642256,
1488 yOffset = 19172.25350774846,
1489 mapID = 1519,
1491 Stranglethorn = {
1492 height = 4254.18312444072,
1493 width = 6381.248484543122,
1494 xOffset = 15951.13375783437,
1495 yOffset = 22345.18258706305,
1496 mapID = 33,
1498 Sunwell = {
1499 height = 2218.756638064149,
1500 width = 3327.084777999942,
1501 xOffset = 21074.0484502027,
1502 yOffset = 7.595267688679496,
1503 mapID = 4080,
1505 SwampOfSorrows = {
1506 height = 1529.173695058727,
1507 width = 2293.753807610138,
1508 xOffset = 20394.88183258176,
1509 yOffset = 20797.25913588854,
1510 mapID = 8,
1512 Tirisfal = {
1513 height = 3012.510490816506,
1514 width = 4518.749381850256,
1515 xOffset = 15138.63417865412,
1516 yOffset = 7338.874503644808,
1517 mapID = 85,
1519 Undercity = {
1520 height = 640.1067253394195,
1521 width = 959.3752013853186,
1522 xOffset = 17298.77399735696,
1523 yOffset = 9298.435338905521,
1524 mapID = 1497,
1526 WesternPlaguelands = {
1527 height = 2866.677213191588,
1528 width = 4299.998717025251,
1529 xOffset = 17755.30067544475,
1530 yOffset = 7809.708745090687,
1531 mapID = 28,
1533 Westfall = {
1534 height = 2333.342039971409,
1535 width = 3500.001170481545,
1536 xOffset = 15155.29922254704,
1537 yOffset = 20576.42557120998,
1538 mapID = 40,
1540 Wetlands = {
1541 height = 2756.260286844545,
1542 width = 4135.414389381328,
1543 xOffset = 18561.55091405621,
1544 yOffset = 13324.31339403164,
1545 mapID = 11,
1549 -- Outland
1550 { -- [3]
1551 parentContinent = 3,
1552 height = 11642.355227091,
1553 width = 17463.987300595,
1554 zoneData = {
1555 BladesEdgeMountains = {
1556 height = 3616.553511321226,
1557 width = 5424.972055480694,
1558 xOffset = 4150.184214583454,
1559 yOffset = 1412.98225932006,
1560 mapID = 3522,
1562 Hellfire = {
1563 height = 3443.642450656037,
1564 width = 5164.556104714847,
1565 xOffset = 7456.417230912641,
1566 yOffset = 4339.973750274888,
1567 mapID = 3483,
1569 Nagrand = {
1570 height = 3683.218538167106,
1571 width = 5524.971495006054,
1572 xOffset = 2700.192018521809,
1573 yOffset = 5779.511974812862,
1574 mapID = 3518,
1576 Netherstorm = {
1577 height = 3716.550608724641,
1578 width = 5574.970083688359,
1579 xOffset = 7512.667416095402,
1580 yOffset = 365.0979827402549,
1581 mapID = 3523,
1583 ShadowmoonValley = {
1584 height = 3666.552070430093,
1585 width = 5499.971770418525,
1586 xOffset = 8770.993458280615,
1587 yOffset = 7769.033264592288,
1588 mapID = 3520,
1590 ShattrathCity = {
1591 height = 870.8059516186869,
1592 width = 1306.242821388422,
1593 xOffset = 6860.744740098593,
1594 yOffset = 7295.086120456203,
1595 mapID = 3703,
1597 TerokkarForest = {
1598 height = 3599.887783533737,
1599 width = 5399.971351016305,
1600 xOffset = 5912.675516998205,
1601 yOffset = 6821.146319031154,
1602 mapID = 3519,
1604 Zangarmarsh = {
1605 height = 3351.978710181591,
1606 width = 5027.057650868489,
1607 xOffset = 3521.020638264577,
1608 yOffset = 3885.821278366336,
1609 mapID = 3521,
1615 --- WotLK Adjustments, now permanently enabled. Someday I should probably merge these in.
1616 if true then
1617 WorldMapSize[0].height = 31809.64859753034;
1618 WorldMapSize[0].width = 47714.27770954026;
1620 WorldMapSize[1].xOffset = -8590.409362625034;
1621 WorldMapSize[1].yOffset = 5628.694276155668;
1623 WorldMapSize[2].xOffset = 18542.31268111796;
1624 WorldMapSize[2].yOffset = 3585.574682467752;
1627 WorldMapSize[4] = {
1628 parentContinent = 0,
1629 height = 11834.31067391958,
1630 width = 17751.3936186856,
1631 xOffset = 16020.94093549576,
1632 yOffset = 454.2464807713226,
1633 zoneData = {
1634 BoreanTundra = {
1635 height = 3843.765503862232,
1636 width = 5764.58206497758,
1637 xOffset = 646.3186767730767,
1638 yOffset = 5695.480016983896,
1639 mapID = 3537,
1641 CrystalsongForest = {
1642 height = 1814.590053385046,
1643 width = 2722.916164555434,
1644 xOffset = 7773.400227973558,
1645 yOffset = 4091.307437548815,
1646 mapID = 2817,
1648 Dalaran = {
1649 height = 553.3419356683534,
1650 width = 830.014625253355,
1651 xOffset = 8164.640128758279,
1652 yOffset = 4526.722218200071,
1653 mapID = 4395,
1655 Dragonblight = {
1656 height = 3739.597759999098,
1657 width = 5608.331259502691,
1658 xOffset = 5590.067753073641,
1659 yOffset = 5018.394106536425,
1660 mapID = 65,
1662 GrizzlyHills = {
1663 height = 3500.013349296343,
1664 width = 5249.9986179934,
1665 xOffset = 10327.56614428777,
1666 yOffset = 5076.727864214266,
1667 mapID = 394,
1669 HowlingFjord = {
1670 height = 4031.266275060274,
1671 width = 6045.831339550668,
1672 xOffset = 10615.0658552538,
1673 yOffset = 7476.736868262738,
1674 mapID = 495,
1676 IcecrownGlacier = {
1677 height = 4181.266519840844,
1678 width = 6270.832975322177,
1679 xOffset = 3773.401695036191,
1680 yOffset = 1166.296622984233,
1681 mapID = 210,
1683 LakeWintergrasp = {
1684 height = 1983.342901980711,
1685 width = 2974.999377667768,
1686 xOffset = 4887.984320612982,
1687 yOffset = 4876.725348039468,
1688 mapID = 4197,
1690 SholazarBasin = {
1691 height = 2904.177559586215,
1692 width = 4356.248328680455,
1693 xOffset = 2287.985279107324,
1694 yOffset = 3305.887993444818,
1695 mapID = 3711,
1697 TheStormPeaks = {
1698 height = 4741.684940421732,
1699 width = 7112.498205872217,
1700 xOffset = 7375.483315518691,
1701 yOffset = 395.4596828327046,
1702 mapID = 67,
1704 ZulDrak = {
1705 height = 3329.179510740043,
1706 width = 4993.747919923504,
1707 xOffset = 9817.150055203074,
1708 yOffset = 2924.636381254688,
1709 mapID = 66,
1711 HrothgarsLanding = {
1712 height = 2452.7,
1713 width = 2452.7*1.5,
1714 xOffset = 23967.599 - 17549.182,
1715 yOffset = 1027.392 - 1215.431,
1720 local function VContinent(index, name, size)
1721 assert(1, not WorldMapSize[index], "denied")
1723 WorldMapSize[index] = {
1724 parentContinent = index,
1725 height = size,
1726 width = size*1.5,
1727 zoneData = { },
1729 WorldMapSize[index].zoneData[name] = {
1730 height = size,
1731 width = size*1.5,
1732 xOffset = 0,
1733 yOffset = 0,
1737 VContinent(-77, "ScarletEnclave", 2125)
1740 VContinent(-80, "UtgardeKeep1", 100) -- temporary value
1741 VContinent(-81, "UtgardeKeep2", 100) -- temporary value
1742 VContinent(-82, "UtgardeKeep3", 100) -- temporary value
1744 VContinent(-83, "TheNexus", 734.2)
1746 VContinent(-84, "AzjolNerub1", 100) -- temporary value
1747 VContinent(-85, "AzjolNerub2", 100) -- temporary value
1748 VContinent(-86, "AzjolNerub3", 100) -- temporary value
1750 VContinent(-87, "Ahnkahet", 648.3)
1752 VContinent(-88, "DrakTharonKeep1", 100) -- temporary value
1753 VContinent(-89, "DrakTharonKeep2", 100) -- temporary value
1755 VContinent(-90, "VioletHold", 170.83)
1757 VContinent(-91, "Gundrak", 603.35)
1759 VContinent(-92, "Ulduar77", 613.5) -- Halls of Stone
1761 VContinent(-93, "HallsofLightning1", 100) -- temporary value
1762 VContinent(-94, "HallsofLightning2", 100) -- temporary value
1764 VContinent(-95, "Nexus801", 100) -- temporary value -- Oculus
1765 VContinent(-96, "Nexus802", 100) -- temporary value
1766 VContinent(-97, "Nexus803", 100) -- temporary value
1767 VContinent(-98, "Nexus804", 100) -- temporary value
1769 VContinent(-99, "CoTStratholme1", 750.2)
1770 VContinent(-100, "CoTStratholme2", 1216.66)
1772 VContinent(-101, "UtgardePinnacle1", 100) -- temporary value -- hey they spelled it right
1773 VContinent(-102, "UtgardePinnacle2", 100) -- temporary value
1775 VContinent(-103, "VaultofArchavon", 603.25) -- temporary value -- Weirdly, Emalon is actually within the "Vault of Archavon"
1777 VContinent(-104, "Naxxramas1", 1237.5) -- construct quarter
1778 VContinent(-105, "Naxxramas2", 1237.5) -- arachnid quarter
1779 VContinent(-106, "Naxxramas3", 1237.5) -- military quarter
1780 VContinent(-107, "Naxxramas4", 1237.5) -- plague quarter
1781 VContinent(-108, "Naxxramas5", 1379.9) -- overview
1782 VContinent(-109, "Naxxramas6", 437.3) -- lair
1784 VContinent(-110, "TheObsidianSanctum", 775.1)
1786 VContinent(-111, "TheEyeOfEternity", 286.7)
1788 VContinent(-112, "Ulduar", 2191.7) -- temporary value
1789 VContinent(-113, "Ulduar1", 446.5) -- temporary value
1790 VContinent(-114, "Ulduar2", 885.6) -- temporary value
1791 VContinent(-115, "Ulduar3", 100) -- temporary value
1792 VContinent(-116, "Ulduar4", 100) -- temporary value
1794 VContinent(-117, "TheForgeofSouls", 965.4) -- temporary value
1795 VContinent(-118, "PitofSaron", 1022.3)
1798 VirtualContinentIndexes = { -- Don't change values here, since programs might want to store them
1799 ["ScarletEnclave"] = -77,
1801 ["UtgardeKeep1"] = -80,
1802 ["UtgardeKeep2"] = -81,
1803 ["UtgardeKeep3"] = -82,
1805 ["TheNexus"] = -83,
1807 ["AzjolNerub1"] = -84,
1808 ["AzjolNerub2"] = -85,
1809 ["AzjolNerub3"] = -86,
1811 ["Ahnkahet"] = -87,
1813 ["DrakTharonKeep1"] = -88,
1814 ["DrakTharonKeep2"] = -89,
1816 ["VioletHold"] = -90,
1818 ["Gundrak"] = -91,
1820 ["Ulduar77"] = -92, -- Halls of Stone
1822 ["HallsofLightning1"] = -93,
1823 ["HallsofLightning2"] = -94,
1825 ["Nexus801"] = -95, -- Oculus
1826 ["Nexus802"] = -96,
1827 ["Nexus803"] = -97,
1828 ["Nexus804"] = -98,
1830 ["CoTStratholme1"] = -99,
1831 ["CoTStratholme2"] = -100,
1833 ["UtgardePinnacle1"] = -101, -- hey they spelled it right
1834 ["UtgardePinnacle2"] = -102,
1836 ["VaultofArchavon"] = -103, -- Weirdly, Emalon is actually within the "Vault of Archavon"
1838 ["Naxxramas1"] = -104,
1839 ["Naxxramas2"] = -105,
1840 ["Naxxramas3"] = -106,
1841 ["Naxxramas4"] = -107,
1842 ["Naxxramas5"] = -108,
1843 ["Naxxramas6"] = -109,
1845 ["TheObsidianSanctum"] = -110,
1847 ["TheEyeOfEternity"] = -111,
1849 ["Ulduar"] = -112,
1850 ["Ulduar1"] = -113,
1851 ["Ulduar2"] = -114,
1852 ["Ulduar3"] = -115,
1853 ["Ulduar4"] = -116,
1855 ["TheForgeofSouls"] = -117,
1856 ["PitofSaron"] = -118,
1859 DongleStub:Register(Astrolabe, activate)
1861 local zeroData;
1862 zeroData = { xOffset = 0, height = 0, yOffset = 0, width = 0, __index = function() return zeroData end };
1863 setmetatable(zeroData, zeroData);
1864 setmetatable(WorldMapSize, zeroData);
1866 for continent, zones in pairs(Astrolabe.ContinentList) do
1867 local mapData = WorldMapSize[continent];
1868 for index, mapName in pairs(zones) do
1869 if not ( mapData.zoneData[mapName] ) then
1870 --WE HAVE A PROBLEM!!!
1871 ChatFrame1:AddMessage("Astrolabe is missing data for "..select(index, GetMapZones(continent))..".");
1872 mapData.zoneData[mapName] = zeroData;
1874 mapData[index] = mapData.zoneData[mapName];
1875 mapData.zoneData[mapName] = nil;
1880 -- register this library with AstrolabeMapMonitor, this will cause a full update if PLAYER_LOGIN has already fired
1881 local AstrolabeMapMonitor = DongleStub("AstrolabeMapMonitor");
1882 AstrolabeMapMonitor:RegisterAstrolabeLibrary(Astrolabe, LIBRARY_VERSION_MAJOR);
1885 QH_Astrolabe_Ready = true