Merge branch 'master' of git://cams.pavlovian.net/questhelper
[QuestHelper.git] / AstrolabeQH / Astrolabe.lua
blob3845114804764b61dddc47c11c497047821f38f5
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 -- WARNING!!!
44 -- DO NOT MAKE CHANGES TO THIS LIBRARY WITHOUT FIRST CHANGING THE LIBRARY_VERSION_MAJOR
45 -- STRING (to something unique) OR ELSE YOU MAY BREAK OTHER ADDONS THAT USE THIS LIBRARY!!!
46 local LIBRARY_VERSION_MAJOR = "Astrolabe-0.4-QuestHelper"
47 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
49 if not DongleStub then error(LIBRARY_VERSION_MAJOR .. " requires DongleStub.") end
50 if not DongleStub:IsNewerVersion(LIBRARY_VERSION_MAJOR, LIBRARY_VERSION_MINOR) then return end
52 local Astrolabe = {};
54 local Minimap = _G.Minimap
56 -- define local variables for Data Tables (defined at the end of this file)
57 local WorldMapSize, MinimapSize, ValidMinimapShapes, VirtualContinentIndexes;
59 function Astrolabe:GetVersion()
60 return LIBRARY_VERSION_MAJOR, LIBRARY_VERSION_MINOR;
61 end
64 --------------------------------------------------------------------------------------------------------------
65 -- Config Constants
66 --------------------------------------------------------------------------------------------------------------
68 local configConstants = {
69 MinimapUpdateMultiplier = true,
72 -- this constant is multiplied by the current framerate to determine
73 -- how many icons are updated each frame
74 Astrolabe.MinimapUpdateMultiplier = 1;
77 --------------------------------------------------------------------------------------------------------------
78 -- Working Tables
79 --------------------------------------------------------------------------------------------------------------
81 Astrolabe.LastPlayerPosition = { 0, 0, 0, 0 };
82 Astrolabe.MinimapIcons = {};
83 Astrolabe.IconsOnEdge = {};
84 Astrolabe.IconsOnEdge_GroupChangeCallbacks = {};
86 Astrolabe.MinimapIconCount = 0
87 Astrolabe.ForceNextUpdate = false;
88 Astrolabe.IconsOnEdgeChanged = false;
90 -- This variable indicates whether we know of a visible World Map or not.
91 -- The state of this variable is controlled by the AstrolabeMapMonitor library.
92 Astrolabe.WorldMapVisible = false;
94 local AddedOrUpdatedIcons = {}
95 local MinimapIconsMetatable = { __index = AddedOrUpdatedIcons }
98 --------------------------------------------------------------------------------------------------------------
99 -- Local Pointers for often used API functions
100 --------------------------------------------------------------------------------------------------------------
102 local twoPi = math.pi * 2;
103 local atan2 = math.atan2;
104 local sin = math.sin;
105 local cos = math.cos;
106 local abs = math.abs;
107 local sqrt = math.sqrt;
108 local min = math.min
109 local max = math.max
110 local yield = coroutine.yield
111 local GetFramerate = GetFramerate
114 --------------------------------------------------------------------------------------------------------------
115 -- Internal Utility Functions
116 --------------------------------------------------------------------------------------------------------------
118 local function assert(level,condition,message)
119 if not condition then
120 error(message,level)
124 local function argcheck(value, num, ...)
125 assert(1, type(num) == "number", "Bad argument #2 to 'argcheck' (number expected, got " .. type(level) .. ")")
127 for i=1,select("#", ...) do
128 if type(value) == select(i, ...) then return end
131 local types = strjoin(", ", ...)
132 local name = string.match(debugstack(2,2,0), ": in function [`<](.-)['>]")
133 error(string.format("Bad argument #%d to 'Astrolabe.%s' (%s expected, got %s)", num, name, types, type(value)), 3)
136 local function getContPosition( zoneData, z, x, y )
137 if ( z ~= 0 ) then
138 zoneData = zoneData[z];
139 x = x * zoneData.width + zoneData.xOffset;
140 y = y * zoneData.height + zoneData.yOffset;
141 else
142 x = x * zoneData.width;
143 y = y * zoneData.height;
145 return x, y;
148 --------------------------------------------------------------------------------------------------------------
149 -- Virtual Continent Functions
150 --------------------------------------------------------------------------------------------------------------
152 function Astrolabe:GetCurrentVirtualMapCZ()
153 local C, Z = GetCurrentMapContinent(), GetCurrentMapZone();
154 if C == -1 and Z == 0 then
155 -- welllllp
156 local mapname = GetMapInfo()
157 if VirtualContinentIndexes[mapname] and GetCurrentMapDungeonLevel() == 0 then
158 C = VirtualContinentIndexes[mapname]
159 Z = 1
160 elseif mapname and VirtualContinentIndexes[mapname .. GetCurrentMapDungeonLevel()] then
161 C = VirtualContinentIndexes[mapname .. GetCurrentMapDungeonLevel()]
162 Z = 1
163 elseif mapname == "CoTStratholme" and GetCurrentMapDungeonLevel() == 0 then
164 -- why do you gotta make me angry, baby
165 C = VirtualContinentIndexes["CoTStratholme2"]
166 Z = 1
169 return C, Z
172 function Astrolabe:GetCurrentVirtualMapContinent() local C, Z = self:GetCurrentVirtualMapCZ() return C end
173 function Astrolabe:GetCurrentVirtualMapZone() local C, Z = self:GetCurrentVirtualMapCZ() return Z end
175 -- Does much the same as GetMapContinents, but returns as an array and includes the virtual continents in the mix
176 function Astrolabe:GetMapVirtualContinents()
177 local rv = {GetMapContinents()}
178 for k, v in pairs(VirtualContinentIndexes) do
179 rv[v] = k .. "_Continent"
181 return rv
184 -- Does much the same as GetMapContinents, but returns as an array and includes the virtual continents in the mix
185 function Astrolabe:GetMapVirtualZones(zone)
186 for k, v in pairs(VirtualContinentIndexes) do
187 if v == zone then
188 return {[1] = k}
192 return {GetMapZones(zone)}
195 function Astrolabe:GetMapTexture(c, z)
196 for k, v in pairs(VirtualContinentIndexes) do
197 if v == c and z == 0 then
198 return k .. "_Continent"
199 elseif v == c and z == 1 then
200 return k
204 SetMapZoom(c, z)
205 return (GetMapInfo())
208 --------------------------------------------------------------------------------------------------------------
209 -- General Utility Functions
210 --------------------------------------------------------------------------------------------------------------
212 function Astrolabe:ComputeDistance( c1, z1, x1, y1, c2, z2, x2, y2 )
213 QuestHelper: Assert(c1 and z1 and x1 and y1 and c2 and z2 and x2 and y2)
214 --[[
215 argcheck(c1, 2, "number");
216 assert(3, c1 >= 0, "ComputeDistance: Illegal continent index to c1: "..c1);
217 argcheck(z1, 3, "number", "nil");
218 argcheck(x1, 4, "number");
219 argcheck(y1, 5, "number");
220 argcheck(c2, 6, "number");
221 assert(3, c2 >= 0, "ComputeDistance: Illegal continent index to c2: "..c2);
222 argcheck(z2, 7, "number", "nil");
223 argcheck(x2, 8, "number");
224 argcheck(y2, 9, "number");
225 --]]
227 z1 = z1 or 0;
228 z2 = z2 or 0;
230 local dist, xDelta, yDelta;
231 if ( c1 == c2 and z1 == z2 ) then
232 -- points in the same zone
233 local zoneData = WorldMapSize[c1];
234 if ( z1 ~= 0 ) then
235 zoneData = zoneData[z1];
237 xDelta = (x2 - x1) * zoneData.width;
238 yDelta = (y2 - y1) * zoneData.height;
240 elseif ( c1 == c2 ) then
241 -- points on the same continent
242 local zoneData = WorldMapSize[c1];
243 x1, y1 = getContPosition(zoneData, z1, x1, y1);
244 x2, y2 = getContPosition(zoneData, z2, x2, y2);
245 xDelta = (x2 - x1);
246 yDelta = (y2 - y1);
248 elseif ( c1 and c2 ) then
249 local cont1 = WorldMapSize[c1];
250 local cont2 = WorldMapSize[c2];
251 if ( cont1.parentContinent == cont2.parentContinent ) then
252 x1, y1 = getContPosition(cont1, z1, x1, y1);
253 x2, y2 = getContPosition(cont2, z2, x2, y2);
254 if ( c1 ~= cont1.parentContinent ) then
255 x1 = x1 + cont1.xOffset;
256 y1 = y1 + cont1.yOffset;
258 if ( c2 ~= cont2.parentContinent ) then
259 x2 = x2 + cont2.xOffset;
260 y2 = y2 + cont2.yOffset;
263 xDelta = x2 - x1;
264 yDelta = y2 - y1;
268 if ( xDelta and yDelta ) then
269 dist = sqrt(xDelta*xDelta + yDelta*yDelta);
271 return dist, xDelta, yDelta;
274 function Astrolabe:TranslateWorldMapPosition( C, Z, xPos, yPos, nC, nZ )
275 --[[
276 argcheck(C, 2, "number");
277 argcheck(Z, 3, "number", "nil");
278 argcheck(xPos, 4, "number");
279 argcheck(yPos, 5, "number");
280 argcheck(nC, 6, "number");
281 argcheck(nZ, 7, "number", "nil");
282 --]]
284 Z = Z or 0;
285 nZ = nZ or 0;
286 if ( nC < 0 and nC > -77 ) then
287 return;
290 local zoneData;
291 if ( C == nC and Z == nZ ) then
292 return xPos, yPos;
294 elseif ( C == nC ) then
295 -- points on the same continent
296 zoneData = WorldMapSize[C];
297 xPos, yPos = getContPosition(zoneData, Z, xPos, yPos);
298 if ( nZ ~= 0 ) then
299 zoneData = WorldMapSize[C][nZ];
300 xPos = xPos - zoneData.xOffset;
301 yPos = yPos - zoneData.yOffset;
304 elseif ( C and nC ) and ( WorldMapSize[C].parentContinent == WorldMapSize[nC].parentContinent ) then
305 -- different continents, same world
306 zoneData = WorldMapSize[C];
307 local parentContinent = zoneData.parentContinent;
308 xPos, yPos = getContPosition(zoneData, Z, xPos, yPos);
309 if ( C ~= parentContinent ) then
310 -- translate up to world map if we aren't there already
311 xPos = xPos + zoneData.xOffset;
312 yPos = yPos + zoneData.yOffset;
313 zoneData = WorldMapSize[parentContinent];
315 if ( nC ~= parentContinent ) then
316 -- translate down to the new continent
317 zoneData = WorldMapSize[nC];
318 xPos = xPos - zoneData.xOffset;
319 yPos = yPos - zoneData.yOffset;
320 if ( nZ ~= 0 ) then
321 zoneData = zoneData[nZ];
322 xPos = xPos - zoneData.xOffset;
323 yPos = yPos - zoneData.yOffset;
327 else
328 return;
331 return (xPos / zoneData.width), (yPos / zoneData.height);
334 function Astrolabe:GetAbsoluteContinentPosition( C, Z, xPos, yPos )
335 if C == -1 then -- We're in a battleground that doesn't have a virtual continent, we're just kind of fucked.
336 return
339 assert(0, type(WorldMapSize[C].parentContinent) == "number")
341 local x, y = Astrolabe:TranslateWorldMapPosition(C, Z, xPos, yPos, WorldMapSize[C].parentContinent, 0)
342 if not x or not y then return end
343 local zoneData = WorldMapSize[WorldMapSize[C].parentContinent]
344 return WorldMapSize[C].parentContinent, (x * zoneData.width), (y * zoneData.height)
347 function Astrolabe:FromAbsoluteContinentPosition(C, xPos, yPos)
348 return C, xPos / WorldMapSize[C].width, yPos / WorldMapSize[C].height
351 --*****************************************************************************
352 -- This function will do its utmost to retrieve some sort of valid position
353 -- for the specified unit, including changing the current map zoom (if needed).
354 -- Map Zoom is returned to its previous setting before this function returns.
355 --*****************************************************************************
356 function Astrolabe:GetUnitPosition( unit, noMapChange )
357 local x, y = GetPlayerMapPosition(unit);
358 if ( x <= 0 and y <= 0 ) then
359 if ( noMapChange ) then
360 -- no valid position on the current map, and we aren't allowed
361 -- to change map zoom, so return
362 return;
364 local lastCont, lastZone = GetCurrentMapContinent(), GetCurrentMapZone();
365 SetMapToCurrentZone();
366 x, y = GetPlayerMapPosition(unit);
367 if ( x <= 0 and y <= 0 ) then
368 SetMapZoom(GetCurrentMapContinent());
369 x, y = GetPlayerMapPosition(unit);
370 if ( x <= 0 and y <= 0 ) then
371 -- we are in an instance or otherwise off the continent map
372 return;
375 local C, Z = GetCurrentMapContinent(), GetCurrentMapZone();
376 if ( C ~= lastCont or Z ~= lastZone ) then
377 SetMapZoom(lastCont, lastZone); -- set map zoom back to what it was before
379 return C, Z, x, y;
381 return self:GetCurrentVirtualMapContinent(), self:GetCurrentVirtualMapZone(), x, y;
384 --*****************************************************************************
385 -- This function will do its utmost to retrieve some sort of valid position
386 -- for the specified unit, including changing the current map zoom (if needed).
387 -- However, if a monitored WorldMapFrame (See AstrolabeMapMonitor.lua) is
388 -- visible, then will simply return nil if the current zoom does not provide
389 -- a valid position for the player unit. Map Zoom is returned to its previous
390 -- setting before this function returns, if it was changed.
391 --*****************************************************************************
392 function Astrolabe:GetCurrentPlayerPosition()
393 local x, y = GetPlayerMapPosition("player");
394 if ( x <= 0 and y <= 0 ) then
395 if ( self.WorldMapVisible ) then
396 -- we know there is a visible world map, so don't cause
397 -- WORLD_MAP_UPDATE events by changing map zoom
398 return;
400 local lastCont, lastZone = GetCurrentMapContinent(), GetCurrentMapZone();
401 SetMapToCurrentZone();
402 x, y = GetPlayerMapPosition("player");
403 if ( x <= 0 and y <= 0 ) then
404 SetMapZoom(GetCurrentMapContinent());
405 x, y = GetPlayerMapPosition("player");
406 if ( x <= 0 and y <= 0 ) then
407 -- we are in an instance or otherwise off the continent map
408 return;
411 local C, Z = GetCurrentMapContinent(), GetCurrentMapZone();
413 if ( C ~= lastCont or Z ~= lastZone ) then
414 SetMapZoom(lastCont, lastZone); --set map zoom back to what it was before
416 return C, Z, x, y;
419 return self:GetCurrentVirtualMapContinent(), self:GetCurrentVirtualMapZone(), x, y;
423 --------------------------------------------------------------------------------------------------------------
424 -- Working Table Cache System
425 --------------------------------------------------------------------------------------------------------------
427 local tableCache = {};
428 tableCache["__mode"] = "v";
429 setmetatable(tableCache, tableCache);
431 local function GetWorkingTable( icon )
432 if ( tableCache[icon] ) then
433 return tableCache[icon];
434 else
435 local T = {};
436 tableCache[icon] = T;
437 return T;
442 --------------------------------------------------------------------------------------------------------------
443 -- Minimap Icon Placement
444 --------------------------------------------------------------------------------------------------------------
446 --*****************************************************************************
447 -- local variables specifically for use in this section
448 --*****************************************************************************
449 local minimapRotationEnabled = false;
450 local minimapShape = false;
452 local MinimapCompassTexture = MinimapCompassTexture;
453 local MinimapCompassRing = MiniMapCompassRing;
454 function Astrolabe:GetFacing()
455 if MinimapCompassRing then -- 3.1 hackery
456 return MinimapCompassRing:GetFacing()
457 else
458 return -GetPlayerFacing()
461 local minimapRotationOffset = -Astrolabe.GetFacing()
463 local function placeIconOnMinimap( minimap, minimapZoom, mapWidth, mapHeight, icon, dist, xDist, yDist )
464 local mapDiameter;
465 if ( Astrolabe.minimapOutside ) then
466 mapDiameter = MinimapSize.outdoor[minimapZoom];
467 else
468 mapDiameter = MinimapSize.indoor[minimapZoom];
470 local mapRadius = mapDiameter / 2;
471 local xScale = mapDiameter / mapWidth;
472 local yScale = mapDiameter / mapHeight;
473 local iconDiameter = ((icon:GetWidth() / 2) + 3) * xScale;
474 local iconOnEdge = nil;
475 local isRound = true;
477 if ( minimapRotationEnabled ) then
478 local sinTheta = sin(minimapRotationOffset)
479 local cosTheta = cos(minimapRotationOffset)
480 --[[
481 Math Note
482 The math that is acutally going on in the next 3 lines is:
483 local dx, dy = xDist, -yDist
484 xDist = (dx * cosTheta) + (dy * sinTheta)
485 yDist = -((-dx * sinTheta) + (dy * cosTheta))
487 This is because the origin for map coordinates is the top left corner
488 of the map, not the bottom left, and so we have to reverse the vertical
489 distance when doing the our rotation, and then reverse the result vertical
490 distance because this rotation formula gives us a result with the origin based
491 in the bottom left corner (of the (+, +) quadrant).
492 The actual code is a simplification of the above.
494 local dx, dy = xDist, yDist
495 xDist = (dx * cosTheta) - (dy * sinTheta)
496 yDist = (dx * sinTheta) + (dy * cosTheta)
499 if ( minimapShape and not (xDist == 0 or yDist == 0) ) then
500 isRound = (xDist < 0) and 1 or 3;
501 if ( yDist < 0 ) then
502 isRound = minimapShape[isRound];
503 else
504 isRound = minimapShape[isRound + 1];
508 -- for non-circular portions of the Minimap edge
509 if not ( isRound ) then
510 dist = max(abs(xDist), abs(yDist))
513 if ( (dist + iconDiameter) > mapRadius ) then
514 -- position along the outside of the Minimap
515 iconOnEdge = true;
516 local factor = (mapRadius - iconDiameter) / dist;
517 xDist = xDist * factor;
518 yDist = yDist * factor;
521 if ( Astrolabe.IconsOnEdge[icon] ~= iconOnEdge ) then
522 Astrolabe.IconsOnEdge[icon] = iconOnEdge;
523 Astrolabe.IconsOnEdgeChanged = true;
526 icon:ClearAllPoints();
527 icon:SetPoint("CENTER", minimap, "CENTER", xDist/xScale, -yDist/yScale);
530 function Astrolabe:PlaceIconOnMinimap( icon, continent, zone, xPos, yPos )
531 -- check argument types
532 argcheck(icon, 2, "table");
533 assert(3, icon.SetPoint and icon.ClearAllPoints, "Usage Message");
534 argcheck(continent, 3, "number");
535 argcheck(zone, 4, "number", "nil");
536 argcheck(xPos, 5, "number");
537 argcheck(yPos, 6, "number");
539 local lC, lZ, lx, ly = unpack(self.LastPlayerPosition);
540 local dist, xDist, yDist = self:ComputeDistance(lC, lZ, lx, ly, continent, zone, xPos, yPos);
541 if not ( dist ) then
542 --icon's position has no meaningful position relative to the player's current location
543 return -1;
546 local iconData = GetWorkingTable(icon);
547 if ( self.MinimapIcons[icon] ) then
548 self.MinimapIcons[icon] = nil;
549 else
550 self.MinimapIconCount = self.MinimapIconCount + 1
553 -- We know this icon's position is valid, so we need to make sure the icon placement
554 -- system is active. We call this here so that if this is the first icon being added to
555 -- an empty buffer, the full recalc will not completely redo the work done by this function
556 -- because the icon has not yet actually been placed in the buffer.
557 self.processingFrame:Show()
559 AddedOrUpdatedIcons[icon] = iconData
560 iconData.continent = continent;
561 iconData.zone = zone;
562 iconData.xPos = xPos;
563 iconData.yPos = yPos;
564 iconData.dist = dist;
565 iconData.xDist = xDist;
566 iconData.yDist = yDist;
568 minimapRotationEnabled = GetCVar("rotateMinimap") ~= "0"
569 if ( minimapRotationEnabled ) then
570 minimapRotationOffset = -Astrolabe.GetFacing()
573 -- check Minimap Shape
574 minimapShape = GetMinimapShape and ValidMinimapShapes[GetMinimapShape()];
576 -- place the icon on the Minimap and :Show() it
577 local map = Minimap
578 placeIconOnMinimap(map, map:GetZoom(), map:GetWidth(), map:GetHeight(), icon, dist, xDist, yDist);
579 icon:Show()
581 return 0;
584 function Astrolabe:RemoveIconFromMinimap( icon )
585 if not ( self.MinimapIcons[icon] ) then
586 return 1;
588 AddedOrUpdatedIcons[icon] = nil
589 self.MinimapIcons[icon] = nil;
590 self.IconsOnEdge[icon] = nil;
591 icon:Hide();
593 local MinimapIconCount = self.MinimapIconCount - 1
594 if ( MinimapIconCount <= 0 ) then
595 -- no icons left to manage
596 self.processingFrame:Hide()
597 MinimapIconCount = 0 -- because I'm paranoid
599 self.MinimapIconCount = MinimapIconCount
601 return 0;
604 function Astrolabe:RemoveAllMinimapIcons()
605 self:DumpNewIconsCache()
606 local MinimapIcons = self.MinimapIcons;
607 local IconsOnEdge = self.IconsOnEdge;
608 for k, v in pairs(MinimapIcons) do
609 MinimapIcons[k] = nil;
610 IconsOnEdge[k] = nil;
611 k:Hide();
613 self.MinimapIconCount = 0
614 self.processingFrame:Hide()
617 local lastZoom; -- to remember the last seen Minimap zoom level
619 -- local variables to track the status of the two update coroutines
620 local fullUpdateInProgress = true
621 local resetIncrementalUpdate = false
622 local resetFullUpdate = false
624 -- Incremental Update Code
626 -- local variables to track the incremental update coroutine
627 local incrementalUpdateCrashed = true
628 local incrementalUpdateThread
630 local function UpdateMinimapIconPositions( self )
631 yield()
633 while ( true ) do
634 self:DumpNewIconsCache() -- put new/updated icons into the main datacache
636 resetIncrementalUpdate = false -- by definition, the incremental update is reset if it is here
638 local C, Z, x, y = self:GetCurrentPlayerPosition();
639 if ( C and C ~= -1 ) then
640 local lastPosition = self.LastPlayerPosition;
641 local lC, lZ, lx, ly = unpack(lastPosition);
643 minimapRotationEnabled = GetCVar("rotateMinimap") ~= "0"
644 if ( minimapRotationEnabled ) then
645 minimapRotationOffset = -Astrolabe.GetFacing()
648 -- check current frame rate
649 local numPerCycle = min(50, GetFramerate() * (self.MinimapUpdateMultiplier or 1))
651 -- check Minimap Shape
652 minimapShape = GetMinimapShape and ValidMinimapShapes[GetMinimapShape()];
654 if ( lC == C and lZ == Z and lx == x and ly == y ) then
655 -- player has not moved since the last update
656 if ( lastZoom ~= Minimap:GetZoom() or self.ForceNextUpdate or minimapRotationEnabled ) then
657 local currentZoom = Minimap:GetZoom();
658 lastZoom = currentZoom;
659 local mapWidth = Minimap:GetWidth();
660 local mapHeight = Minimap:GetHeight();
661 numPerCycle = numPerCycle * 2
662 local count = 0
663 for icon, data in pairs(self.MinimapIcons) do
664 placeIconOnMinimap(Minimap, currentZoom, mapWidth, mapHeight, icon, data.dist, data.xDist, data.yDist);
666 count = count + 1
667 if ( count > numPerCycle ) then
668 count = 0
669 yield()
670 -- check if the incremental update cycle needs to be reset
671 -- because a full update has been run
672 if ( resetIncrementalUpdate ) then
673 break;
677 self.ForceNextUpdate = false;
679 else
680 local dist, xDelta, yDelta = self:ComputeDistance(lC, lZ, lx, ly, C, Z, x, y);
681 if ( dist ) then
682 local currentZoom = Minimap:GetZoom();
683 lastZoom = currentZoom;
684 local mapWidth = Minimap:GetWidth();
685 local mapHeight = Minimap:GetHeight();
686 local count = 0
687 for icon, data in pairs(self.MinimapIcons) do
688 local xDist = data.xDist - xDelta;
689 local yDist = data.yDist - yDelta;
690 local dist = sqrt(xDist*xDist + yDist*yDist);
691 placeIconOnMinimap(Minimap, currentZoom, mapWidth, mapHeight, icon, dist, xDist, yDist);
693 data.dist = dist;
694 data.xDist = xDist;
695 data.yDist = yDist;
697 count = count + 1
698 if ( count >= numPerCycle ) then
699 count = 0
700 yield()
701 -- check if the incremental update cycle needs to be reset
702 -- because a full update has been run
703 if ( resetIncrementalUpdate ) then
704 break;
708 if not ( resetIncrementalUpdate ) then
709 lastPosition[1] = C;
710 lastPosition[2] = Z;
711 lastPosition[3] = x;
712 lastPosition[4] = y;
714 else
715 self:RemoveAllMinimapIcons()
716 lastPosition[1] = C;
717 lastPosition[2] = Z;
718 lastPosition[3] = x;
719 lastPosition[4] = y;
722 else
723 if not ( self.WorldMapVisible ) then
724 self.processingFrame:Hide();
728 -- if we've been reset, then we want to start the new cycle immediately
729 if not ( resetIncrementalUpdate ) then
730 yield()
735 function Astrolabe:UpdateMinimapIconPositions()
736 if ( fullUpdateInProgress ) then
737 -- if we're in the middle a a full update, we want to finish that first
738 self:CalculateMinimapIconPositions()
739 else
740 if ( incrementalUpdateCrashed ) then
741 incrementalUpdateThread = coroutine.wrap(UpdateMinimapIconPositions)
742 incrementalUpdateThread(self) --initialize the thread
744 incrementalUpdateCrashed = true
745 incrementalUpdateThread()
746 incrementalUpdateCrashed = false
751 -- Full Update Code
753 -- local variables to track the full update coroutine
754 local fullUpdateCrashed = true
755 local fullUpdateThread
757 local function CalculateMinimapIconPositions( self )
758 yield()
760 while ( true ) do
761 self:DumpNewIconsCache() -- put new/updated icons into the main datacache
763 resetFullUpdate = false -- by definition, the full update is reset if it is here
765 fullUpdateInProgress = true -- set the flag the says a full update is in progress
767 local C, Z, x, y = self:GetCurrentPlayerPosition();
768 if ( C and C ~= -1 ) then
769 minimapRotationEnabled = GetCVar("rotateMinimap") ~= "0"
770 if ( minimapRotationEnabled ) then
771 minimapRotationOffset = Astrolabe.GetFacing()
774 -- check current frame rate
775 local numPerCycle = GetFramerate() * (self.MinimapUpdateMultiplier or 1) * 2
777 -- check Minimap Shape
778 minimapShape = GetMinimapShape and ValidMinimapShapes[GetMinimapShape()];
780 local currentZoom = Minimap:GetZoom();
781 lastZoom = currentZoom;
782 local mapWidth = Minimap:GetWidth();
783 local mapHeight = Minimap:GetHeight();
784 local count = 0
785 for icon, data in pairs(self.MinimapIcons) do
786 local dist, xDist, yDist = self:ComputeDistance(C, Z, x, y, data.continent, data.zone, data.xPos, data.yPos);
787 if ( dist ) then
788 placeIconOnMinimap(Minimap, currentZoom, mapWidth, mapHeight, icon, dist, xDist, yDist);
790 data.dist = dist;
791 data.xDist = xDist;
792 data.yDist = yDist;
793 else
794 self:RemoveIconFromMinimap(icon)
797 count = count + 1
798 if ( count >= numPerCycle ) then
799 count = 0
800 yield()
801 -- check if we need to restart due to the full update being reset
802 if ( resetFullUpdate ) then
803 break;
808 if not ( resetFullUpdate ) then
809 local lastPosition = self.LastPlayerPosition;
810 lastPosition[1] = C;
811 lastPosition[2] = Z;
812 lastPosition[3] = x;
813 lastPosition[4] = y;
815 resetIncrementalUpdate = true
817 else
818 if not ( self.WorldMapVisible ) then
819 self.processingFrame:Hide();
823 -- if we've been reset, then we want to start the new cycle immediately
824 if not ( resetFullUpdate ) then
825 fullUpdateInProgress = false
826 yield()
831 function Astrolabe:CalculateMinimapIconPositions( reset )
832 if ( fullUpdateCrashed ) then
833 fullUpdateThread = coroutine.wrap(CalculateMinimapIconPositions)
834 fullUpdateThread(self) --initialize the thread
835 elseif ( reset ) then
836 resetFullUpdate = true
838 fullUpdateCrashed = true
839 fullUpdateThread()
840 fullUpdateCrashed = false
842 -- return result flag
843 if ( fullUpdateInProgress ) then
844 return 1 -- full update started, but did not complete on this cycle
846 else
847 if ( resetIncrementalUpdate ) then
848 return 0 -- update completed
849 else
850 return -1 -- full update did no occur for some reason
857 function Astrolabe:GetDistanceToIcon( icon )
858 local data = self.MinimapIcons[icon];
859 if ( data ) then
860 return data.dist, data.xDist, data.yDist;
864 function Astrolabe:IsIconOnEdge( icon )
865 return self.IconsOnEdge[icon];
868 function Astrolabe:GetDirectionToIcon( icon )
869 local data = self.MinimapIcons[icon];
870 if ( data ) then
871 local dir = atan2(data.xDist, -(data.yDist))
872 if ( dir > 0 ) then
873 return twoPi - dir;
874 else
875 return -dir;
880 function Astrolabe:Register_OnEdgeChanged_Callback( func, ident )
881 -- check argument types
882 argcheck(func, 2, "function");
884 self.IconsOnEdge_GroupChangeCallbacks[func] = ident;
887 --*****************************************************************************
888 -- INTERNAL USE ONLY PLEASE!!!
889 -- Calling this function at the wrong time can cause errors
890 --*****************************************************************************
891 function Astrolabe:DumpNewIconsCache()
892 local MinimapIcons = self.MinimapIcons
893 for icon, data in pairs(AddedOrUpdatedIcons) do
894 MinimapIcons[icon] = data
895 AddedOrUpdatedIcons[icon] = nil
897 -- we now need to restart any updates that were in progress
898 resetIncrementalUpdate = true
899 resetFullUpdate = true
903 --------------------------------------------------------------------------------------------------------------
904 -- World Map Icon Placement
905 --------------------------------------------------------------------------------------------------------------
907 function Astrolabe:PlaceIconOnWorldMap( worldMapFrame, icon, continent, zone, xPos, yPos )
908 -- check argument types
909 argcheck(worldMapFrame, 2, "table");
910 assert(3, worldMapFrame.GetWidth and worldMapFrame.GetHeight, "Usage Message");
911 argcheck(icon, 3, "table");
912 assert(3, icon.SetPoint and icon.ClearAllPoints, "Usage Message");
913 argcheck(continent, 4, "number");
914 argcheck(zone, 5, "number", "nil");
915 argcheck(xPos, 6, "number");
916 argcheck(yPos, 7, "number");
918 local C, Z = self:GetCurrentVirtualMapCZ();
919 local nX, nY = self:TranslateWorldMapPosition(continent, zone, xPos, yPos, C, Z);
921 -- anchor and :Show() the icon if it is within the boundry of the current map, :Hide() it otherwise
922 if ( nX and nY and (0 < nX and nX <= 1) and (0 < nY and nY <= 1) ) then
923 icon:ClearAllPoints();
924 icon:SetPoint("CENTER", worldMapFrame, "TOPLEFT", nX * worldMapFrame:GetWidth(), -nY * worldMapFrame:GetHeight());
925 icon:Show();
926 else
927 icon:Hide();
929 return nX, nY;
933 --------------------------------------------------------------------------------------------------------------
934 -- Handler Scripts
935 --------------------------------------------------------------------------------------------------------------
937 function Astrolabe:OnEvent( frame, event )
938 if ( event == "MINIMAP_UPDATE_ZOOM" ) then
939 -- update minimap zoom scale
940 local curZoom = Minimap:GetZoom();
941 if ( GetCVar("minimapZoom") == GetCVar("minimapInsideZoom") ) then
942 if ( curZoom < 2 ) then
943 Minimap:SetZoom(curZoom + 1);
944 else
945 Minimap:SetZoom(curZoom - 1);
948 if ( GetCVar("minimapZoom")+0 == Minimap:GetZoom() ) then
949 self.minimapOutside = true;
950 else
951 self.minimapOutside = false;
953 Minimap:SetZoom(curZoom);
955 -- re-calculate all Minimap Icon positions
956 if ( frame:IsVisible() ) then
957 self:CalculateMinimapIconPositions(true);
960 elseif ( event == "PLAYER_LEAVING_WORLD" ) then
961 frame:Hide(); -- yes, I know this is redunant
962 self:RemoveAllMinimapIcons(); --dump all minimap icons
964 elseif ( event == "PLAYER_ENTERING_WORLD" ) then
965 frame:Show();
966 if not ( frame:IsVisible() ) then
967 -- do the minimap recalculation anyways if the OnShow script didn't execute
968 -- this is done to ensure the accuracy of information about icons that were
969 -- inserted while the Player was in the process of zoning
970 self:CalculateMinimapIconPositions(true);
973 elseif ( event == "ZONE_CHANGED_NEW_AREA" ) then
974 frame:Hide();
975 frame:Show();
980 function Astrolabe:OnUpdate( frame, elapsed )
981 -- on-edge group changed call-backs
982 if ( self.IconsOnEdgeChanged ) then
983 self.IconsOnEdgeChanged = false;
984 for func in pairs(self.IconsOnEdge_GroupChangeCallbacks) do
985 pcall(func);
989 self:UpdateMinimapIconPositions();
992 function Astrolabe:OnShow( frame )
993 -- set the world map to a zoom with a valid player position
994 if not ( self.WorldMapVisible ) then
995 SetMapToCurrentZone();
997 local C, Z = Astrolabe:GetCurrentPlayerPosition();
998 if ( C and C ~= -1 ) then
999 if C >= 0 then -- If we're in Wackyland, we can't change the world map anyway, so at least it's probably right
1000 SetMapZoom(C, Z);
1002 else
1003 frame:Hide();
1004 return
1007 -- re-calculate minimap icon positions
1008 self:CalculateMinimapIconPositions(true);
1010 if ( self.MinimapIconCount <= 0 ) then
1011 -- no icons left to manage
1012 self.processingFrame:Hide()
1016 -- called by AstrolabMapMonitor when all world maps are hidden
1017 function Astrolabe:AllWorldMapsHidden()
1018 if ( IsLoggedIn() ) then
1019 self.processingFrame:Hide();
1020 self.processingFrame:Show();
1024 function Astrolabe:SetMinimapObject(minimap)
1025 Minimap = minimap
1026 self:UpdateMinimapIconPositions()
1029 --------------------------------------------------------------------------------------------------------------
1030 -- Library Registration
1031 --------------------------------------------------------------------------------------------------------------
1033 local function activate( newInstance, oldInstance )
1034 if ( oldInstance ) then -- this is an upgrade activate
1035 if ( oldInstance.DumpNewIconsCache ) then
1036 oldInstance:DumpNewIconsCache()
1038 for k, v in pairs(oldInstance) do
1039 if ( type(v) ~= "function" and (not configConstants[k]) ) then
1040 newInstance[k] = v;
1043 -- sync up the current MinimapIconCount value
1044 local iconCount = 0
1045 for _ in pairs(newInstance.MinimapIcons) do
1046 iconCount = iconCount + 1
1048 newInstance.MinimapIconCount = iconCount
1050 Astrolabe = oldInstance;
1051 else
1052 local frame = CreateFrame("Frame");
1053 newInstance.processingFrame = frame;
1055 newInstance.ContinentList = Astrolabe:GetMapVirtualContinents();
1057 for C in pairs(newInstance.ContinentList) do
1058 local zones = Astrolabe:GetMapVirtualZones(C);
1059 newInstance.ContinentList[C] = zones;
1060 for Z in ipairs(zones) do
1061 zones[Z] = Astrolabe:GetMapTexture(C, Z);
1065 configConstants = nil -- we don't need this anymore
1067 local frame = newInstance.processingFrame;
1068 frame:Hide();
1069 frame:SetParent("Minimap");
1070 frame:UnregisterAllEvents();
1071 frame:RegisterEvent("MINIMAP_UPDATE_ZOOM");
1072 frame:RegisterEvent("PLAYER_LEAVING_WORLD");
1073 frame:RegisterEvent("PLAYER_ENTERING_WORLD");
1074 frame:RegisterEvent("ZONE_CHANGED_NEW_AREA");
1075 frame:SetScript("OnEvent",
1076 function( frame, event, ... )
1077 Astrolabe:OnEvent(frame, event, ...);
1080 frame:SetScript("OnUpdate",
1081 function( frame, elapsed )
1082 Astrolabe:OnUpdate(frame, elapsed);
1085 frame:SetScript("OnShow",
1086 function( frame )
1087 Astrolabe:OnShow(frame);
1091 setmetatable(Astrolabe.MinimapIcons, MinimapIconsMetatable)
1094 --------------------------------------------------------------------------------------------------------------
1095 -- Data
1096 --------------------------------------------------------------------------------------------------------------
1098 -- diameter of the Minimap in game yards at
1099 -- the various possible zoom levels
1100 MinimapSize = {
1101 indoor = {
1102 [0] = 300, -- scale
1103 [1] = 240, -- 1.25
1104 [2] = 180, -- 5/3
1105 [3] = 120, -- 2.5
1106 [4] = 80, -- 3.75
1107 [5] = 50, -- 6
1109 outdoor = {
1110 [0] = 466 + 2/3, -- scale
1111 [1] = 400, -- 7/6
1112 [2] = 333 + 1/3, -- 1.4
1113 [3] = 266 + 2/6, -- 1.75
1114 [4] = 200, -- 7/3
1115 [5] = 133 + 1/3, -- 3.5
1119 ValidMinimapShapes = {
1120 -- { upper-left, lower-left, upper-right, lower-right }
1121 ["SQUARE"] = { false, false, false, false },
1122 ["CORNER-TOPLEFT"] = { true, false, false, false },
1123 ["CORNER-TOPRIGHT"] = { false, false, true, false },
1124 ["CORNER-BOTTOMLEFT"] = { false, true, false, false },
1125 ["CORNER-BOTTOMRIGHT"] = { false, false, false, true },
1126 ["SIDE-LEFT"] = { true, true, false, false },
1127 ["SIDE-RIGHT"] = { false, false, true, true },
1128 ["SIDE-TOP"] = { true, false, true, false },
1129 ["SIDE-BOTTOM"] = { false, true, false, true },
1130 ["TRICORNER-TOPLEFT"] = { true, true, true, false },
1131 ["TRICORNER-TOPRIGHT"] = { true, false, true, true },
1132 ["TRICORNER-BOTTOMLEFT"] = { true, true, false, true },
1133 ["TRICORNER-BOTTOMRIGHT"] = { false, true, true, true },
1136 -- distances across and offsets of the world maps
1137 -- in game yards
1138 WorldMapSize = {
1139 -- World Map of Azeroth
1140 [0] = {
1141 parentContinent = 0,
1142 height = 29688.932932224,
1143 width = 44537.340058402,
1145 -- Kalimdor
1146 { -- [1]
1147 parentContinent = 0,
1148 height = 24533.025279205,
1149 width = 36800.210572494,
1150 xOffset = -8311.793923510446,
1151 yOffset = 1815.215685280706,
1152 zoneData = {
1153 Ashenvale = {
1154 height = 3843.722811451077,
1155 width = 5766.728884700476,
1156 xOffset = 15366.76755576002,
1157 yOffset = 8126.925260781192,
1159 Aszhara = {
1160 height = 3381.225696279877,
1161 width = 5070.888165752819,
1162 xOffset = 20343.90485013144,
1163 yOffset = 7458.180046130774,
1165 AzuremystIsle = {
1166 height = 2714.561862167815,
1167 width = 4070.883253576282,
1168 xOffset = 9966.70736478994,
1169 yOffset = 5460.278138661794,
1171 Barrens = {
1172 height = 6756.202067150937,
1173 width = 10133.44343943073,
1174 xOffset = 14443.84117394525,
1175 yOffset = 11187.32013604393,
1177 BloodmystIsle = {
1178 height = 2174.984710698752,
1179 width = 3262.517428121028,
1180 xOffset = 9541.713418184554,
1181 yOffset = 3424.874558234072,
1183 Darkshore = {
1184 height = 4366.636219106706,
1185 width = 6550.06962983463,
1186 xOffset = 14125.08809600818,
1187 yOffset = 4466.534412478246,
1189 Darnassis = {
1190 height = 705.7248633938184,
1191 width = 1058.342927027606,
1192 xOffset = 14128.39258617903,
1193 yOffset = 2561.565012455802,
1195 Desolace = {
1196 height = 2997.895174253872,
1197 width = 4495.882023201739,
1198 xOffset = 12833.40729836031,
1199 yOffset = 12347.72848626745,
1201 Durotar = {
1202 height = 3524.975114832228,
1203 width = 5287.558038649864,
1204 xOffset = 19029.30699887344,
1205 yOffset = 10991.48801260963,
1207 Dustwallow = {
1208 height = 3499.975146240067,
1209 width = 5250.057259791282,
1210 xOffset = 18041.79657043901,
1211 yOffset = 14833.12751666842,
1213 Felwood = {
1214 height = 3833.305958270781,
1215 width = 5750.062034325837,
1216 xOffset = 15425.10163773161,
1217 yOffset = 5666.526367166872,
1219 Feralas = {
1220 height = 4633.30011661694,
1221 width = 6950.075260353015,
1222 xOffset = 11625.06045254075,
1223 yOffset = 15166.45834829251,
1225 Moonglade = {
1226 height = 1539.572509508711,
1227 width = 2308.356845256911,
1228 xOffset = 18448.05172159372,
1229 yOffset = 4308.20254319874,
1231 Mulgore = {
1232 height = 3424.975945100366,
1233 width = 5137.555355060729,
1234 xOffset = 15018.84750987729,
1235 yOffset = 13072.72336630089,
1237 Ogrimmar = {
1238 height = 935.4100697456119,
1239 width = 1402.621211455915,
1240 xOffset = 20747.42666130799,
1241 yOffset = 10525.94769396873,
1243 Silithus = {
1244 height = 2322.899061688691,
1245 width = 3483.371975265956,
1246 xOffset = 14529.25864164056,
1247 yOffset = 18758.10068625832,
1249 StonetalonMountains = {
1250 height = 3256.226691571251,
1251 width = 4883.385977951072,
1252 xOffset = 13820.91773479217,
1253 yOffset = 9883.162892509636,
1255 Tanaris = {
1256 height = 4599.965662459992,
1257 width = 6900.073766103516,
1258 xOffset = 17285.539010128,
1259 yOffset = 18674.7673661939,
1261 Teldrassil = {
1262 height = 3393.726923234355,
1263 width = 5091.720903621394,
1264 xOffset = 13252.16205313556,
1265 yOffset = 968.6418744503761,
1267 TheExodar = {
1268 height = 704.6826864472878,
1269 width = 1056.781131437323,
1270 xOffset = 10533.08314172693,
1271 yOffset = 6276.205331713322,
1273 ThousandNeedles = {
1274 height = 2933.312180524323,
1275 width = 4400.046681282484,
1276 xOffset = 17500.12437633161,
1277 yOffset = 16766.44698282704,
1279 ThunderBluff = {
1280 height = 695.8282721105132,
1281 width = 1043.761263579803,
1282 xOffset = 16550.11410485969,
1283 yOffset = 13649.80260929285,
1285 UngoroCrater = {
1286 height = 2466.647220780505,
1287 width = 3700.040077455555,
1288 xOffset = 16533.44712326324,
1289 yOffset = 18766.4334494793,
1291 Winterspring = {
1292 height = 4733.299561046713,
1293 width = 7100.077599808275,
1294 xOffset = 17383.45606038691,
1295 yOffset = 4266.536453420381,
1299 -- Eastern Kingdoms
1300 { -- [2]
1301 parentContinent = 0,
1302 height = 27149.795290881,
1303 width = 40741.175327834,
1304 xOffset = 14407.1086092051,
1305 yOffset = 290.3230897653046,
1306 zoneData = {
1307 Alterac = {
1308 height = 1866.673586850316,
1309 width = 2800.000436369314,
1310 xOffset = 17388.63313899802,
1311 yOffset = 9676.382605411302,
1313 Arathi = {
1314 height = 2400.0092446309,
1315 width = 3599.999380663208,
1316 xOffset = 19038.63328411639,
1317 yOffset = 11309.72201070757,
1319 Badlands = {
1320 height = 1658.340965090961,
1321 width = 2487.498490907989,
1322 xOffset = 20251.1337564772,
1323 yOffset = 17065.99404487956,
1325 BlastedLands = {
1326 height = 2233.343415116865,
1327 width = 3349.999381676505,
1328 xOffset = 19413.63362865575,
1329 yOffset = 21743.09582955139,
1331 BurningSteppes = {
1332 height = 1952.091972408385,
1333 width = 2929.16694293186,
1334 xOffset = 18438.633261567,
1335 yOffset = 18207.66513379744,
1337 DeadwindPass = {
1338 height = 1666.673818905317,
1339 width = 2499.999888210889,
1340 xOffset = 19005.29993968603,
1341 yOffset = 21043.0932328648,
1343 DunMorogh = {
1344 height = 3283.345779814337,
1345 width = 4924.998791911572,
1346 xOffset = 16369.8840376619,
1347 yOffset = 15053.48695195484,
1349 Duskwood = {
1350 height = 1800.007653419076,
1351 width = 2699.999669551933,
1352 xOffset = 17338.63354148773,
1353 yOffset = 20893.09259181909,
1355 EasternPlaguelands = {
1356 height = 2581.259876367526,
1357 width = 3870.832396995169,
1358 xOffset = 20357.38356562001,
1359 yOffset = 7376.373692430854,
1361 Elwynn = {
1362 height = 2314.591970284716,
1363 width = 3470.831971412848,
1364 xOffset = 16636.55099386465,
1365 yOffset = 19116.0027890283,
1367 EversongWoods = {
1368 height = 3283.346366715794,
1369 width = 4924.998483501337,
1370 xOffset = 20259.46725884782,
1371 yOffset = 2534.687567863296,
1373 Ghostlands = {
1374 height = 2200.008945183733,
1375 width = 3300.002855743766,
1376 xOffset = 21055.29786070095,
1377 yOffset = 5309.698546426793,
1379 Hilsbrad = {
1380 height = 2133.341840477916,
1381 width = 3200.000391416799,
1382 xOffset = 17105.29968281043,
1383 yOffset = 10776.38652289269,
1385 Hinterlands = {
1386 height = 2566.676323518885,
1387 width = 3849.998492380244,
1388 xOffset = 19746.96704279287,
1389 yOffset = 9709.715966757984,
1391 Ironforge = {
1392 height = 527.6056771582851,
1393 width = 790.6252518322632,
1394 xOffset = 18885.55815177769,
1395 yOffset = 15745.64795436116,
1397 LochModan = {
1398 height = 1839.590356444166,
1399 width = 2758.33360594204,
1400 xOffset = 20165.71623436714,
1401 yOffset = 15663.90573348468,
1403 Redridge = {
1404 height = 1447.922213393415,
1405 width = 2170.833229570681,
1406 xOffset = 19742.79960560691,
1407 yOffset = 19751.42209395218,
1409 SearingGorge = {
1410 height = 1487.505203229038,
1411 width = 2231.250200533406,
1412 xOffset = 18494.88325409831,
1413 yOffset = 17276.41231120941,
1415 SilvermoonCity = {
1416 height = 806.7751969249011,
1417 width = 1211.458551923779,
1418 xOffset = 22172.71573747824,
1419 yOffset = 3422.647395021269,
1421 Silverpine = {
1422 height = 2800.011187621704,
1423 width = 4200.000573479695,
1424 xOffset = 14721.96646274185,
1425 yOffset = 9509.714741967448,
1427 Stormwind = {
1428 height = 896.3598437319051,
1429 width = 1344.270269919159,
1430 xOffset = 16790.9956264139,
1431 yOffset = 19455.27053790398,
1433 Stranglethorn = {
1434 height = 4254.18312444072,
1435 width = 6381.248484543122,
1436 xOffset = 15951.13375783437,
1437 yOffset = 22345.18258706305,
1439 Sunwell = {
1440 height = 2218.756638064149,
1441 width = 3327.084777999942,
1442 xOffset = 21074.0484502027,
1443 yOffset = 7.595267688679496,
1445 SwampOfSorrows = {
1446 height = 1529.173695058727,
1447 width = 2293.753807610138,
1448 xOffset = 20394.88183258176,
1449 yOffset = 20797.25913588854,
1451 Tirisfal = {
1452 height = 3012.510490816506,
1453 width = 4518.749381850256,
1454 xOffset = 15138.63417865412,
1455 yOffset = 7338.874503644808,
1457 Undercity = {
1458 height = 640.1067253394195,
1459 width = 959.3752013853186,
1460 xOffset = 17298.77399735696,
1461 yOffset = 9298.435338905521,
1463 WesternPlaguelands = {
1464 height = 2866.677213191588,
1465 width = 4299.998717025251,
1466 xOffset = 17755.30067544475,
1467 yOffset = 7809.708745090687,
1469 Westfall = {
1470 height = 2333.342039971409,
1471 width = 3500.001170481545,
1472 xOffset = 15155.29922254704,
1473 yOffset = 20576.42557120998,
1475 Wetlands = {
1476 height = 2756.260286844545,
1477 width = 4135.414389381328,
1478 xOffset = 18561.55091405621,
1479 yOffset = 13324.31339403164,
1483 -- Outland
1484 { -- [3]
1485 parentContinent = 3,
1486 height = 11642.355227091,
1487 width = 17463.987300595,
1488 zoneData = {
1489 BladesEdgeMountains = {
1490 height = 3616.553511321226,
1491 width = 5424.972055480694,
1492 xOffset = 4150.184214583454,
1493 yOffset = 1412.98225932006,
1495 Hellfire = {
1496 height = 3443.642450656037,
1497 width = 5164.556104714847,
1498 xOffset = 7456.417230912641,
1499 yOffset = 4339.973750274888,
1501 Nagrand = {
1502 height = 3683.218538167106,
1503 width = 5524.971495006054,
1504 xOffset = 2700.192018521809,
1505 yOffset = 5779.511974812862,
1507 Netherstorm = {
1508 height = 3716.550608724641,
1509 width = 5574.970083688359,
1510 xOffset = 7512.667416095402,
1511 yOffset = 365.0979827402549,
1513 ShadowmoonValley = {
1514 height = 3666.552070430093,
1515 width = 5499.971770418525,
1516 xOffset = 8770.993458280615,
1517 yOffset = 7769.033264592288,
1519 ShattrathCity = {
1520 height = 870.8059516186869,
1521 width = 1306.242821388422,
1522 xOffset = 6860.744740098593,
1523 yOffset = 7295.086120456203,
1525 TerokkarForest = {
1526 height = 3599.887783533737,
1527 width = 5399.971351016305,
1528 xOffset = 5912.675516998205,
1529 yOffset = 6821.146319031154,
1531 Zangarmarsh = {
1532 height = 3351.978710181591,
1533 width = 5027.057650868489,
1534 xOffset = 3521.020638264577,
1535 yOffset = 3885.821278366336,
1541 --- WotLK Adjustments, now permanently enabled. Someday I should probably merge these in.
1542 if true then
1543 WorldMapSize[0].height = 31809.64859753034;
1544 WorldMapSize[0].width = 47714.27770954026;
1546 WorldMapSize[1].xOffset = -8590.409362625034;
1547 WorldMapSize[1].yOffset = 5628.694276155668;
1549 WorldMapSize[2].xOffset = 18542.31268111796;
1550 WorldMapSize[2].yOffset = 3585.574682467752;
1551 WorldMapSize[2].zoneData.Stormwind = {
1552 height = 1158.33686894901,
1553 width = 1737.498058940429,
1554 xOffset = 16449.05164642256,
1555 yOffset = 19172.25350774846,
1557 WorldMapSize[2].zoneData.EasternPlaguelands = {
1558 height = 2687.510360231216,
1559 width = 4031.249051993366,
1560 xOffset = 20459.46801235962,
1561 yOffset = 7472.207045901617,
1564 WorldMapSize[4] = {
1565 parentContinent = 0,
1566 height = 11834.31067391958,
1567 width = 17751.3936186856,
1568 xOffset = 16020.94093549576,
1569 yOffset = 454.2464807713226,
1570 zoneData = {
1571 BoreanTundra = {
1572 height = 3843.765503862232,
1573 width = 5764.58206497758,
1574 xOffset = 646.3186767730767,
1575 yOffset = 5695.480016983896,
1577 CrystalsongForest = {
1578 height = 1814.590053385046,
1579 width = 2722.916164555434,
1580 xOffset = 7773.400227973558,
1581 yOffset = 4091.307437548815,
1583 Dalaran = {
1584 height = 553.3419356683534,
1585 width = 830.014625253355,
1586 xOffset = 8164.640128758279,
1587 yOffset = 4526.722218200071,
1589 Dragonblight = {
1590 height = 3739.597759999098,
1591 width = 5608.331259502691,
1592 xOffset = 5590.067753073641,
1593 yOffset = 5018.394106536425,
1595 GrizzlyHills = {
1596 height = 3500.013349296343,
1597 width = 5249.9986179934,
1598 xOffset = 10327.56614428777,
1599 yOffset = 5076.727864214266,
1601 HowlingFjord = {
1602 height = 4031.266275060274,
1603 width = 6045.831339550668,
1604 xOffset = 10615.0658552538,
1605 yOffset = 7476.736868262738,
1607 IcecrownGlacier = {
1608 height = 4181.266519840844,
1609 width = 6270.832975322177,
1610 xOffset = 3773.401695036191,
1611 yOffset = 1166.296622984233,
1613 LakeWintergrasp = {
1614 height = 1983.342901980711,
1615 width = 2974.999377667768,
1616 xOffset = 4887.984320612982,
1617 yOffset = 4876.725348039468,
1619 SholazarBasin = {
1620 height = 2904.177559586215,
1621 width = 4356.248328680455,
1622 xOffset = 2287.985279107324,
1623 yOffset = 3305.887993444818,
1625 TheStormPeaks = {
1626 height = 4741.684940421732,
1627 width = 7112.498205872217,
1628 xOffset = 7375.483315518691,
1629 yOffset = 395.4596828327046,
1631 ZulDrak = {
1632 height = 3329.179510740043,
1633 width = 4993.747919923504,
1634 xOffset = 9817.150055203074,
1635 yOffset = 2924.636381254688,
1640 local function VContinent(index, name, size)
1641 assert(1, not WorldMapSize[index], "denied")
1643 WorldMapSize[index] = {
1644 parentContinent = index,
1645 height = size,
1646 width = size*1.5,
1647 zoneData = { },
1649 WorldMapSize[index].zoneData[name] = {
1650 height = size,
1651 width = size*1.5,
1652 xOffset = 0,
1653 yOffset = 0,
1657 VContinent(-77, "ScarletEnclave", 2125)
1660 VContinent(-80, "UtgardeKeep1", 100) -- temporary value
1661 VContinent(-81, "UtgardeKeep2", 100) -- temporary value
1662 VContinent(-82, "UtgardeKeep3", 100) -- temporary value
1664 VContinent(-83, "TheNexus", 734.2)
1666 VContinent(-84, "AzjolNerub1", 100) -- temporary value
1667 VContinent(-85, "AzjolNerub2", 100) -- temporary value
1668 VContinent(-86, "AzjolNerub3", 100) -- temporary value
1670 VContinent(-87, "Ahnkahet", 648.3)
1672 VContinent(-88, "DrakTharonKeep1", 100) -- temporary value
1673 VContinent(-89, "DrakTharonKeep2", 100) -- temporary value
1675 VContinent(-90, "VioletHold", 170.83)
1677 VContinent(-91, "Gundrak", 603.35)
1679 VContinent(-92, "Ulduar77", 613.5) -- Halls of Stone
1681 VContinent(-93, "HallsofLightning1", 100) -- temporary value
1682 VContinent(-94, "HallsofLightning2", 100) -- temporary value
1684 VContinent(-95, "Nexus801", 100) -- temporary value -- Oculus
1685 VContinent(-96, "Nexus802", 100) -- temporary value
1686 VContinent(-97, "Nexus803", 100) -- temporary value
1687 VContinent(-98, "Nexus804", 100) -- temporary value
1689 VContinent(-99, "CoTStratholme1", 750.2)
1690 VContinent(-100, "CoTStratholme2", 1216.66)
1692 VContinent(-101, "UtgardePinnacle1", 100) -- temporary value -- hey they spelled it right
1693 VContinent(-102, "UtgardePinnacle2", 100) -- temporary value
1695 VContinent(-103, "VaultofArchavon", 603.25) -- temporary value -- Weirdly, Emalon is actually within the "Vault of Archavon"
1697 VContinent(-104, "Naxxramas1", 1237.5) -- construct quarter
1698 VContinent(-105, "Naxxramas2", 1237.5) -- arachnid quarter
1699 VContinent(-106, "Naxxramas3", 1237.5) -- military quarter
1700 VContinent(-107, "Naxxramas4", 1237.5) -- plague quarter
1701 VContinent(-108, "Naxxramas5", 1379.9) -- overview
1702 VContinent(-109, "Naxxramas6", 437.3) -- lair
1704 VContinent(-110, "TheObsidianSanctum", 775.1)
1706 VContinent(-111, "TheEyeOfEternity", 286.7)
1708 VContinent(-112, "Ulduar", 2191.7) -- temporary value
1709 VContinent(-113, "Ulduar1", 446.5) -- temporary value
1710 VContinent(-114, "Ulduar2", 885.6) -- temporary value
1711 VContinent(-115, "Ulduar3", 100) -- temporary value
1712 VContinent(-116, "Ulduar4", 100) -- temporary value
1715 VirtualContinentIndexes = { -- Don't change values here, since programs might want to store them
1716 ["ScarletEnclave"] = -77,
1718 ["UtgardeKeep1"] = -80,
1719 ["UtgardeKeep2"] = -81,
1720 ["UtgardeKeep3"] = -82,
1722 ["TheNexus"] = -83,
1724 ["AzjolNerub1"] = -84,
1725 ["AzjolNerub2"] = -85,
1726 ["AzjolNerub3"] = -86,
1728 ["Ahnkahet"] = -87,
1730 ["DrakTharonKeep1"] = -88,
1731 ["DrakTharonKeep2"] = -89,
1733 ["VioletHold"] = -90,
1735 ["Gundrak"] = -91,
1737 ["Ulduar77"] = -92, -- Halls of Stone
1739 ["HallsofLightning1"] = -93,
1740 ["HallsofLightning2"] = -94,
1742 ["Nexus801"] = -95, -- Oculus
1743 ["Nexus802"] = -96,
1744 ["Nexus803"] = -97,
1745 ["Nexus804"] = -98,
1747 ["CoTStratholme1"] = -99,
1748 ["CoTStratholme2"] = -100,
1750 ["UtgardePinnacle1"] = -101, -- hey they spelled it right
1751 ["UtgardePinnacle2"] = -102,
1753 ["VaultofArchavon"] = -103, -- Weirdly, Emalon is actually within the "Vault of Archavon"
1755 ["Naxxramas1"] = -104,
1756 ["Naxxramas2"] = -105,
1757 ["Naxxramas3"] = -106,
1758 ["Naxxramas4"] = -107,
1759 ["Naxxramas5"] = -108,
1760 ["Naxxramas6"] = -109,
1762 ["TheObsidianSanctum"] = -110,
1764 ["TheEyeOfEternity"] = -111,
1766 ["Ulduar"] = -112,
1767 ["Ulduar1"] = -113,
1768 ["Ulduar2"] = -114,
1769 ["Ulduar3"] = -115,
1770 ["Ulduar4"] = -116,
1773 DongleStub:Register(Astrolabe, activate)
1775 local zeroData;
1776 zeroData = { xOffset = 0, height = 0, yOffset = 0, width = 0, __index = function() return zeroData end };
1777 setmetatable(zeroData, zeroData);
1778 setmetatable(WorldMapSize, zeroData);
1780 for continent, zones in pairs(Astrolabe.ContinentList) do
1781 local mapData = WorldMapSize[continent];
1782 for index, mapName in pairs(zones) do
1783 if not ( mapData.zoneData[mapName] ) then
1784 --WE HAVE A PROBLEM!!!
1785 ChatFrame1:AddMessage("Astrolabe is missing data for "..select(index, GetVirtualMapZones(continent))..".");
1786 mapData.zoneData[mapName] = zeroData;
1788 mapData[index] = mapData.zoneData[mapName];
1789 mapData.zoneData[mapName] = nil;
1794 -- register this library with AstrolabeMapMonitor, this will cause a full update if PLAYER_LOGIN has already fired
1795 local AstrolabeMapMonitor = DongleStub("AstrolabeMapMonitor");
1796 AstrolabeMapMonitor:RegisterAstrolabeLibrary(Astrolabe, LIBRARY_VERSION_MAJOR);