try to instrument more proper-installation stuff
[QuestHelper.git] / AstrolabeQH / Astrolabe.lua
blob01b25bb9f2c0c05ad92c493a201c3192bdb3fc70
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 x = x * zoneData.width + zoneData.xOffset;
143 y = y * zoneData.height + zoneData.yOffset;
144 else
145 x = x * zoneData.width;
146 y = y * zoneData.height;
148 return x, y;
151 --------------------------------------------------------------------------------------------------------------
152 -- Virtual Continent Functions
153 --------------------------------------------------------------------------------------------------------------
155 function Astrolabe:GetCurrentVirtualMapCZ()
156 local C, Z = GetCurrentMapContinent(), GetCurrentMapZone();
157 if C == -1 and Z == 0 then
158 -- welllllp
159 local mapname = GetMapInfo()
160 if VirtualContinentIndexes[mapname] and GetCurrentMapDungeonLevel() == 0 then
161 C = VirtualContinentIndexes[mapname]
162 Z = 1
163 elseif mapname and VirtualContinentIndexes[mapname .. GetCurrentMapDungeonLevel()] then
164 C = VirtualContinentIndexes[mapname .. GetCurrentMapDungeonLevel()]
165 Z = 1
166 elseif mapname == "CoTStratholme" and GetCurrentMapDungeonLevel() == 0 then
167 -- why do you gotta make me angry, baby
168 C = VirtualContinentIndexes["CoTStratholme2"]
169 Z = 1
172 return C, Z
175 function Astrolabe:GetCurrentVirtualMapContinent() local C, Z = self:GetCurrentVirtualMapCZ() return C end
176 function Astrolabe:GetCurrentVirtualMapZone() local C, Z = self:GetCurrentVirtualMapCZ() return Z end
178 -- Does much the same as GetMapContinents, but returns as an array and includes the virtual continents in the mix
179 function Astrolabe:GetMapVirtualContinents()
180 local rv = {GetMapContinents()}
181 for k, v in pairs(VirtualContinentIndexes) do
182 rv[v] = k .. "_Continent"
184 return rv
187 -- Does much the same as GetMapContinents, but returns as an array and includes the virtual continents in the mix
188 function Astrolabe:GetMapVirtualZones(zone)
189 for k, v in pairs(VirtualContinentIndexes) do
190 if v == zone then
191 return {[1] = k}
195 return {GetMapZones(zone)}
198 function Astrolabe:GetMapTexture(c, z)
199 for k, v in pairs(VirtualContinentIndexes) do
200 if v == c and z == 0 then
201 return k .. "_Continent"
202 elseif v == c and z == 1 then
203 return k
207 SetMapZoom(c, z)
208 return (GetMapInfo())
211 --------------------------------------------------------------------------------------------------------------
212 -- General Utility Functions
213 --------------------------------------------------------------------------------------------------------------
215 function Astrolabe:ComputeDistance( c1, z1, x1, y1, c2, z2, x2, y2 )
216 QuestHelper: Assert(c1 and z1 and x1 and y1 and c2 and z2 and x2 and y2)
217 --[[
218 argcheck(c1, 2, "number");
219 assert(3, c1 >= 0, "ComputeDistance: Illegal continent index to c1: "..c1);
220 argcheck(z1, 3, "number", "nil");
221 argcheck(x1, 4, "number");
222 argcheck(y1, 5, "number");
223 argcheck(c2, 6, "number");
224 assert(3, c2 >= 0, "ComputeDistance: Illegal continent index to c2: "..c2);
225 argcheck(z2, 7, "number", "nil");
226 argcheck(x2, 8, "number");
227 argcheck(y2, 9, "number");
228 --]]
230 z1 = z1 or 0;
231 z2 = z2 or 0;
233 local dist, xDelta, yDelta;
234 if ( c1 == c2 and z1 == z2 ) then
235 -- points in the same zone
236 local zoneData = WorldMapSize[c1];
237 if ( z1 ~= 0 ) then
238 zoneData = zoneData[z1];
240 xDelta = (x2 - x1) * zoneData.width;
241 yDelta = (y2 - y1) * zoneData.height;
243 elseif ( c1 == c2 ) then
244 -- points on the same continent
245 local zoneData = WorldMapSize[c1];
246 x1, y1 = getContPosition(zoneData, z1, x1, y1);
247 x2, y2 = getContPosition(zoneData, z2, x2, y2);
248 xDelta = (x2 - x1);
249 yDelta = (y2 - y1);
251 elseif ( c1 and c2 ) then
252 local cont1 = WorldMapSize[c1];
253 local cont2 = WorldMapSize[c2];
254 if ( cont1.parentContinent == cont2.parentContinent ) then
255 x1, y1 = getContPosition(cont1, z1, x1, y1);
256 x2, y2 = getContPosition(cont2, z2, x2, y2);
257 if ( c1 ~= cont1.parentContinent ) then
258 x1 = x1 + cont1.xOffset;
259 y1 = y1 + cont1.yOffset;
261 if ( c2 ~= cont2.parentContinent ) then
262 x2 = x2 + cont2.xOffset;
263 y2 = y2 + cont2.yOffset;
266 xDelta = x2 - x1;
267 yDelta = y2 - y1;
271 if ( xDelta and yDelta ) then
272 dist = sqrt(xDelta*xDelta + yDelta*yDelta);
274 return dist, xDelta, yDelta;
277 function Astrolabe:TranslateWorldMapPosition( C, Z, xPos, yPos, nC, nZ )
278 --[[
279 argcheck(C, 2, "number");
280 argcheck(Z, 3, "number", "nil");
281 argcheck(xPos, 4, "number");
282 argcheck(yPos, 5, "number");
283 argcheck(nC, 6, "number");
284 argcheck(nZ, 7, "number", "nil");
285 --]]
287 Z = Z or 0;
288 nZ = nZ or 0;
289 if ( nC < 0 and nC > -77 ) then
290 return;
293 local zoneData;
294 if ( C == nC and Z == nZ ) then
295 return xPos, yPos;
297 elseif ( C == nC ) then
298 -- points on the same continent
299 zoneData = WorldMapSize[C];
300 xPos, yPos = getContPosition(zoneData, Z, xPos, yPos);
301 if ( nZ ~= 0 ) then
302 zoneData = WorldMapSize[C][nZ];
303 xPos = xPos - zoneData.xOffset;
304 yPos = yPos - zoneData.yOffset;
307 elseif ( C and nC ) and ( WorldMapSize[C].parentContinent == WorldMapSize[nC].parentContinent ) then
308 -- different continents, same world
309 zoneData = WorldMapSize[C];
310 local parentContinent = zoneData.parentContinent;
311 xPos, yPos = getContPosition(zoneData, Z, xPos, yPos);
312 if ( C ~= parentContinent ) then
313 -- translate up to world map if we aren't there already
314 xPos = xPos + zoneData.xOffset;
315 yPos = yPos + zoneData.yOffset;
316 zoneData = WorldMapSize[parentContinent];
318 if ( nC ~= parentContinent ) then
319 -- translate down to the new continent
320 zoneData = WorldMapSize[nC];
321 xPos = xPos - zoneData.xOffset;
322 yPos = yPos - zoneData.yOffset;
323 if ( nZ ~= 0 ) then
324 zoneData = zoneData[nZ];
325 xPos = xPos - zoneData.xOffset;
326 yPos = yPos - zoneData.yOffset;
330 else
331 return;
334 return (xPos / zoneData.width), (yPos / zoneData.height);
337 function Astrolabe:GetAbsoluteContinentPosition( C, Z, xPos, yPos )
338 if C == -1 then -- We're in a battleground that doesn't have a virtual continent, we're just kind of fucked.
339 return
342 assert(0, type(WorldMapSize[C].parentContinent) == "number")
344 local x, y = Astrolabe:TranslateWorldMapPosition(C, Z, xPos, yPos, WorldMapSize[C].parentContinent, 0)
345 if not x or not y then return end
346 local zoneData = WorldMapSize[WorldMapSize[C].parentContinent]
347 return WorldMapSize[C].parentContinent, (x * zoneData.width), (y * zoneData.height)
350 function Astrolabe:FromAbsoluteContinentPosition(C, xPos, yPos)
351 return C, xPos / WorldMapSize[C].width, yPos / WorldMapSize[C].height
354 function Astrolabe:GetZoneWidth(c, z)
355 if z ~= 0 then
356 return WorldMapSize[c][z].width
357 else
358 return WorldMapSize[c].width
362 --*****************************************************************************
363 -- This function will do its utmost to retrieve some sort of valid position
364 -- for the specified unit, including changing the current map zoom (if needed).
365 -- Map Zoom is returned to its previous setting before this function returns.
366 --*****************************************************************************
367 function Astrolabe:GetUnitPosition( unit, noMapChange )
368 local x, y = GetPlayerMapPosition(unit);
369 if ( x <= 0 and y <= 0 ) then
370 if ( noMapChange ) then
371 -- no valid position on the current map, and we aren't allowed
372 -- to change map zoom, so return
373 return;
375 local lastCont, lastZone = GetCurrentMapContinent(), GetCurrentMapZone();
376 SetMapToCurrentZone();
377 x, y = GetPlayerMapPosition(unit);
378 if ( x <= 0 and y <= 0 ) then
379 SetMapZoom(GetCurrentMapContinent());
380 x, y = GetPlayerMapPosition(unit);
381 if ( x <= 0 and y <= 0 ) then
382 -- we are in an instance or otherwise off the continent map
383 return;
386 local C, Z = GetCurrentMapContinent(), GetCurrentMapZone();
387 if ( C ~= lastCont or Z ~= lastZone ) then
388 SetMapZoom(lastCont, lastZone); -- set map zoom back to what it was before
390 return C, Z, x, y;
392 return self:GetCurrentVirtualMapContinent(), self:GetCurrentVirtualMapZone(), x, y;
395 --*****************************************************************************
396 -- This function will do its utmost to retrieve some sort of valid position
397 -- for the specified unit, including changing the current map zoom (if needed).
398 -- However, if a monitored WorldMapFrame (See AstrolabeMapMonitor.lua) is
399 -- visible, then will simply return nil if the current zoom does not provide
400 -- a valid position for the player unit. Map Zoom is returned to its previous
401 -- setting before this function returns, if it was changed.
402 --*****************************************************************************
403 function Astrolabe:GetCurrentPlayerPosition()
404 local x, y = GetPlayerMapPosition("player");
405 if ( x <= 0 and y <= 0 ) then
406 if ( self.WorldMapVisible ) then
407 -- we know there is a visible world map, so don't cause
408 -- WORLD_MAP_UPDATE events by changing map zoom
409 return;
411 local lastCont, lastZone = GetCurrentMapContinent(), GetCurrentMapZone();
412 SetMapToCurrentZone();
413 x, y = GetPlayerMapPosition("player");
414 if ( x <= 0 and y <= 0 ) then
415 SetMapZoom(GetCurrentMapContinent());
416 x, y = GetPlayerMapPosition("player");
417 if ( x <= 0 and y <= 0 ) then
418 -- we are in an instance or otherwise off the continent map
419 return;
422 local C, Z = GetCurrentMapContinent(), GetCurrentMapZone();
424 if ( C ~= lastCont or Z ~= lastZone ) then
425 SetMapZoom(lastCont, lastZone); --set map zoom back to what it was before
427 return C, Z, x, y;
430 return self:GetCurrentVirtualMapContinent(), self:GetCurrentVirtualMapZone(), x, y;
434 --------------------------------------------------------------------------------------------------------------
435 -- Working Table Cache System
436 --------------------------------------------------------------------------------------------------------------
438 local tableCache = {};
439 tableCache["__mode"] = "v";
440 setmetatable(tableCache, tableCache);
442 local function GetWorkingTable( icon )
443 if ( tableCache[icon] ) then
444 return tableCache[icon];
445 else
446 local T = {};
447 tableCache[icon] = T;
448 return T;
453 --------------------------------------------------------------------------------------------------------------
454 -- Minimap Icon Placement
455 --------------------------------------------------------------------------------------------------------------
457 --*****************************************************************************
458 -- local variables specifically for use in this section
459 --*****************************************************************************
460 local minimapRotationEnabled = false;
461 local minimapShape = false;
463 local MinimapCompassTexture = MinimapCompassTexture;
464 local MinimapCompassRing = MiniMapCompassRing;
465 function Astrolabe:GetFacing()
466 if MinimapCompassRing then -- 3.1 hackery
467 return MinimapCompassRing:GetFacing()
468 else
469 return -GetPlayerFacing()
472 local minimapRotationOffset = -Astrolabe.GetFacing()
474 local function placeIconOnMinimap( minimap, minimapZoom, mapWidth, mapHeight, icon, dist, xDist, yDist )
475 local mapDiameter;
476 if ( Astrolabe.minimapOutside ) then
477 mapDiameter = MinimapSize.outdoor[minimapZoom];
478 else
479 mapDiameter = MinimapSize.indoor[minimapZoom];
481 local mapRadius = mapDiameter / 2;
482 local xScale = mapDiameter / mapWidth;
483 local yScale = mapDiameter / mapHeight;
484 local iconDiameter = ((icon:GetWidth() / 2) + 3) * xScale;
485 local iconOnEdge = nil;
486 local isRound = true;
488 if ( minimapRotationEnabled ) then
489 local sinTheta = sin(minimapRotationOffset)
490 local cosTheta = cos(minimapRotationOffset)
491 --[[
492 Math Note
493 The math that is acutally going on in the next 3 lines is:
494 local dx, dy = xDist, -yDist
495 xDist = (dx * cosTheta) + (dy * sinTheta)
496 yDist = -((-dx * sinTheta) + (dy * cosTheta))
498 This is because the origin for map coordinates is the top left corner
499 of the map, not the bottom left, and so we have to reverse the vertical
500 distance when doing the our rotation, and then reverse the result vertical
501 distance because this rotation formula gives us a result with the origin based
502 in the bottom left corner (of the (+, +) quadrant).
503 The actual code is a simplification of the above.
505 local dx, dy = xDist, yDist
506 xDist = (dx * cosTheta) - (dy * sinTheta)
507 yDist = (dx * sinTheta) + (dy * cosTheta)
510 if ( minimapShape and not (xDist == 0 or yDist == 0) ) then
511 isRound = (xDist < 0) and 1 or 3;
512 if ( yDist < 0 ) then
513 isRound = minimapShape[isRound];
514 else
515 isRound = minimapShape[isRound + 1];
519 -- for non-circular portions of the Minimap edge
520 if not ( isRound ) then
521 dist = max(abs(xDist), abs(yDist))
524 if ( (dist + iconDiameter) > mapRadius ) then
525 -- position along the outside of the Minimap
526 iconOnEdge = true;
527 local factor = (mapRadius - iconDiameter) / dist;
528 xDist = xDist * factor;
529 yDist = yDist * factor;
532 if ( Astrolabe.IconsOnEdge[icon] ~= iconOnEdge ) then
533 Astrolabe.IconsOnEdge[icon] = iconOnEdge;
534 Astrolabe.IconsOnEdgeChanged = true;
537 icon:ClearAllPoints();
538 icon:SetPoint("CENTER", minimap, "CENTER", xDist/xScale, -yDist/yScale);
541 function Astrolabe:PlaceIconOnMinimap( icon, continent, zone, xPos, yPos )
542 -- check argument types
543 argcheck(icon, 2, "table");
544 assert(3, icon.SetPoint and icon.ClearAllPoints, "Usage Message");
545 argcheck(continent, 3, "number");
546 argcheck(zone, 4, "number", "nil");
547 argcheck(xPos, 5, "number");
548 argcheck(yPos, 6, "number");
550 local lC, lZ, lx, ly = unpack(self.LastPlayerPosition);
551 local dist, xDist, yDist = self:ComputeDistance(lC, lZ, lx, ly, continent, zone, xPos, yPos);
552 if not ( dist ) then
553 --icon's position has no meaningful position relative to the player's current location
554 return -1;
557 local iconData = GetWorkingTable(icon);
558 if ( self.MinimapIcons[icon] ) then
559 self.MinimapIcons[icon] = nil;
560 else
561 self.MinimapIconCount = self.MinimapIconCount + 1
564 -- We know this icon's position is valid, so we need to make sure the icon placement
565 -- system is active. We call this here so that if this is the first icon being added to
566 -- an empty buffer, the full recalc will not completely redo the work done by this function
567 -- because the icon has not yet actually been placed in the buffer.
568 self.processingFrame:Show()
570 AddedOrUpdatedIcons[icon] = iconData
571 iconData.continent = continent;
572 iconData.zone = zone;
573 iconData.xPos = xPos;
574 iconData.yPos = yPos;
575 iconData.dist = dist;
576 iconData.xDist = xDist;
577 iconData.yDist = yDist;
579 minimapRotationEnabled = GetCVar("rotateMinimap") ~= "0"
580 if ( minimapRotationEnabled ) then
581 minimapRotationOffset = -Astrolabe.GetFacing()
584 -- check Minimap Shape
585 minimapShape = GetMinimapShape and ValidMinimapShapes[GetMinimapShape()];
587 -- place the icon on the Minimap and :Show() it
588 local map = Minimap
589 placeIconOnMinimap(map, map:GetZoom(), map:GetWidth(), map:GetHeight(), icon, dist, xDist, yDist);
590 icon:Show()
592 return 0;
595 function Astrolabe:RemoveIconFromMinimap( icon )
596 if not ( self.MinimapIcons[icon] ) then
597 return 1;
599 AddedOrUpdatedIcons[icon] = nil
600 self.MinimapIcons[icon] = nil;
601 self.IconsOnEdge[icon] = nil;
602 icon:Hide();
604 local MinimapIconCount = self.MinimapIconCount - 1
605 if ( MinimapIconCount <= 0 ) then
606 -- no icons left to manage
607 self.processingFrame:Hide()
608 MinimapIconCount = 0 -- because I'm paranoid
610 self.MinimapIconCount = MinimapIconCount
612 return 0;
615 function Astrolabe:RemoveAllMinimapIcons()
616 self:DumpNewIconsCache()
617 local MinimapIcons = self.MinimapIcons;
618 local IconsOnEdge = self.IconsOnEdge;
619 for k, v in pairs(MinimapIcons) do
620 MinimapIcons[k] = nil;
621 IconsOnEdge[k] = nil;
622 k:Hide();
624 self.MinimapIconCount = 0
625 self.processingFrame:Hide()
628 local lastZoom; -- to remember the last seen Minimap zoom level
630 -- local variables to track the status of the two update coroutines
631 local fullUpdateInProgress = true
632 local resetIncrementalUpdate = false
633 local resetFullUpdate = false
635 -- Incremental Update Code
637 -- local variables to track the incremental update coroutine
638 local incrementalUpdateCrashed = true
639 local incrementalUpdateThread
641 local function UpdateMinimapIconPositions( self )
642 yield()
644 while ( true ) do
645 self:DumpNewIconsCache() -- put new/updated icons into the main datacache
647 resetIncrementalUpdate = false -- by definition, the incremental update is reset if it is here
649 local C, Z, x, y = self:GetCurrentPlayerPosition();
650 if ( C and C ~= -1 ) then
651 local lastPosition = self.LastPlayerPosition;
652 local lC, lZ, lx, ly = unpack(lastPosition);
654 minimapRotationEnabled = GetCVar("rotateMinimap") ~= "0"
655 if ( minimapRotationEnabled ) then
656 minimapRotationOffset = -Astrolabe.GetFacing()
659 -- check current frame rate
660 local numPerCycle = min(50, GetFramerate() * (self.MinimapUpdateMultiplier or 1))
662 -- check Minimap Shape
663 minimapShape = GetMinimapShape and ValidMinimapShapes[GetMinimapShape()];
665 if ( lC == C and lZ == Z and lx == x and ly == y ) then
666 -- player has not moved since the last update
667 if ( lastZoom ~= Minimap:GetZoom() or self.ForceNextUpdate or minimapRotationEnabled ) then
668 local currentZoom = Minimap:GetZoom();
669 lastZoom = currentZoom;
670 local mapWidth = Minimap:GetWidth();
671 local mapHeight = Minimap:GetHeight();
672 numPerCycle = numPerCycle * 2
673 local count = 0
674 for icon, data in pairs(self.MinimapIcons) do
675 placeIconOnMinimap(Minimap, currentZoom, mapWidth, mapHeight, icon, data.dist, data.xDist, data.yDist);
677 count = count + 1
678 if ( count > numPerCycle ) then
679 count = 0
680 yield()
681 -- check if the incremental update cycle needs to be reset
682 -- because a full update has been run
683 if ( resetIncrementalUpdate ) then
684 break;
688 self.ForceNextUpdate = false;
690 else
691 local dist, xDelta, yDelta = self:ComputeDistance(lC, lZ, lx, ly, C, Z, x, y);
692 if ( dist ) then
693 local currentZoom = Minimap:GetZoom();
694 lastZoom = currentZoom;
695 local mapWidth = Minimap:GetWidth();
696 local mapHeight = Minimap:GetHeight();
697 local count = 0
698 for icon, data in pairs(self.MinimapIcons) do
699 local xDist = data.xDist - xDelta;
700 local yDist = data.yDist - yDelta;
701 local dist = sqrt(xDist*xDist + yDist*yDist);
702 placeIconOnMinimap(Minimap, currentZoom, mapWidth, mapHeight, icon, dist, xDist, yDist);
704 data.dist = dist;
705 data.xDist = xDist;
706 data.yDist = yDist;
708 count = count + 1
709 if ( count >= numPerCycle ) then
710 count = 0
711 yield()
712 -- check if the incremental update cycle needs to be reset
713 -- because a full update has been run
714 if ( resetIncrementalUpdate ) then
715 break;
719 if not ( resetIncrementalUpdate ) then
720 lastPosition[1] = C;
721 lastPosition[2] = Z;
722 lastPosition[3] = x;
723 lastPosition[4] = y;
725 else
726 self:RemoveAllMinimapIcons()
727 lastPosition[1] = C;
728 lastPosition[2] = Z;
729 lastPosition[3] = x;
730 lastPosition[4] = y;
733 else
734 if not ( self.WorldMapVisible ) then
735 self.processingFrame:Hide();
739 -- if we've been reset, then we want to start the new cycle immediately
740 if not ( resetIncrementalUpdate ) then
741 yield()
746 function Astrolabe:UpdateMinimapIconPositions()
747 if ( fullUpdateInProgress ) then
748 -- if we're in the middle a a full update, we want to finish that first
749 self:CalculateMinimapIconPositions()
750 else
751 if ( incrementalUpdateCrashed ) then
752 incrementalUpdateThread = coroutine.wrap(UpdateMinimapIconPositions)
753 incrementalUpdateThread(self) --initialize the thread
755 incrementalUpdateCrashed = true
756 incrementalUpdateThread()
757 incrementalUpdateCrashed = false
762 -- Full Update Code
764 -- local variables to track the full update coroutine
765 local fullUpdateCrashed = true
766 local fullUpdateThread
768 local function CalculateMinimapIconPositions( self )
769 yield()
771 while ( true ) do
772 self:DumpNewIconsCache() -- put new/updated icons into the main datacache
774 resetFullUpdate = false -- by definition, the full update is reset if it is here
776 fullUpdateInProgress = true -- set the flag the says a full update is in progress
778 local C, Z, x, y = self:GetCurrentPlayerPosition();
779 if ( C and C ~= -1 ) then
780 minimapRotationEnabled = GetCVar("rotateMinimap") ~= "0"
781 if ( minimapRotationEnabled ) then
782 minimapRotationOffset = Astrolabe.GetFacing()
785 -- check current frame rate
786 local numPerCycle = GetFramerate() * (self.MinimapUpdateMultiplier or 1) * 2
788 -- check Minimap Shape
789 minimapShape = GetMinimapShape and ValidMinimapShapes[GetMinimapShape()];
791 local currentZoom = Minimap:GetZoom();
792 lastZoom = currentZoom;
793 local mapWidth = Minimap:GetWidth();
794 local mapHeight = Minimap:GetHeight();
795 local count = 0
796 for icon, data in pairs(self.MinimapIcons) do
797 local dist, xDist, yDist = self:ComputeDistance(C, Z, x, y, data.continent, data.zone, data.xPos, data.yPos);
798 if ( dist ) then
799 placeIconOnMinimap(Minimap, currentZoom, mapWidth, mapHeight, icon, dist, xDist, yDist);
801 data.dist = dist;
802 data.xDist = xDist;
803 data.yDist = yDist;
804 else
805 self:RemoveIconFromMinimap(icon)
808 count = count + 1
809 if ( count >= numPerCycle ) then
810 count = 0
811 yield()
812 -- check if we need to restart due to the full update being reset
813 if ( resetFullUpdate ) then
814 break;
819 if not ( resetFullUpdate ) then
820 local lastPosition = self.LastPlayerPosition;
821 lastPosition[1] = C;
822 lastPosition[2] = Z;
823 lastPosition[3] = x;
824 lastPosition[4] = y;
826 resetIncrementalUpdate = true
828 else
829 if not ( self.WorldMapVisible ) then
830 self.processingFrame:Hide();
834 -- if we've been reset, then we want to start the new cycle immediately
835 if not ( resetFullUpdate ) then
836 fullUpdateInProgress = false
837 yield()
842 function Astrolabe:CalculateMinimapIconPositions( reset )
843 if ( fullUpdateCrashed ) then
844 fullUpdateThread = coroutine.wrap(CalculateMinimapIconPositions)
845 fullUpdateThread(self) --initialize the thread
846 elseif ( reset ) then
847 resetFullUpdate = true
849 fullUpdateCrashed = true
850 fullUpdateThread()
851 fullUpdateCrashed = false
853 -- return result flag
854 if ( fullUpdateInProgress ) then
855 return 1 -- full update started, but did not complete on this cycle
857 else
858 if ( resetIncrementalUpdate ) then
859 return 0 -- update completed
860 else
861 return -1 -- full update did no occur for some reason
868 function Astrolabe:GetDistanceToIcon( icon )
869 local data = self.MinimapIcons[icon];
870 if ( data ) then
871 return data.dist, data.xDist, data.yDist;
875 function Astrolabe:IsIconOnEdge( icon )
876 return self.IconsOnEdge[icon];
879 function Astrolabe:GetDirectionToIcon( icon )
880 local data = self.MinimapIcons[icon];
881 if ( data ) then
882 local dir = atan2(data.xDist, -(data.yDist))
883 if ( dir > 0 ) then
884 return twoPi - dir;
885 else
886 return -dir;
891 function Astrolabe:Register_OnEdgeChanged_Callback( func, ident )
892 -- check argument types
893 argcheck(func, 2, "function");
895 self.IconsOnEdge_GroupChangeCallbacks[func] = ident;
898 --*****************************************************************************
899 -- INTERNAL USE ONLY PLEASE!!!
900 -- Calling this function at the wrong time can cause errors
901 --*****************************************************************************
902 function Astrolabe:DumpNewIconsCache()
903 local MinimapIcons = self.MinimapIcons
904 for icon, data in pairs(AddedOrUpdatedIcons) do
905 MinimapIcons[icon] = data
906 AddedOrUpdatedIcons[icon] = nil
908 -- we now need to restart any updates that were in progress
909 resetIncrementalUpdate = true
910 resetFullUpdate = true
914 --------------------------------------------------------------------------------------------------------------
915 -- World Map Icon Placement
916 --------------------------------------------------------------------------------------------------------------
918 function Astrolabe:PlaceIconOnWorldMap( worldMapFrame, icon, continent, zone, xPos, yPos )
919 -- check argument types
920 argcheck(worldMapFrame, 2, "table");
921 assert(3, worldMapFrame.GetWidth and worldMapFrame.GetHeight, "Usage Message");
922 argcheck(icon, 3, "table");
923 assert(3, icon.SetPoint and icon.ClearAllPoints, "Usage Message");
924 argcheck(continent, 4, "number");
925 argcheck(zone, 5, "number", "nil");
926 argcheck(xPos, 6, "number");
927 argcheck(yPos, 7, "number");
929 local C, Z = self:GetCurrentVirtualMapCZ();
930 local nX, nY = self:TranslateWorldMapPosition(continent, zone, xPos, yPos, C, Z);
932 -- anchor and :Show() the icon if it is within the boundry of the current map, :Hide() it otherwise
933 if ( nX and nY and (0 < nX and nX <= 1) and (0 < nY and nY <= 1) ) then
934 icon:ClearAllPoints();
935 icon:SetPoint("CENTER", worldMapFrame, "TOPLEFT", nX * worldMapFrame:GetWidth(), -nY * worldMapFrame:GetHeight());
936 icon:Show();
937 else
938 icon:Hide();
940 return nX, nY;
944 --------------------------------------------------------------------------------------------------------------
945 -- Handler Scripts
946 --------------------------------------------------------------------------------------------------------------
948 function Astrolabe:OnEvent( frame, event )
949 if ( event == "MINIMAP_UPDATE_ZOOM" ) then
950 -- update minimap zoom scale
951 local curZoom = Minimap:GetZoom();
952 if ( GetCVar("minimapZoom") == GetCVar("minimapInsideZoom") ) then
953 if ( curZoom < 2 ) then
954 Minimap:SetZoom(curZoom + 1);
955 else
956 Minimap:SetZoom(curZoom - 1);
959 if ( GetCVar("minimapZoom")+0 == Minimap:GetZoom() ) then
960 self.minimapOutside = true;
961 else
962 self.minimapOutside = false;
964 Minimap:SetZoom(curZoom);
966 -- re-calculate all Minimap Icon positions
967 if ( frame:IsVisible() ) then
968 self:CalculateMinimapIconPositions(true);
971 elseif ( event == "PLAYER_LEAVING_WORLD" ) then
972 frame:Hide(); -- yes, I know this is redunant
973 self:RemoveAllMinimapIcons(); --dump all minimap icons
975 elseif ( event == "PLAYER_ENTERING_WORLD" ) then
976 frame:Show();
977 if not ( frame:IsVisible() ) then
978 -- do the minimap recalculation anyways if the OnShow script didn't execute
979 -- this is done to ensure the accuracy of information about icons that were
980 -- inserted while the Player was in the process of zoning
981 self:CalculateMinimapIconPositions(true);
984 elseif ( event == "ZONE_CHANGED_NEW_AREA" ) then
985 frame:Hide();
986 frame:Show();
991 function Astrolabe:OnUpdate( frame, elapsed )
992 -- on-edge group changed call-backs
993 if ( self.IconsOnEdgeChanged ) then
994 self.IconsOnEdgeChanged = false;
995 for func in pairs(self.IconsOnEdge_GroupChangeCallbacks) do
996 pcall(func);
1000 self:UpdateMinimapIconPositions();
1003 function Astrolabe:OnShow( frame )
1004 -- set the world map to a zoom with a valid player position
1005 if not ( self.WorldMapVisible ) then
1006 SetMapToCurrentZone();
1008 local C, Z = Astrolabe:GetCurrentPlayerPosition();
1009 if ( C and C ~= -1 ) then
1010 if C >= 0 then -- If we're in Wackyland, we can't change the world map anyway, so at least it's probably right
1011 SetMapZoom(C, Z);
1013 else
1014 frame:Hide();
1015 return
1018 -- re-calculate minimap icon positions
1019 self:CalculateMinimapIconPositions(true);
1021 if ( self.MinimapIconCount <= 0 ) then
1022 -- no icons left to manage
1023 self.processingFrame:Hide()
1027 -- called by AstrolabMapMonitor when all world maps are hidden
1028 function Astrolabe:AllWorldMapsHidden()
1029 if ( IsLoggedIn() ) then
1030 self.processingFrame:Hide();
1031 self.processingFrame:Show();
1035 function Astrolabe:SetMinimapObject(minimap)
1036 Minimap = minimap
1037 self:UpdateMinimapIconPositions()
1040 --------------------------------------------------------------------------------------------------------------
1041 -- Library Registration
1042 --------------------------------------------------------------------------------------------------------------
1044 local function activate( newInstance, oldInstance )
1045 if ( oldInstance ) then -- this is an upgrade activate
1046 if ( oldInstance.DumpNewIconsCache ) then
1047 oldInstance:DumpNewIconsCache()
1049 for k, v in pairs(oldInstance) do
1050 if ( type(v) ~= "function" and (not configConstants[k]) ) then
1051 newInstance[k] = v;
1054 -- sync up the current MinimapIconCount value
1055 local iconCount = 0
1056 for _ in pairs(newInstance.MinimapIcons) do
1057 iconCount = iconCount + 1
1059 newInstance.MinimapIconCount = iconCount
1061 Astrolabe = oldInstance;
1062 else
1063 local frame = CreateFrame("Frame");
1064 newInstance.processingFrame = frame;
1066 newInstance.ContinentList = Astrolabe:GetMapVirtualContinents();
1068 for C in pairs(newInstance.ContinentList) do
1069 local zones = Astrolabe:GetMapVirtualZones(C);
1070 newInstance.ContinentList[C] = zones;
1071 for Z in ipairs(zones) do
1072 zones[Z] = Astrolabe:GetMapTexture(C, Z);
1076 configConstants = nil -- we don't need this anymore
1078 local frame = newInstance.processingFrame;
1079 frame:Hide();
1080 frame:SetParent("Minimap");
1081 frame:UnregisterAllEvents();
1082 frame:RegisterEvent("MINIMAP_UPDATE_ZOOM");
1083 frame:RegisterEvent("PLAYER_LEAVING_WORLD");
1084 frame:RegisterEvent("PLAYER_ENTERING_WORLD");
1085 frame:RegisterEvent("ZONE_CHANGED_NEW_AREA");
1086 frame:SetScript("OnEvent",
1087 function( frame, event, ... )
1088 Astrolabe:OnEvent(frame, event, ...);
1091 frame:SetScript("OnUpdate",
1092 function( frame, elapsed )
1093 Astrolabe:OnUpdate(frame, elapsed);
1096 frame:SetScript("OnShow",
1097 function( frame )
1098 Astrolabe:OnShow(frame);
1102 setmetatable(Astrolabe.MinimapIcons, MinimapIconsMetatable)
1105 --------------------------------------------------------------------------------------------------------------
1106 -- Data
1107 --------------------------------------------------------------------------------------------------------------
1109 -- diameter of the Minimap in game yards at
1110 -- the various possible zoom levels
1111 MinimapSize = {
1112 indoor = {
1113 [0] = 300, -- scale
1114 [1] = 240, -- 1.25
1115 [2] = 180, -- 5/3
1116 [3] = 120, -- 2.5
1117 [4] = 80, -- 3.75
1118 [5] = 50, -- 6
1120 outdoor = {
1121 [0] = 466 + 2/3, -- scale
1122 [1] = 400, -- 7/6
1123 [2] = 333 + 1/3, -- 1.4
1124 [3] = 266 + 2/6, -- 1.75
1125 [4] = 200, -- 7/3
1126 [5] = 133 + 1/3, -- 3.5
1130 ValidMinimapShapes = {
1131 -- { upper-left, lower-left, upper-right, lower-right }
1132 ["SQUARE"] = { false, false, false, false },
1133 ["CORNER-TOPLEFT"] = { true, false, false, false },
1134 ["CORNER-TOPRIGHT"] = { false, false, true, false },
1135 ["CORNER-BOTTOMLEFT"] = { false, true, false, false },
1136 ["CORNER-BOTTOMRIGHT"] = { false, false, false, true },
1137 ["SIDE-LEFT"] = { true, true, false, false },
1138 ["SIDE-RIGHT"] = { false, false, true, true },
1139 ["SIDE-TOP"] = { true, false, true, false },
1140 ["SIDE-BOTTOM"] = { false, true, false, true },
1141 ["TRICORNER-TOPLEFT"] = { true, true, true, false },
1142 ["TRICORNER-TOPRIGHT"] = { true, false, true, true },
1143 ["TRICORNER-BOTTOMLEFT"] = { true, true, false, true },
1144 ["TRICORNER-BOTTOMRIGHT"] = { false, true, true, true },
1147 -- distances across and offsets of the world maps
1148 -- in game yards
1149 WorldMapSize = {
1150 -- World Map of Azeroth
1151 [0] = {
1152 parentContinent = 0,
1153 height = 29688.932932224,
1154 width = 44537.340058402,
1156 -- Kalimdor
1157 { -- [1]
1158 parentContinent = 0,
1159 height = 24533.025279205,
1160 width = 36800.210572494,
1161 xOffset = -8311.793923510446,
1162 yOffset = 1815.215685280706,
1163 zoneData = {
1164 Ashenvale = {
1165 height = 3843.722811451077,
1166 width = 5766.728884700476,
1167 xOffset = 15366.76755576002,
1168 yOffset = 8126.925260781192,
1169 mapID = 331,
1171 Aszhara = {
1172 height = 3381.225696279877,
1173 width = 5070.888165752819,
1174 xOffset = 20343.90485013144,
1175 yOffset = 7458.180046130774,
1176 mapID = 16,
1178 AzuremystIsle = {
1179 height = 2714.561862167815,
1180 width = 4070.883253576282,
1181 xOffset = 9966.70736478994,
1182 yOffset = 5460.278138661794,
1183 mapID = 3524,
1185 Barrens = {
1186 height = 6756.202067150937,
1187 width = 10133.44343943073,
1188 xOffset = 14443.84117394525,
1189 yOffset = 11187.32013604393,
1190 mapID = 17,
1192 BloodmystIsle = {
1193 height = 2174.984710698752,
1194 width = 3262.517428121028,
1195 xOffset = 9541.713418184554,
1196 yOffset = 3424.874558234072,
1197 mapID = 3525,
1199 Darkshore = {
1200 height = 4366.636219106706,
1201 width = 6550.06962983463,
1202 xOffset = 14125.08809600818,
1203 yOffset = 4466.534412478246,
1204 mapID = 148,
1206 Darnassis = {
1207 height = 705.7248633938184,
1208 width = 1058.342927027606,
1209 xOffset = 14128.39258617903,
1210 yOffset = 2561.565012455802,
1211 mapID = 1657,
1213 Desolace = {
1214 height = 2997.895174253872,
1215 width = 4495.882023201739,
1216 xOffset = 12833.40729836031,
1217 yOffset = 12347.72848626745,
1218 mapID = 405,
1220 Durotar = {
1221 height = 3524.975114832228,
1222 width = 5287.558038649864,
1223 xOffset = 19029.30699887344,
1224 yOffset = 10991.48801260963,
1225 mapID = 14,
1227 Dustwallow = {
1228 height = 3499.975146240067,
1229 width = 5250.057259791282,
1230 xOffset = 18041.79657043901,
1231 yOffset = 14833.12751666842,
1232 mapID = 15,
1234 Felwood = {
1235 height = 3833.305958270781,
1236 width = 5750.062034325837,
1237 xOffset = 15425.10163773161,
1238 yOffset = 5666.526367166872,
1239 mapID = 361,
1241 Feralas = {
1242 height = 4633.30011661694,
1243 width = 6950.075260353015,
1244 xOffset = 11625.06045254075,
1245 yOffset = 15166.45834829251,
1246 mapID = 357,
1248 Moonglade = {
1249 height = 1539.572509508711,
1250 width = 2308.356845256911,
1251 xOffset = 18448.05172159372,
1252 yOffset = 4308.20254319874,
1253 mapID = 493,
1255 Mulgore = {
1256 height = 3424.975945100366,
1257 width = 5137.555355060729,
1258 xOffset = 15018.84750987729,
1259 yOffset = 13072.72336630089,
1260 mapID = 215,
1262 Ogrimmar = {
1263 height = 935.4100697456119,
1264 width = 1402.621211455915,
1265 xOffset = 20747.42666130799,
1266 yOffset = 10525.94769396873,
1267 mapID = 1637,
1269 Silithus = {
1270 height = 2322.899061688691,
1271 width = 3483.371975265956,
1272 xOffset = 14529.25864164056,
1273 yOffset = 18758.10068625832,
1274 mapID = 1377,
1276 StonetalonMountains = {
1277 height = 3256.226691571251,
1278 width = 4883.385977951072,
1279 xOffset = 13820.91773479217,
1280 yOffset = 9883.162892509636,
1281 mapID = 406,
1283 Tanaris = {
1284 height = 4599.965662459992,
1285 width = 6900.073766103516,
1286 xOffset = 17285.539010128,
1287 yOffset = 18674.7673661939,
1288 mapID = 440,
1290 Teldrassil = {
1291 height = 3393.726923234355,
1292 width = 5091.720903621394,
1293 xOffset = 13252.16205313556,
1294 yOffset = 968.6418744503761,
1295 mapID = 141,
1297 TheExodar = {
1298 height = 704.6826864472878,
1299 width = 1056.781131437323,
1300 xOffset = 10533.08314172693,
1301 yOffset = 6276.205331713322,
1302 mapID = 3557,
1304 ThousandNeedles = {
1305 height = 2933.312180524323,
1306 width = 4400.046681282484,
1307 xOffset = 17500.12437633161,
1308 yOffset = 16766.44698282704,
1309 mapID = 400,
1311 ThunderBluff = {
1312 height = 695.8282721105132,
1313 width = 1043.761263579803,
1314 xOffset = 16550.11410485969,
1315 yOffset = 13649.80260929285,
1316 mapID = 1638,
1318 UngoroCrater = {
1319 height = 2466.647220780505,
1320 width = 3700.040077455555,
1321 xOffset = 16533.44712326324,
1322 yOffset = 18766.4334494793,
1323 mapID = 490,
1325 Winterspring = {
1326 height = 4733.299561046713,
1327 width = 7100.077599808275,
1328 xOffset = 17383.45606038691,
1329 yOffset = 4266.536453420381,
1330 mapID = 618,
1334 -- Eastern Kingdoms
1335 { -- [2]
1336 parentContinent = 0,
1337 height = 27149.795290881,
1338 width = 40741.175327834,
1339 xOffset = 14407.1086092051,
1340 yOffset = 290.3230897653046,
1341 zoneData = {
1342 Alterac = {
1343 height = 1866.673586850316,
1344 width = 2800.000436369314,
1345 xOffset = 17388.63313899802,
1346 yOffset = 9676.382605411302,
1347 mapID = 2597,
1349 Arathi = {
1350 height = 2400.0092446309,
1351 width = 3599.999380663208,
1352 xOffset = 19038.63328411639,
1353 yOffset = 11309.72201070757,
1354 mapID = 3358,
1356 Badlands = {
1357 height = 1658.340965090961,
1358 width = 2487.498490907989,
1359 xOffset = 20251.1337564772,
1360 yOffset = 17065.99404487956,
1361 mapID = 3,
1363 BlastedLands = {
1364 height = 2233.343415116865,
1365 width = 3349.999381676505,
1366 xOffset = 19413.63362865575,
1367 yOffset = 21743.09582955139,
1368 mapID = 4,
1370 BurningSteppes = {
1371 height = 1952.091972408385,
1372 width = 2929.16694293186,
1373 xOffset = 18438.633261567,
1374 yOffset = 18207.66513379744,
1375 mapID = 46,
1377 DeadwindPass = {
1378 height = 1666.673818905317,
1379 width = 2499.999888210889,
1380 xOffset = 19005.29993968603,
1381 yOffset = 21043.0932328648,
1382 mapID = 41,
1384 DunMorogh = {
1385 height = 3283.345779814337,
1386 width = 4924.998791911572,
1387 xOffset = 16369.8840376619,
1388 yOffset = 15053.48695195484,
1389 mapID = 1,
1391 Duskwood = {
1392 height = 1800.007653419076,
1393 width = 2699.999669551933,
1394 xOffset = 17338.63354148773,
1395 yOffset = 20893.09259181909,
1396 mapID = 10,
1398 EasternPlaguelands = {
1399 height = 2687.510360231216,
1400 width = 4031.249051993366,
1401 xOffset = 20459.46801235962,
1402 yOffset = 7472.207045901617,
1403 mapID = 139,
1405 Elwynn = {
1406 height = 2314.591970284716,
1407 width = 3470.831971412848,
1408 xOffset = 16636.55099386465,
1409 yOffset = 19116.0027890283,
1410 mapID = 12,
1412 EversongWoods = {
1413 height = 3283.346366715794,
1414 width = 4924.998483501337,
1415 xOffset = 20259.46725884782,
1416 yOffset = 2534.687567863296,
1417 mapID = 3430,
1419 Ghostlands = {
1420 height = 2200.008945183733,
1421 width = 3300.002855743766,
1422 xOffset = 21055.29786070095,
1423 yOffset = 5309.698546426793,
1424 mapID = 3433,
1426 Hilsbrad = {
1427 height = 2133.341840477916,
1428 width = 3200.000391416799,
1429 xOffset = 17105.29968281043,
1430 yOffset = 10776.38652289269,
1431 mapID = 267,
1433 Hinterlands = {
1434 height = 2566.676323518885,
1435 width = 3849.998492380244,
1436 xOffset = 19746.96704279287,
1437 yOffset = 9709.715966757984,
1438 mapID = 47,
1440 Ironforge = {
1441 height = 527.6056771582851,
1442 width = 790.6252518322632,
1443 xOffset = 18885.55815177769,
1444 yOffset = 15745.64795436116,
1445 mapID = 1537,
1447 LochModan = {
1448 height = 1839.590356444166,
1449 width = 2758.33360594204,
1450 xOffset = 20165.71623436714,
1451 yOffset = 15663.90573348468,
1452 mapID = 38,
1454 Redridge = {
1455 height = 1447.922213393415,
1456 width = 2170.833229570681,
1457 xOffset = 19742.79960560691,
1458 yOffset = 19751.42209395218,
1459 mapID = 44,
1461 SearingGorge = {
1462 height = 1487.505203229038,
1463 width = 2231.250200533406,
1464 xOffset = 18494.88325409831,
1465 yOffset = 17276.41231120941,
1466 mapID = 51,
1468 SilvermoonCity = {
1469 height = 806.7751969249011,
1470 width = 1211.458551923779,
1471 xOffset = 22172.71573747824,
1472 yOffset = 3422.647395021269,
1473 mapID = 3487,
1475 Silverpine = {
1476 height = 2800.011187621704,
1477 width = 4200.000573479695,
1478 xOffset = 14721.96646274185,
1479 yOffset = 9509.714741967448,
1480 mapID = 130,
1482 Stormwind = {
1483 height = 1158.33686894901,
1484 width = 1737.498058940429,
1485 xOffset = 16449.05164642256,
1486 yOffset = 19172.25350774846,
1487 mapID = 1519,
1489 Stranglethorn = {
1490 height = 4254.18312444072,
1491 width = 6381.248484543122,
1492 xOffset = 15951.13375783437,
1493 yOffset = 22345.18258706305,
1494 mapID = 33,
1496 Sunwell = {
1497 height = 2218.756638064149,
1498 width = 3327.084777999942,
1499 xOffset = 21074.0484502027,
1500 yOffset = 7.595267688679496,
1501 mapID = 4080,
1503 SwampOfSorrows = {
1504 height = 1529.173695058727,
1505 width = 2293.753807610138,
1506 xOffset = 20394.88183258176,
1507 yOffset = 20797.25913588854,
1508 mapID = 8,
1510 Tirisfal = {
1511 height = 3012.510490816506,
1512 width = 4518.749381850256,
1513 xOffset = 15138.63417865412,
1514 yOffset = 7338.874503644808,
1515 mapID = 85,
1517 Undercity = {
1518 height = 640.1067253394195,
1519 width = 959.3752013853186,
1520 xOffset = 17298.77399735696,
1521 yOffset = 9298.435338905521,
1522 mapID = 1497,
1524 WesternPlaguelands = {
1525 height = 2866.677213191588,
1526 width = 4299.998717025251,
1527 xOffset = 17755.30067544475,
1528 yOffset = 7809.708745090687,
1529 mapID = 28,
1531 Westfall = {
1532 height = 2333.342039971409,
1533 width = 3500.001170481545,
1534 xOffset = 15155.29922254704,
1535 yOffset = 20576.42557120998,
1536 mapID = 40,
1538 Wetlands = {
1539 height = 2756.260286844545,
1540 width = 4135.414389381328,
1541 xOffset = 18561.55091405621,
1542 yOffset = 13324.31339403164,
1543 mapID = 11,
1547 -- Outland
1548 { -- [3]
1549 parentContinent = 3,
1550 height = 11642.355227091,
1551 width = 17463.987300595,
1552 zoneData = {
1553 BladesEdgeMountains = {
1554 height = 3616.553511321226,
1555 width = 5424.972055480694,
1556 xOffset = 4150.184214583454,
1557 yOffset = 1412.98225932006,
1558 mapID = 3522,
1560 Hellfire = {
1561 height = 3443.642450656037,
1562 width = 5164.556104714847,
1563 xOffset = 7456.417230912641,
1564 yOffset = 4339.973750274888,
1565 mapID = 3483,
1567 Nagrand = {
1568 height = 3683.218538167106,
1569 width = 5524.971495006054,
1570 xOffset = 2700.192018521809,
1571 yOffset = 5779.511974812862,
1572 mapID = 3518,
1574 Netherstorm = {
1575 height = 3716.550608724641,
1576 width = 5574.970083688359,
1577 xOffset = 7512.667416095402,
1578 yOffset = 365.0979827402549,
1579 mapID = 3523,
1581 ShadowmoonValley = {
1582 height = 3666.552070430093,
1583 width = 5499.971770418525,
1584 xOffset = 8770.993458280615,
1585 yOffset = 7769.033264592288,
1586 mapID = 3520,
1588 ShattrathCity = {
1589 height = 870.8059516186869,
1590 width = 1306.242821388422,
1591 xOffset = 6860.744740098593,
1592 yOffset = 7295.086120456203,
1593 mapID = 3703,
1595 TerokkarForest = {
1596 height = 3599.887783533737,
1597 width = 5399.971351016305,
1598 xOffset = 5912.675516998205,
1599 yOffset = 6821.146319031154,
1600 mapID = 3519,
1602 Zangarmarsh = {
1603 height = 3351.978710181591,
1604 width = 5027.057650868489,
1605 xOffset = 3521.020638264577,
1606 yOffset = 3885.821278366336,
1607 mapID = 3521,
1613 --- WotLK Adjustments, now permanently enabled. Someday I should probably merge these in.
1614 if true then
1615 WorldMapSize[0].height = 31809.64859753034;
1616 WorldMapSize[0].width = 47714.27770954026;
1618 WorldMapSize[1].xOffset = -8590.409362625034;
1619 WorldMapSize[1].yOffset = 5628.694276155668;
1621 WorldMapSize[2].xOffset = 18542.31268111796;
1622 WorldMapSize[2].yOffset = 3585.574682467752;
1625 WorldMapSize[4] = {
1626 parentContinent = 0,
1627 height = 11834.31067391958,
1628 width = 17751.3936186856,
1629 xOffset = 16020.94093549576,
1630 yOffset = 454.2464807713226,
1631 zoneData = {
1632 BoreanTundra = {
1633 height = 3843.765503862232,
1634 width = 5764.58206497758,
1635 xOffset = 646.3186767730767,
1636 yOffset = 5695.480016983896,
1637 mapID = 3537,
1639 CrystalsongForest = {
1640 height = 1814.590053385046,
1641 width = 2722.916164555434,
1642 xOffset = 7773.400227973558,
1643 yOffset = 4091.307437548815,
1644 mapID = 2817,
1646 Dalaran = {
1647 height = 553.3419356683534,
1648 width = 830.014625253355,
1649 xOffset = 8164.640128758279,
1650 yOffset = 4526.722218200071,
1651 mapID = 4395,
1653 Dragonblight = {
1654 height = 3739.597759999098,
1655 width = 5608.331259502691,
1656 xOffset = 5590.067753073641,
1657 yOffset = 5018.394106536425,
1658 mapID = 65,
1660 GrizzlyHills = {
1661 height = 3500.013349296343,
1662 width = 5249.9986179934,
1663 xOffset = 10327.56614428777,
1664 yOffset = 5076.727864214266,
1665 mapID = 394,
1667 HowlingFjord = {
1668 height = 4031.266275060274,
1669 width = 6045.831339550668,
1670 xOffset = 10615.0658552538,
1671 yOffset = 7476.736868262738,
1672 mapID = 495,
1674 IcecrownGlacier = {
1675 height = 4181.266519840844,
1676 width = 6270.832975322177,
1677 xOffset = 3773.401695036191,
1678 yOffset = 1166.296622984233,
1679 mapID = 210,
1681 LakeWintergrasp = {
1682 height = 1983.342901980711,
1683 width = 2974.999377667768,
1684 xOffset = 4887.984320612982,
1685 yOffset = 4876.725348039468,
1686 mapID = 4197,
1688 SholazarBasin = {
1689 height = 2904.177559586215,
1690 width = 4356.248328680455,
1691 xOffset = 2287.985279107324,
1692 yOffset = 3305.887993444818,
1693 mapID = 3711,
1695 TheStormPeaks = {
1696 height = 4741.684940421732,
1697 width = 7112.498205872217,
1698 xOffset = 7375.483315518691,
1699 yOffset = 395.4596828327046,
1700 mapID = 67,
1702 ZulDrak = {
1703 height = 3329.179510740043,
1704 width = 4993.747919923504,
1705 xOffset = 9817.150055203074,
1706 yOffset = 2924.636381254688,
1707 mapID = 66,
1709 HrothgarsLanding = {
1710 height = 2452.7,
1711 width = 2452.7*1.5,
1712 xOffset = 23967.599 - 17549.182,
1713 yOffset = 1027.392 - 1215.431,
1718 local function VContinent(index, name, size)
1719 assert(1, not WorldMapSize[index], "denied")
1721 WorldMapSize[index] = {
1722 parentContinent = index,
1723 height = size,
1724 width = size*1.5,
1725 zoneData = { },
1727 WorldMapSize[index].zoneData[name] = {
1728 height = size,
1729 width = size*1.5,
1730 xOffset = 0,
1731 yOffset = 0,
1735 VContinent(-77, "ScarletEnclave", 2125)
1738 VContinent(-80, "UtgardeKeep1", 100) -- temporary value
1739 VContinent(-81, "UtgardeKeep2", 100) -- temporary value
1740 VContinent(-82, "UtgardeKeep3", 100) -- temporary value
1742 VContinent(-83, "TheNexus", 734.2)
1744 VContinent(-84, "AzjolNerub1", 100) -- temporary value
1745 VContinent(-85, "AzjolNerub2", 100) -- temporary value
1746 VContinent(-86, "AzjolNerub3", 100) -- temporary value
1748 VContinent(-87, "Ahnkahet", 648.3)
1750 VContinent(-88, "DrakTharonKeep1", 100) -- temporary value
1751 VContinent(-89, "DrakTharonKeep2", 100) -- temporary value
1753 VContinent(-90, "VioletHold", 170.83)
1755 VContinent(-91, "Gundrak", 603.35)
1757 VContinent(-92, "Ulduar77", 613.5) -- Halls of Stone
1759 VContinent(-93, "HallsofLightning1", 100) -- temporary value
1760 VContinent(-94, "HallsofLightning2", 100) -- temporary value
1762 VContinent(-95, "Nexus801", 100) -- temporary value -- Oculus
1763 VContinent(-96, "Nexus802", 100) -- temporary value
1764 VContinent(-97, "Nexus803", 100) -- temporary value
1765 VContinent(-98, "Nexus804", 100) -- temporary value
1767 VContinent(-99, "CoTStratholme1", 750.2)
1768 VContinent(-100, "CoTStratholme2", 1216.66)
1770 VContinent(-101, "UtgardePinnacle1", 100) -- temporary value -- hey they spelled it right
1771 VContinent(-102, "UtgardePinnacle2", 100) -- temporary value
1773 VContinent(-103, "VaultofArchavon", 603.25) -- temporary value -- Weirdly, Emalon is actually within the "Vault of Archavon"
1775 VContinent(-104, "Naxxramas1", 1237.5) -- construct quarter
1776 VContinent(-105, "Naxxramas2", 1237.5) -- arachnid quarter
1777 VContinent(-106, "Naxxramas3", 1237.5) -- military quarter
1778 VContinent(-107, "Naxxramas4", 1237.5) -- plague quarter
1779 VContinent(-108, "Naxxramas5", 1379.9) -- overview
1780 VContinent(-109, "Naxxramas6", 437.3) -- lair
1782 VContinent(-110, "TheObsidianSanctum", 775.1)
1784 VContinent(-111, "TheEyeOfEternity", 286.7)
1786 VContinent(-112, "Ulduar", 2191.7) -- temporary value
1787 VContinent(-113, "Ulduar1", 446.5) -- temporary value
1788 VContinent(-114, "Ulduar2", 885.6) -- temporary value
1789 VContinent(-115, "Ulduar3", 100) -- temporary value
1790 VContinent(-116, "Ulduar4", 100) -- temporary value
1793 VirtualContinentIndexes = { -- Don't change values here, since programs might want to store them
1794 ["ScarletEnclave"] = -77,
1796 ["UtgardeKeep1"] = -80,
1797 ["UtgardeKeep2"] = -81,
1798 ["UtgardeKeep3"] = -82,
1800 ["TheNexus"] = -83,
1802 ["AzjolNerub1"] = -84,
1803 ["AzjolNerub2"] = -85,
1804 ["AzjolNerub3"] = -86,
1806 ["Ahnkahet"] = -87,
1808 ["DrakTharonKeep1"] = -88,
1809 ["DrakTharonKeep2"] = -89,
1811 ["VioletHold"] = -90,
1813 ["Gundrak"] = -91,
1815 ["Ulduar77"] = -92, -- Halls of Stone
1817 ["HallsofLightning1"] = -93,
1818 ["HallsofLightning2"] = -94,
1820 ["Nexus801"] = -95, -- Oculus
1821 ["Nexus802"] = -96,
1822 ["Nexus803"] = -97,
1823 ["Nexus804"] = -98,
1825 ["CoTStratholme1"] = -99,
1826 ["CoTStratholme2"] = -100,
1828 ["UtgardePinnacle1"] = -101, -- hey they spelled it right
1829 ["UtgardePinnacle2"] = -102,
1831 ["VaultofArchavon"] = -103, -- Weirdly, Emalon is actually within the "Vault of Archavon"
1833 ["Naxxramas1"] = -104,
1834 ["Naxxramas2"] = -105,
1835 ["Naxxramas3"] = -106,
1836 ["Naxxramas4"] = -107,
1837 ["Naxxramas5"] = -108,
1838 ["Naxxramas6"] = -109,
1840 ["TheObsidianSanctum"] = -110,
1842 ["TheEyeOfEternity"] = -111,
1844 ["Ulduar"] = -112,
1845 ["Ulduar1"] = -113,
1846 ["Ulduar2"] = -114,
1847 ["Ulduar3"] = -115,
1848 ["Ulduar4"] = -116,
1851 DongleStub:Register(Astrolabe, activate)
1853 local zeroData;
1854 zeroData = { xOffset = 0, height = 0, yOffset = 0, width = 0, __index = function() return zeroData end };
1855 setmetatable(zeroData, zeroData);
1856 setmetatable(WorldMapSize, zeroData);
1858 for continent, zones in pairs(Astrolabe.ContinentList) do
1859 local mapData = WorldMapSize[continent];
1860 for index, mapName in pairs(zones) do
1861 if not ( mapData.zoneData[mapName] ) then
1862 --WE HAVE A PROBLEM!!!
1863 ChatFrame1:AddMessage("Astrolabe is missing data for "..select(index, GetMapZones(continent))..".");
1864 mapData.zoneData[mapName] = zeroData;
1866 mapData[index] = mapData.zoneData[mapName];
1867 mapData.zoneData[mapName] = nil;
1872 -- register this library with AstrolabeMapMonitor, this will cause a full update if PLAYER_LOGIN has already fired
1873 local AstrolabeMapMonitor = DongleStub("AstrolabeMapMonitor");
1874 AstrolabeMapMonitor:RegisterAstrolabeLibrary(Astrolabe, LIBRARY_VERSION_MAJOR);
1877 QH_Astrolabe_Ready = true