who is awesome? it is me
[QuestHelper.git] / AstrolabeQH / Astrolabe.lua
blob46857da86a6c58eb4441dd50a980c3684ba6e33b
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 function Astrolabe:GetZoneWidth(c, z)
352 if z ~= 0 then
353 return WorldMapSize[c][z].width
354 else
355 return WorldMapSize[c].width
359 --*****************************************************************************
360 -- This function will do its utmost to retrieve some sort of valid position
361 -- for the specified unit, including changing the current map zoom (if needed).
362 -- Map Zoom is returned to its previous setting before this function returns.
363 --*****************************************************************************
364 function Astrolabe:GetUnitPosition( unit, noMapChange )
365 local x, y = GetPlayerMapPosition(unit);
366 if ( x <= 0 and y <= 0 ) then
367 if ( noMapChange ) then
368 -- no valid position on the current map, and we aren't allowed
369 -- to change map zoom, so return
370 return;
372 local lastCont, lastZone = GetCurrentMapContinent(), GetCurrentMapZone();
373 SetMapToCurrentZone();
374 x, y = GetPlayerMapPosition(unit);
375 if ( x <= 0 and y <= 0 ) then
376 SetMapZoom(GetCurrentMapContinent());
377 x, y = GetPlayerMapPosition(unit);
378 if ( x <= 0 and y <= 0 ) then
379 -- we are in an instance or otherwise off the continent map
380 return;
383 local C, Z = GetCurrentMapContinent(), GetCurrentMapZone();
384 if ( C ~= lastCont or Z ~= lastZone ) then
385 SetMapZoom(lastCont, lastZone); -- set map zoom back to what it was before
387 return C, Z, x, y;
389 return self:GetCurrentVirtualMapContinent(), self:GetCurrentVirtualMapZone(), x, y;
392 --*****************************************************************************
393 -- This function will do its utmost to retrieve some sort of valid position
394 -- for the specified unit, including changing the current map zoom (if needed).
395 -- However, if a monitored WorldMapFrame (See AstrolabeMapMonitor.lua) is
396 -- visible, then will simply return nil if the current zoom does not provide
397 -- a valid position for the player unit. Map Zoom is returned to its previous
398 -- setting before this function returns, if it was changed.
399 --*****************************************************************************
400 function Astrolabe:GetCurrentPlayerPosition()
401 local x, y = GetPlayerMapPosition("player");
402 if ( x <= 0 and y <= 0 ) then
403 if ( self.WorldMapVisible ) then
404 -- we know there is a visible world map, so don't cause
405 -- WORLD_MAP_UPDATE events by changing map zoom
406 return;
408 local lastCont, lastZone = GetCurrentMapContinent(), GetCurrentMapZone();
409 SetMapToCurrentZone();
410 x, y = GetPlayerMapPosition("player");
411 if ( x <= 0 and y <= 0 ) then
412 SetMapZoom(GetCurrentMapContinent());
413 x, y = GetPlayerMapPosition("player");
414 if ( x <= 0 and y <= 0 ) then
415 -- we are in an instance or otherwise off the continent map
416 return;
419 local C, Z = GetCurrentMapContinent(), GetCurrentMapZone();
421 if ( C ~= lastCont or Z ~= lastZone ) then
422 SetMapZoom(lastCont, lastZone); --set map zoom back to what it was before
424 return C, Z, x, y;
427 return self:GetCurrentVirtualMapContinent(), self:GetCurrentVirtualMapZone(), x, y;
431 --------------------------------------------------------------------------------------------------------------
432 -- Working Table Cache System
433 --------------------------------------------------------------------------------------------------------------
435 local tableCache = {};
436 tableCache["__mode"] = "v";
437 setmetatable(tableCache, tableCache);
439 local function GetWorkingTable( icon )
440 if ( tableCache[icon] ) then
441 return tableCache[icon];
442 else
443 local T = {};
444 tableCache[icon] = T;
445 return T;
450 --------------------------------------------------------------------------------------------------------------
451 -- Minimap Icon Placement
452 --------------------------------------------------------------------------------------------------------------
454 --*****************************************************************************
455 -- local variables specifically for use in this section
456 --*****************************************************************************
457 local minimapRotationEnabled = false;
458 local minimapShape = false;
460 local MinimapCompassTexture = MinimapCompassTexture;
461 local MinimapCompassRing = MiniMapCompassRing;
462 function Astrolabe:GetFacing()
463 if MinimapCompassRing then -- 3.1 hackery
464 return MinimapCompassRing:GetFacing()
465 else
466 return -GetPlayerFacing()
469 local minimapRotationOffset = -Astrolabe.GetFacing()
471 local function placeIconOnMinimap( minimap, minimapZoom, mapWidth, mapHeight, icon, dist, xDist, yDist )
472 local mapDiameter;
473 if ( Astrolabe.minimapOutside ) then
474 mapDiameter = MinimapSize.outdoor[minimapZoom];
475 else
476 mapDiameter = MinimapSize.indoor[minimapZoom];
478 local mapRadius = mapDiameter / 2;
479 local xScale = mapDiameter / mapWidth;
480 local yScale = mapDiameter / mapHeight;
481 local iconDiameter = ((icon:GetWidth() / 2) + 3) * xScale;
482 local iconOnEdge = nil;
483 local isRound = true;
485 if ( minimapRotationEnabled ) then
486 local sinTheta = sin(minimapRotationOffset)
487 local cosTheta = cos(minimapRotationOffset)
488 --[[
489 Math Note
490 The math that is acutally going on in the next 3 lines is:
491 local dx, dy = xDist, -yDist
492 xDist = (dx * cosTheta) + (dy * sinTheta)
493 yDist = -((-dx * sinTheta) + (dy * cosTheta))
495 This is because the origin for map coordinates is the top left corner
496 of the map, not the bottom left, and so we have to reverse the vertical
497 distance when doing the our rotation, and then reverse the result vertical
498 distance because this rotation formula gives us a result with the origin based
499 in the bottom left corner (of the (+, +) quadrant).
500 The actual code is a simplification of the above.
502 local dx, dy = xDist, yDist
503 xDist = (dx * cosTheta) - (dy * sinTheta)
504 yDist = (dx * sinTheta) + (dy * cosTheta)
507 if ( minimapShape and not (xDist == 0 or yDist == 0) ) then
508 isRound = (xDist < 0) and 1 or 3;
509 if ( yDist < 0 ) then
510 isRound = minimapShape[isRound];
511 else
512 isRound = minimapShape[isRound + 1];
516 -- for non-circular portions of the Minimap edge
517 if not ( isRound ) then
518 dist = max(abs(xDist), abs(yDist))
521 if ( (dist + iconDiameter) > mapRadius ) then
522 -- position along the outside of the Minimap
523 iconOnEdge = true;
524 local factor = (mapRadius - iconDiameter) / dist;
525 xDist = xDist * factor;
526 yDist = yDist * factor;
529 if ( Astrolabe.IconsOnEdge[icon] ~= iconOnEdge ) then
530 Astrolabe.IconsOnEdge[icon] = iconOnEdge;
531 Astrolabe.IconsOnEdgeChanged = true;
534 icon:ClearAllPoints();
535 icon:SetPoint("CENTER", minimap, "CENTER", xDist/xScale, -yDist/yScale);
538 function Astrolabe:PlaceIconOnMinimap( icon, continent, zone, xPos, yPos )
539 -- check argument types
540 argcheck(icon, 2, "table");
541 assert(3, icon.SetPoint and icon.ClearAllPoints, "Usage Message");
542 argcheck(continent, 3, "number");
543 argcheck(zone, 4, "number", "nil");
544 argcheck(xPos, 5, "number");
545 argcheck(yPos, 6, "number");
547 local lC, lZ, lx, ly = unpack(self.LastPlayerPosition);
548 local dist, xDist, yDist = self:ComputeDistance(lC, lZ, lx, ly, continent, zone, xPos, yPos);
549 if not ( dist ) then
550 --icon's position has no meaningful position relative to the player's current location
551 return -1;
554 local iconData = GetWorkingTable(icon);
555 if ( self.MinimapIcons[icon] ) then
556 self.MinimapIcons[icon] = nil;
557 else
558 self.MinimapIconCount = self.MinimapIconCount + 1
561 -- We know this icon's position is valid, so we need to make sure the icon placement
562 -- system is active. We call this here so that if this is the first icon being added to
563 -- an empty buffer, the full recalc will not completely redo the work done by this function
564 -- because the icon has not yet actually been placed in the buffer.
565 self.processingFrame:Show()
567 AddedOrUpdatedIcons[icon] = iconData
568 iconData.continent = continent;
569 iconData.zone = zone;
570 iconData.xPos = xPos;
571 iconData.yPos = yPos;
572 iconData.dist = dist;
573 iconData.xDist = xDist;
574 iconData.yDist = yDist;
576 minimapRotationEnabled = GetCVar("rotateMinimap") ~= "0"
577 if ( minimapRotationEnabled ) then
578 minimapRotationOffset = -Astrolabe.GetFacing()
581 -- check Minimap Shape
582 minimapShape = GetMinimapShape and ValidMinimapShapes[GetMinimapShape()];
584 -- place the icon on the Minimap and :Show() it
585 local map = Minimap
586 placeIconOnMinimap(map, map:GetZoom(), map:GetWidth(), map:GetHeight(), icon, dist, xDist, yDist);
587 icon:Show()
589 return 0;
592 function Astrolabe:RemoveIconFromMinimap( icon )
593 if not ( self.MinimapIcons[icon] ) then
594 return 1;
596 AddedOrUpdatedIcons[icon] = nil
597 self.MinimapIcons[icon] = nil;
598 self.IconsOnEdge[icon] = nil;
599 icon:Hide();
601 local MinimapIconCount = self.MinimapIconCount - 1
602 if ( MinimapIconCount <= 0 ) then
603 -- no icons left to manage
604 self.processingFrame:Hide()
605 MinimapIconCount = 0 -- because I'm paranoid
607 self.MinimapIconCount = MinimapIconCount
609 return 0;
612 function Astrolabe:RemoveAllMinimapIcons()
613 self:DumpNewIconsCache()
614 local MinimapIcons = self.MinimapIcons;
615 local IconsOnEdge = self.IconsOnEdge;
616 for k, v in pairs(MinimapIcons) do
617 MinimapIcons[k] = nil;
618 IconsOnEdge[k] = nil;
619 k:Hide();
621 self.MinimapIconCount = 0
622 self.processingFrame:Hide()
625 local lastZoom; -- to remember the last seen Minimap zoom level
627 -- local variables to track the status of the two update coroutines
628 local fullUpdateInProgress = true
629 local resetIncrementalUpdate = false
630 local resetFullUpdate = false
632 -- Incremental Update Code
634 -- local variables to track the incremental update coroutine
635 local incrementalUpdateCrashed = true
636 local incrementalUpdateThread
638 local function UpdateMinimapIconPositions( self )
639 yield()
641 while ( true ) do
642 self:DumpNewIconsCache() -- put new/updated icons into the main datacache
644 resetIncrementalUpdate = false -- by definition, the incremental update is reset if it is here
646 local C, Z, x, y = self:GetCurrentPlayerPosition();
647 if ( C and C ~= -1 ) then
648 local lastPosition = self.LastPlayerPosition;
649 local lC, lZ, lx, ly = unpack(lastPosition);
651 minimapRotationEnabled = GetCVar("rotateMinimap") ~= "0"
652 if ( minimapRotationEnabled ) then
653 minimapRotationOffset = -Astrolabe.GetFacing()
656 -- check current frame rate
657 local numPerCycle = min(50, GetFramerate() * (self.MinimapUpdateMultiplier or 1))
659 -- check Minimap Shape
660 minimapShape = GetMinimapShape and ValidMinimapShapes[GetMinimapShape()];
662 if ( lC == C and lZ == Z and lx == x and ly == y ) then
663 -- player has not moved since the last update
664 if ( lastZoom ~= Minimap:GetZoom() or self.ForceNextUpdate or minimapRotationEnabled ) then
665 local currentZoom = Minimap:GetZoom();
666 lastZoom = currentZoom;
667 local mapWidth = Minimap:GetWidth();
668 local mapHeight = Minimap:GetHeight();
669 numPerCycle = numPerCycle * 2
670 local count = 0
671 for icon, data in pairs(self.MinimapIcons) do
672 placeIconOnMinimap(Minimap, currentZoom, mapWidth, mapHeight, icon, data.dist, data.xDist, data.yDist);
674 count = count + 1
675 if ( count > numPerCycle ) then
676 count = 0
677 yield()
678 -- check if the incremental update cycle needs to be reset
679 -- because a full update has been run
680 if ( resetIncrementalUpdate ) then
681 break;
685 self.ForceNextUpdate = false;
687 else
688 local dist, xDelta, yDelta = self:ComputeDistance(lC, lZ, lx, ly, C, Z, x, y);
689 if ( dist ) then
690 local currentZoom = Minimap:GetZoom();
691 lastZoom = currentZoom;
692 local mapWidth = Minimap:GetWidth();
693 local mapHeight = Minimap:GetHeight();
694 local count = 0
695 for icon, data in pairs(self.MinimapIcons) do
696 local xDist = data.xDist - xDelta;
697 local yDist = data.yDist - yDelta;
698 local dist = sqrt(xDist*xDist + yDist*yDist);
699 placeIconOnMinimap(Minimap, currentZoom, mapWidth, mapHeight, icon, dist, xDist, yDist);
701 data.dist = dist;
702 data.xDist = xDist;
703 data.yDist = yDist;
705 count = count + 1
706 if ( count >= numPerCycle ) then
707 count = 0
708 yield()
709 -- check if the incremental update cycle needs to be reset
710 -- because a full update has been run
711 if ( resetIncrementalUpdate ) then
712 break;
716 if not ( resetIncrementalUpdate ) then
717 lastPosition[1] = C;
718 lastPosition[2] = Z;
719 lastPosition[3] = x;
720 lastPosition[4] = y;
722 else
723 self:RemoveAllMinimapIcons()
724 lastPosition[1] = C;
725 lastPosition[2] = Z;
726 lastPosition[3] = x;
727 lastPosition[4] = y;
730 else
731 if not ( self.WorldMapVisible ) then
732 self.processingFrame:Hide();
736 -- if we've been reset, then we want to start the new cycle immediately
737 if not ( resetIncrementalUpdate ) then
738 yield()
743 function Astrolabe:UpdateMinimapIconPositions()
744 if ( fullUpdateInProgress ) then
745 -- if we're in the middle a a full update, we want to finish that first
746 self:CalculateMinimapIconPositions()
747 else
748 if ( incrementalUpdateCrashed ) then
749 incrementalUpdateThread = coroutine.wrap(UpdateMinimapIconPositions)
750 incrementalUpdateThread(self) --initialize the thread
752 incrementalUpdateCrashed = true
753 incrementalUpdateThread()
754 incrementalUpdateCrashed = false
759 -- Full Update Code
761 -- local variables to track the full update coroutine
762 local fullUpdateCrashed = true
763 local fullUpdateThread
765 local function CalculateMinimapIconPositions( self )
766 yield()
768 while ( true ) do
769 self:DumpNewIconsCache() -- put new/updated icons into the main datacache
771 resetFullUpdate = false -- by definition, the full update is reset if it is here
773 fullUpdateInProgress = true -- set the flag the says a full update is in progress
775 local C, Z, x, y = self:GetCurrentPlayerPosition();
776 if ( C and C ~= -1 ) then
777 minimapRotationEnabled = GetCVar("rotateMinimap") ~= "0"
778 if ( minimapRotationEnabled ) then
779 minimapRotationOffset = Astrolabe.GetFacing()
782 -- check current frame rate
783 local numPerCycle = GetFramerate() * (self.MinimapUpdateMultiplier or 1) * 2
785 -- check Minimap Shape
786 minimapShape = GetMinimapShape and ValidMinimapShapes[GetMinimapShape()];
788 local currentZoom = Minimap:GetZoom();
789 lastZoom = currentZoom;
790 local mapWidth = Minimap:GetWidth();
791 local mapHeight = Minimap:GetHeight();
792 local count = 0
793 for icon, data in pairs(self.MinimapIcons) do
794 local dist, xDist, yDist = self:ComputeDistance(C, Z, x, y, data.continent, data.zone, data.xPos, data.yPos);
795 if ( dist ) then
796 placeIconOnMinimap(Minimap, currentZoom, mapWidth, mapHeight, icon, dist, xDist, yDist);
798 data.dist = dist;
799 data.xDist = xDist;
800 data.yDist = yDist;
801 else
802 self:RemoveIconFromMinimap(icon)
805 count = count + 1
806 if ( count >= numPerCycle ) then
807 count = 0
808 yield()
809 -- check if we need to restart due to the full update being reset
810 if ( resetFullUpdate ) then
811 break;
816 if not ( resetFullUpdate ) then
817 local lastPosition = self.LastPlayerPosition;
818 lastPosition[1] = C;
819 lastPosition[2] = Z;
820 lastPosition[3] = x;
821 lastPosition[4] = y;
823 resetIncrementalUpdate = true
825 else
826 if not ( self.WorldMapVisible ) then
827 self.processingFrame:Hide();
831 -- if we've been reset, then we want to start the new cycle immediately
832 if not ( resetFullUpdate ) then
833 fullUpdateInProgress = false
834 yield()
839 function Astrolabe:CalculateMinimapIconPositions( reset )
840 if ( fullUpdateCrashed ) then
841 fullUpdateThread = coroutine.wrap(CalculateMinimapIconPositions)
842 fullUpdateThread(self) --initialize the thread
843 elseif ( reset ) then
844 resetFullUpdate = true
846 fullUpdateCrashed = true
847 fullUpdateThread()
848 fullUpdateCrashed = false
850 -- return result flag
851 if ( fullUpdateInProgress ) then
852 return 1 -- full update started, but did not complete on this cycle
854 else
855 if ( resetIncrementalUpdate ) then
856 return 0 -- update completed
857 else
858 return -1 -- full update did no occur for some reason
865 function Astrolabe:GetDistanceToIcon( icon )
866 local data = self.MinimapIcons[icon];
867 if ( data ) then
868 return data.dist, data.xDist, data.yDist;
872 function Astrolabe:IsIconOnEdge( icon )
873 return self.IconsOnEdge[icon];
876 function Astrolabe:GetDirectionToIcon( icon )
877 local data = self.MinimapIcons[icon];
878 if ( data ) then
879 local dir = atan2(data.xDist, -(data.yDist))
880 if ( dir > 0 ) then
881 return twoPi - dir;
882 else
883 return -dir;
888 function Astrolabe:Register_OnEdgeChanged_Callback( func, ident )
889 -- check argument types
890 argcheck(func, 2, "function");
892 self.IconsOnEdge_GroupChangeCallbacks[func] = ident;
895 --*****************************************************************************
896 -- INTERNAL USE ONLY PLEASE!!!
897 -- Calling this function at the wrong time can cause errors
898 --*****************************************************************************
899 function Astrolabe:DumpNewIconsCache()
900 local MinimapIcons = self.MinimapIcons
901 for icon, data in pairs(AddedOrUpdatedIcons) do
902 MinimapIcons[icon] = data
903 AddedOrUpdatedIcons[icon] = nil
905 -- we now need to restart any updates that were in progress
906 resetIncrementalUpdate = true
907 resetFullUpdate = true
911 --------------------------------------------------------------------------------------------------------------
912 -- World Map Icon Placement
913 --------------------------------------------------------------------------------------------------------------
915 function Astrolabe:PlaceIconOnWorldMap( worldMapFrame, icon, continent, zone, xPos, yPos )
916 -- check argument types
917 argcheck(worldMapFrame, 2, "table");
918 assert(3, worldMapFrame.GetWidth and worldMapFrame.GetHeight, "Usage Message");
919 argcheck(icon, 3, "table");
920 assert(3, icon.SetPoint and icon.ClearAllPoints, "Usage Message");
921 argcheck(continent, 4, "number");
922 argcheck(zone, 5, "number", "nil");
923 argcheck(xPos, 6, "number");
924 argcheck(yPos, 7, "number");
926 local C, Z = self:GetCurrentVirtualMapCZ();
927 local nX, nY = self:TranslateWorldMapPosition(continent, zone, xPos, yPos, C, Z);
929 -- anchor and :Show() the icon if it is within the boundry of the current map, :Hide() it otherwise
930 if ( nX and nY and (0 < nX and nX <= 1) and (0 < nY and nY <= 1) ) then
931 icon:ClearAllPoints();
932 icon:SetPoint("CENTER", worldMapFrame, "TOPLEFT", nX * worldMapFrame:GetWidth(), -nY * worldMapFrame:GetHeight());
933 icon:Show();
934 else
935 icon:Hide();
937 return nX, nY;
941 --------------------------------------------------------------------------------------------------------------
942 -- Handler Scripts
943 --------------------------------------------------------------------------------------------------------------
945 function Astrolabe:OnEvent( frame, event )
946 if ( event == "MINIMAP_UPDATE_ZOOM" ) then
947 -- update minimap zoom scale
948 local curZoom = Minimap:GetZoom();
949 if ( GetCVar("minimapZoom") == GetCVar("minimapInsideZoom") ) then
950 if ( curZoom < 2 ) then
951 Minimap:SetZoom(curZoom + 1);
952 else
953 Minimap:SetZoom(curZoom - 1);
956 if ( GetCVar("minimapZoom")+0 == Minimap:GetZoom() ) then
957 self.minimapOutside = true;
958 else
959 self.minimapOutside = false;
961 Minimap:SetZoom(curZoom);
963 -- re-calculate all Minimap Icon positions
964 if ( frame:IsVisible() ) then
965 self:CalculateMinimapIconPositions(true);
968 elseif ( event == "PLAYER_LEAVING_WORLD" ) then
969 frame:Hide(); -- yes, I know this is redunant
970 self:RemoveAllMinimapIcons(); --dump all minimap icons
972 elseif ( event == "PLAYER_ENTERING_WORLD" ) then
973 frame:Show();
974 if not ( frame:IsVisible() ) then
975 -- do the minimap recalculation anyways if the OnShow script didn't execute
976 -- this is done to ensure the accuracy of information about icons that were
977 -- inserted while the Player was in the process of zoning
978 self:CalculateMinimapIconPositions(true);
981 elseif ( event == "ZONE_CHANGED_NEW_AREA" ) then
982 frame:Hide();
983 frame:Show();
988 function Astrolabe:OnUpdate( frame, elapsed )
989 -- on-edge group changed call-backs
990 if ( self.IconsOnEdgeChanged ) then
991 self.IconsOnEdgeChanged = false;
992 for func in pairs(self.IconsOnEdge_GroupChangeCallbacks) do
993 pcall(func);
997 self:UpdateMinimapIconPositions();
1000 function Astrolabe:OnShow( frame )
1001 -- set the world map to a zoom with a valid player position
1002 if not ( self.WorldMapVisible ) then
1003 SetMapToCurrentZone();
1005 local C, Z = Astrolabe:GetCurrentPlayerPosition();
1006 if ( C and C ~= -1 ) then
1007 if C >= 0 then -- If we're in Wackyland, we can't change the world map anyway, so at least it's probably right
1008 SetMapZoom(C, Z);
1010 else
1011 frame:Hide();
1012 return
1015 -- re-calculate minimap icon positions
1016 self:CalculateMinimapIconPositions(true);
1018 if ( self.MinimapIconCount <= 0 ) then
1019 -- no icons left to manage
1020 self.processingFrame:Hide()
1024 -- called by AstrolabMapMonitor when all world maps are hidden
1025 function Astrolabe:AllWorldMapsHidden()
1026 if ( IsLoggedIn() ) then
1027 self.processingFrame:Hide();
1028 self.processingFrame:Show();
1032 function Astrolabe:SetMinimapObject(minimap)
1033 Minimap = minimap
1034 self:UpdateMinimapIconPositions()
1037 --------------------------------------------------------------------------------------------------------------
1038 -- Library Registration
1039 --------------------------------------------------------------------------------------------------------------
1041 local function activate( newInstance, oldInstance )
1042 if ( oldInstance ) then -- this is an upgrade activate
1043 if ( oldInstance.DumpNewIconsCache ) then
1044 oldInstance:DumpNewIconsCache()
1046 for k, v in pairs(oldInstance) do
1047 if ( type(v) ~= "function" and (not configConstants[k]) ) then
1048 newInstance[k] = v;
1051 -- sync up the current MinimapIconCount value
1052 local iconCount = 0
1053 for _ in pairs(newInstance.MinimapIcons) do
1054 iconCount = iconCount + 1
1056 newInstance.MinimapIconCount = iconCount
1058 Astrolabe = oldInstance;
1059 else
1060 local frame = CreateFrame("Frame");
1061 newInstance.processingFrame = frame;
1063 newInstance.ContinentList = Astrolabe:GetMapVirtualContinents();
1065 for C in pairs(newInstance.ContinentList) do
1066 local zones = Astrolabe:GetMapVirtualZones(C);
1067 newInstance.ContinentList[C] = zones;
1068 for Z in ipairs(zones) do
1069 zones[Z] = Astrolabe:GetMapTexture(C, Z);
1073 configConstants = nil -- we don't need this anymore
1075 local frame = newInstance.processingFrame;
1076 frame:Hide();
1077 frame:SetParent("Minimap");
1078 frame:UnregisterAllEvents();
1079 frame:RegisterEvent("MINIMAP_UPDATE_ZOOM");
1080 frame:RegisterEvent("PLAYER_LEAVING_WORLD");
1081 frame:RegisterEvent("PLAYER_ENTERING_WORLD");
1082 frame:RegisterEvent("ZONE_CHANGED_NEW_AREA");
1083 frame:SetScript("OnEvent",
1084 function( frame, event, ... )
1085 Astrolabe:OnEvent(frame, event, ...);
1088 frame:SetScript("OnUpdate",
1089 function( frame, elapsed )
1090 Astrolabe:OnUpdate(frame, elapsed);
1093 frame:SetScript("OnShow",
1094 function( frame )
1095 Astrolabe:OnShow(frame);
1099 setmetatable(Astrolabe.MinimapIcons, MinimapIconsMetatable)
1102 --------------------------------------------------------------------------------------------------------------
1103 -- Data
1104 --------------------------------------------------------------------------------------------------------------
1106 -- diameter of the Minimap in game yards at
1107 -- the various possible zoom levels
1108 MinimapSize = {
1109 indoor = {
1110 [0] = 300, -- scale
1111 [1] = 240, -- 1.25
1112 [2] = 180, -- 5/3
1113 [3] = 120, -- 2.5
1114 [4] = 80, -- 3.75
1115 [5] = 50, -- 6
1117 outdoor = {
1118 [0] = 466 + 2/3, -- scale
1119 [1] = 400, -- 7/6
1120 [2] = 333 + 1/3, -- 1.4
1121 [3] = 266 + 2/6, -- 1.75
1122 [4] = 200, -- 7/3
1123 [5] = 133 + 1/3, -- 3.5
1127 ValidMinimapShapes = {
1128 -- { upper-left, lower-left, upper-right, lower-right }
1129 ["SQUARE"] = { false, false, false, false },
1130 ["CORNER-TOPLEFT"] = { true, false, false, false },
1131 ["CORNER-TOPRIGHT"] = { false, false, true, false },
1132 ["CORNER-BOTTOMLEFT"] = { false, true, false, false },
1133 ["CORNER-BOTTOMRIGHT"] = { false, false, false, true },
1134 ["SIDE-LEFT"] = { true, true, false, false },
1135 ["SIDE-RIGHT"] = { false, false, true, true },
1136 ["SIDE-TOP"] = { true, false, true, false },
1137 ["SIDE-BOTTOM"] = { false, true, false, true },
1138 ["TRICORNER-TOPLEFT"] = { true, true, true, false },
1139 ["TRICORNER-TOPRIGHT"] = { true, false, true, true },
1140 ["TRICORNER-BOTTOMLEFT"] = { true, true, false, true },
1141 ["TRICORNER-BOTTOMRIGHT"] = { false, true, true, true },
1144 -- distances across and offsets of the world maps
1145 -- in game yards
1146 WorldMapSize = {
1147 -- World Map of Azeroth
1148 [0] = {
1149 parentContinent = 0,
1150 height = 29688.932932224,
1151 width = 44537.340058402,
1153 -- Kalimdor
1154 { -- [1]
1155 parentContinent = 0,
1156 height = 24533.025279205,
1157 width = 36800.210572494,
1158 xOffset = -8311.793923510446,
1159 yOffset = 1815.215685280706,
1160 zoneData = {
1161 Ashenvale = {
1162 height = 3843.722811451077,
1163 width = 5766.728884700476,
1164 xOffset = 15366.76755576002,
1165 yOffset = 8126.925260781192,
1166 mapID = 331,
1168 Aszhara = {
1169 height = 3381.225696279877,
1170 width = 5070.888165752819,
1171 xOffset = 20343.90485013144,
1172 yOffset = 7458.180046130774,
1173 mapID = 16,
1175 AzuremystIsle = {
1176 height = 2714.561862167815,
1177 width = 4070.883253576282,
1178 xOffset = 9966.70736478994,
1179 yOffset = 5460.278138661794,
1180 mapID = 3524,
1182 Barrens = {
1183 height = 6756.202067150937,
1184 width = 10133.44343943073,
1185 xOffset = 14443.84117394525,
1186 yOffset = 11187.32013604393,
1187 mapID = 17,
1189 BloodmystIsle = {
1190 height = 2174.984710698752,
1191 width = 3262.517428121028,
1192 xOffset = 9541.713418184554,
1193 yOffset = 3424.874558234072,
1194 mapID = 3525,
1196 Darkshore = {
1197 height = 4366.636219106706,
1198 width = 6550.06962983463,
1199 xOffset = 14125.08809600818,
1200 yOffset = 4466.534412478246,
1201 mapID = 148,
1203 Darnassis = {
1204 height = 705.7248633938184,
1205 width = 1058.342927027606,
1206 xOffset = 14128.39258617903,
1207 yOffset = 2561.565012455802,
1208 mapID = 1657,
1210 Desolace = {
1211 height = 2997.895174253872,
1212 width = 4495.882023201739,
1213 xOffset = 12833.40729836031,
1214 yOffset = 12347.72848626745,
1215 mapID = 405,
1217 Durotar = {
1218 height = 3524.975114832228,
1219 width = 5287.558038649864,
1220 xOffset = 19029.30699887344,
1221 yOffset = 10991.48801260963,
1222 mapID = 14,
1224 Dustwallow = {
1225 height = 3499.975146240067,
1226 width = 5250.057259791282,
1227 xOffset = 18041.79657043901,
1228 yOffset = 14833.12751666842,
1229 mapID = 15,
1231 Felwood = {
1232 height = 3833.305958270781,
1233 width = 5750.062034325837,
1234 xOffset = 15425.10163773161,
1235 yOffset = 5666.526367166872,
1236 mapID = 361,
1238 Feralas = {
1239 height = 4633.30011661694,
1240 width = 6950.075260353015,
1241 xOffset = 11625.06045254075,
1242 yOffset = 15166.45834829251,
1243 mapID = 357,
1245 Moonglade = {
1246 height = 1539.572509508711,
1247 width = 2308.356845256911,
1248 xOffset = 18448.05172159372,
1249 yOffset = 4308.20254319874,
1250 mapID = 493,
1252 Mulgore = {
1253 height = 3424.975945100366,
1254 width = 5137.555355060729,
1255 xOffset = 15018.84750987729,
1256 yOffset = 13072.72336630089,
1257 mapID = 215,
1259 Ogrimmar = {
1260 height = 935.4100697456119,
1261 width = 1402.621211455915,
1262 xOffset = 20747.42666130799,
1263 yOffset = 10525.94769396873,
1264 mapID = 1637,
1266 Silithus = {
1267 height = 2322.899061688691,
1268 width = 3483.371975265956,
1269 xOffset = 14529.25864164056,
1270 yOffset = 18758.10068625832,
1271 mapID = 1377,
1273 StonetalonMountains = {
1274 height = 3256.226691571251,
1275 width = 4883.385977951072,
1276 xOffset = 13820.91773479217,
1277 yOffset = 9883.162892509636,
1278 mapID = 406,
1280 Tanaris = {
1281 height = 4599.965662459992,
1282 width = 6900.073766103516,
1283 xOffset = 17285.539010128,
1284 yOffset = 18674.7673661939,
1285 mapID = 440,
1287 Teldrassil = {
1288 height = 3393.726923234355,
1289 width = 5091.720903621394,
1290 xOffset = 13252.16205313556,
1291 yOffset = 968.6418744503761,
1292 mapID = 141,
1294 TheExodar = {
1295 height = 704.6826864472878,
1296 width = 1056.781131437323,
1297 xOffset = 10533.08314172693,
1298 yOffset = 6276.205331713322,
1299 mapID = 3557,
1301 ThousandNeedles = {
1302 height = 2933.312180524323,
1303 width = 4400.046681282484,
1304 xOffset = 17500.12437633161,
1305 yOffset = 16766.44698282704,
1306 mapID = 400,
1308 ThunderBluff = {
1309 height = 695.8282721105132,
1310 width = 1043.761263579803,
1311 xOffset = 16550.11410485969,
1312 yOffset = 13649.80260929285,
1313 mapID = 1638,
1315 UngoroCrater = {
1316 height = 2466.647220780505,
1317 width = 3700.040077455555,
1318 xOffset = 16533.44712326324,
1319 yOffset = 18766.4334494793,
1320 mapID = 490,
1322 Winterspring = {
1323 height = 4733.299561046713,
1324 width = 7100.077599808275,
1325 xOffset = 17383.45606038691,
1326 yOffset = 4266.536453420381,
1327 mapID = 618,
1331 -- Eastern Kingdoms
1332 { -- [2]
1333 parentContinent = 0,
1334 height = 27149.795290881,
1335 width = 40741.175327834,
1336 xOffset = 14407.1086092051,
1337 yOffset = 290.3230897653046,
1338 zoneData = {
1339 Alterac = {
1340 height = 1866.673586850316,
1341 width = 2800.000436369314,
1342 xOffset = 17388.63313899802,
1343 yOffset = 9676.382605411302,
1344 mapID = 2597,
1346 Arathi = {
1347 height = 2400.0092446309,
1348 width = 3599.999380663208,
1349 xOffset = 19038.63328411639,
1350 yOffset = 11309.72201070757,
1351 mapID = 3358,
1353 Badlands = {
1354 height = 1658.340965090961,
1355 width = 2487.498490907989,
1356 xOffset = 20251.1337564772,
1357 yOffset = 17065.99404487956,
1358 mapID = 3,
1360 BlastedLands = {
1361 height = 2233.343415116865,
1362 width = 3349.999381676505,
1363 xOffset = 19413.63362865575,
1364 yOffset = 21743.09582955139,
1365 mapID = 4,
1367 BurningSteppes = {
1368 height = 1952.091972408385,
1369 width = 2929.16694293186,
1370 xOffset = 18438.633261567,
1371 yOffset = 18207.66513379744,
1372 mapID = 46,
1374 DeadwindPass = {
1375 height = 1666.673818905317,
1376 width = 2499.999888210889,
1377 xOffset = 19005.29993968603,
1378 yOffset = 21043.0932328648,
1379 mapID = 41,
1381 DunMorogh = {
1382 height = 3283.345779814337,
1383 width = 4924.998791911572,
1384 xOffset = 16369.8840376619,
1385 yOffset = 15053.48695195484,
1386 mapID = 1,
1388 Duskwood = {
1389 height = 1800.007653419076,
1390 width = 2699.999669551933,
1391 xOffset = 17338.63354148773,
1392 yOffset = 20893.09259181909,
1393 mapID = 10,
1395 EasternPlaguelands = {
1396 height = 2687.510360231216,
1397 width = 4031.249051993366,
1398 xOffset = 20459.46801235962,
1399 yOffset = 7472.207045901617,
1400 mapID = 139,
1402 Elwynn = {
1403 height = 2314.591970284716,
1404 width = 3470.831971412848,
1405 xOffset = 16636.55099386465,
1406 yOffset = 19116.0027890283,
1407 mapID = 12,
1409 EversongWoods = {
1410 height = 3283.346366715794,
1411 width = 4924.998483501337,
1412 xOffset = 20259.46725884782,
1413 yOffset = 2534.687567863296,
1414 mapID = 3430,
1416 Ghostlands = {
1417 height = 2200.008945183733,
1418 width = 3300.002855743766,
1419 xOffset = 21055.29786070095,
1420 yOffset = 5309.698546426793,
1421 mapID = 3433,
1423 Hilsbrad = {
1424 height = 2133.341840477916,
1425 width = 3200.000391416799,
1426 xOffset = 17105.29968281043,
1427 yOffset = 10776.38652289269,
1428 mapID = 267,
1430 Hinterlands = {
1431 height = 2566.676323518885,
1432 width = 3849.998492380244,
1433 xOffset = 19746.96704279287,
1434 yOffset = 9709.715966757984,
1435 mapID = 47,
1437 Ironforge = {
1438 height = 527.6056771582851,
1439 width = 790.6252518322632,
1440 xOffset = 18885.55815177769,
1441 yOffset = 15745.64795436116,
1442 mapID = 1537,
1444 LochModan = {
1445 height = 1839.590356444166,
1446 width = 2758.33360594204,
1447 xOffset = 20165.71623436714,
1448 yOffset = 15663.90573348468,
1449 mapID = 38,
1451 Redridge = {
1452 height = 1447.922213393415,
1453 width = 2170.833229570681,
1454 xOffset = 19742.79960560691,
1455 yOffset = 19751.42209395218,
1456 mapID = 44,
1458 SearingGorge = {
1459 height = 1487.505203229038,
1460 width = 2231.250200533406,
1461 xOffset = 18494.88325409831,
1462 yOffset = 17276.41231120941,
1463 mapID = 51,
1465 SilvermoonCity = {
1466 height = 806.7751969249011,
1467 width = 1211.458551923779,
1468 xOffset = 22172.71573747824,
1469 yOffset = 3422.647395021269,
1470 mapID = 3487,
1472 Silverpine = {
1473 height = 2800.011187621704,
1474 width = 4200.000573479695,
1475 xOffset = 14721.96646274185,
1476 yOffset = 9509.714741967448,
1477 mapID = 130,
1479 Stormwind = {
1480 height = 1158.33686894901,
1481 width = 1737.498058940429,
1482 xOffset = 16449.05164642256,
1483 yOffset = 19172.25350774846,
1484 mapID = 1519,
1486 Stranglethorn = {
1487 height = 4254.18312444072,
1488 width = 6381.248484543122,
1489 xOffset = 15951.13375783437,
1490 yOffset = 22345.18258706305,
1491 mapID = 33,
1493 Sunwell = {
1494 height = 2218.756638064149,
1495 width = 3327.084777999942,
1496 xOffset = 21074.0484502027,
1497 yOffset = 7.595267688679496,
1498 mapID = 4080,
1500 SwampOfSorrows = {
1501 height = 1529.173695058727,
1502 width = 2293.753807610138,
1503 xOffset = 20394.88183258176,
1504 yOffset = 20797.25913588854,
1505 mapID = 8,
1507 Tirisfal = {
1508 height = 3012.510490816506,
1509 width = 4518.749381850256,
1510 xOffset = 15138.63417865412,
1511 yOffset = 7338.874503644808,
1512 mapID = 85,
1514 Undercity = {
1515 height = 640.1067253394195,
1516 width = 959.3752013853186,
1517 xOffset = 17298.77399735696,
1518 yOffset = 9298.435338905521,
1519 mapID = 1497,
1521 WesternPlaguelands = {
1522 height = 2866.677213191588,
1523 width = 4299.998717025251,
1524 xOffset = 17755.30067544475,
1525 yOffset = 7809.708745090687,
1526 mapID = 28,
1528 Westfall = {
1529 height = 2333.342039971409,
1530 width = 3500.001170481545,
1531 xOffset = 15155.29922254704,
1532 yOffset = 20576.42557120998,
1533 mapID = 40,
1535 Wetlands = {
1536 height = 2756.260286844545,
1537 width = 4135.414389381328,
1538 xOffset = 18561.55091405621,
1539 yOffset = 13324.31339403164,
1540 mapID = 11,
1544 -- Outland
1545 { -- [3]
1546 parentContinent = 3,
1547 height = 11642.355227091,
1548 width = 17463.987300595,
1549 zoneData = {
1550 BladesEdgeMountains = {
1551 height = 3616.553511321226,
1552 width = 5424.972055480694,
1553 xOffset = 4150.184214583454,
1554 yOffset = 1412.98225932006,
1555 mapID = 3522,
1557 Hellfire = {
1558 height = 3443.642450656037,
1559 width = 5164.556104714847,
1560 xOffset = 7456.417230912641,
1561 yOffset = 4339.973750274888,
1562 mapID = 3483,
1564 Nagrand = {
1565 height = 3683.218538167106,
1566 width = 5524.971495006054,
1567 xOffset = 2700.192018521809,
1568 yOffset = 5779.511974812862,
1569 mapID = 3518,
1571 Netherstorm = {
1572 height = 3716.550608724641,
1573 width = 5574.970083688359,
1574 xOffset = 7512.667416095402,
1575 yOffset = 365.0979827402549,
1576 mapID = 3523,
1578 ShadowmoonValley = {
1579 height = 3666.552070430093,
1580 width = 5499.971770418525,
1581 xOffset = 8770.993458280615,
1582 yOffset = 7769.033264592288,
1583 mapID = 3520,
1585 ShattrathCity = {
1586 height = 870.8059516186869,
1587 width = 1306.242821388422,
1588 xOffset = 6860.744740098593,
1589 yOffset = 7295.086120456203,
1590 mapID = 3703,
1592 TerokkarForest = {
1593 height = 3599.887783533737,
1594 width = 5399.971351016305,
1595 xOffset = 5912.675516998205,
1596 yOffset = 6821.146319031154,
1597 mapID = 3519,
1599 Zangarmarsh = {
1600 height = 3351.978710181591,
1601 width = 5027.057650868489,
1602 xOffset = 3521.020638264577,
1603 yOffset = 3885.821278366336,
1604 mapID = 3521,
1610 --- WotLK Adjustments, now permanently enabled. Someday I should probably merge these in.
1611 if true then
1612 WorldMapSize[0].height = 31809.64859753034;
1613 WorldMapSize[0].width = 47714.27770954026;
1615 WorldMapSize[1].xOffset = -8590.409362625034;
1616 WorldMapSize[1].yOffset = 5628.694276155668;
1618 WorldMapSize[2].xOffset = 18542.31268111796;
1619 WorldMapSize[2].yOffset = 3585.574682467752;
1622 WorldMapSize[4] = {
1623 parentContinent = 0,
1624 height = 11834.31067391958,
1625 width = 17751.3936186856,
1626 xOffset = 16020.94093549576,
1627 yOffset = 454.2464807713226,
1628 zoneData = {
1629 BoreanTundra = {
1630 height = 3843.765503862232,
1631 width = 5764.58206497758,
1632 xOffset = 646.3186767730767,
1633 yOffset = 5695.480016983896,
1634 mapID = 3537,
1636 CrystalsongForest = {
1637 height = 1814.590053385046,
1638 width = 2722.916164555434,
1639 xOffset = 7773.400227973558,
1640 yOffset = 4091.307437548815,
1641 mapID = 2817,
1643 Dalaran = {
1644 height = 553.3419356683534,
1645 width = 830.014625253355,
1646 xOffset = 8164.640128758279,
1647 yOffset = 4526.722218200071,
1648 mapID = 4395,
1650 Dragonblight = {
1651 height = 3739.597759999098,
1652 width = 5608.331259502691,
1653 xOffset = 5590.067753073641,
1654 yOffset = 5018.394106536425,
1655 mapID = 65,
1657 GrizzlyHills = {
1658 height = 3500.013349296343,
1659 width = 5249.9986179934,
1660 xOffset = 10327.56614428777,
1661 yOffset = 5076.727864214266,
1662 mapID = 394,
1664 HowlingFjord = {
1665 height = 4031.266275060274,
1666 width = 6045.831339550668,
1667 xOffset = 10615.0658552538,
1668 yOffset = 7476.736868262738,
1669 mapID = 495,
1671 IcecrownGlacier = {
1672 height = 4181.266519840844,
1673 width = 6270.832975322177,
1674 xOffset = 3773.401695036191,
1675 yOffset = 1166.296622984233,
1676 mapID = 210,
1678 LakeWintergrasp = {
1679 height = 1983.342901980711,
1680 width = 2974.999377667768,
1681 xOffset = 4887.984320612982,
1682 yOffset = 4876.725348039468,
1683 mapID = 4197,
1685 SholazarBasin = {
1686 height = 2904.177559586215,
1687 width = 4356.248328680455,
1688 xOffset = 2287.985279107324,
1689 yOffset = 3305.887993444818,
1690 mapID = 3711,
1692 TheStormPeaks = {
1693 height = 4741.684940421732,
1694 width = 7112.498205872217,
1695 xOffset = 7375.483315518691,
1696 yOffset = 395.4596828327046,
1697 mapID = 67,
1699 ZulDrak = {
1700 height = 3329.179510740043,
1701 width = 4993.747919923504,
1702 xOffset = 9817.150055203074,
1703 yOffset = 2924.636381254688,
1704 mapID = 66,
1706 HrothgarsLanding = {
1707 height = 2452.7,
1708 width = 2452.7*1.5,
1709 xOffset = 23967.599 - 17549.182,
1710 yOffset = 1027.392 - 1215.431,
1715 local function VContinent(index, name, size)
1716 assert(1, not WorldMapSize[index], "denied")
1718 WorldMapSize[index] = {
1719 parentContinent = index,
1720 height = size,
1721 width = size*1.5,
1722 zoneData = { },
1724 WorldMapSize[index].zoneData[name] = {
1725 height = size,
1726 width = size*1.5,
1727 xOffset = 0,
1728 yOffset = 0,
1732 VContinent(-77, "ScarletEnclave", 2125)
1735 VContinent(-80, "UtgardeKeep1", 100) -- temporary value
1736 VContinent(-81, "UtgardeKeep2", 100) -- temporary value
1737 VContinent(-82, "UtgardeKeep3", 100) -- temporary value
1739 VContinent(-83, "TheNexus", 734.2)
1741 VContinent(-84, "AzjolNerub1", 100) -- temporary value
1742 VContinent(-85, "AzjolNerub2", 100) -- temporary value
1743 VContinent(-86, "AzjolNerub3", 100) -- temporary value
1745 VContinent(-87, "Ahnkahet", 648.3)
1747 VContinent(-88, "DrakTharonKeep1", 100) -- temporary value
1748 VContinent(-89, "DrakTharonKeep2", 100) -- temporary value
1750 VContinent(-90, "VioletHold", 170.83)
1752 VContinent(-91, "Gundrak", 603.35)
1754 VContinent(-92, "Ulduar77", 613.5) -- Halls of Stone
1756 VContinent(-93, "HallsofLightning1", 100) -- temporary value
1757 VContinent(-94, "HallsofLightning2", 100) -- temporary value
1759 VContinent(-95, "Nexus801", 100) -- temporary value -- Oculus
1760 VContinent(-96, "Nexus802", 100) -- temporary value
1761 VContinent(-97, "Nexus803", 100) -- temporary value
1762 VContinent(-98, "Nexus804", 100) -- temporary value
1764 VContinent(-99, "CoTStratholme1", 750.2)
1765 VContinent(-100, "CoTStratholme2", 1216.66)
1767 VContinent(-101, "UtgardePinnacle1", 100) -- temporary value -- hey they spelled it right
1768 VContinent(-102, "UtgardePinnacle2", 100) -- temporary value
1770 VContinent(-103, "VaultofArchavon", 603.25) -- temporary value -- Weirdly, Emalon is actually within the "Vault of Archavon"
1772 VContinent(-104, "Naxxramas1", 1237.5) -- construct quarter
1773 VContinent(-105, "Naxxramas2", 1237.5) -- arachnid quarter
1774 VContinent(-106, "Naxxramas3", 1237.5) -- military quarter
1775 VContinent(-107, "Naxxramas4", 1237.5) -- plague quarter
1776 VContinent(-108, "Naxxramas5", 1379.9) -- overview
1777 VContinent(-109, "Naxxramas6", 437.3) -- lair
1779 VContinent(-110, "TheObsidianSanctum", 775.1)
1781 VContinent(-111, "TheEyeOfEternity", 286.7)
1783 VContinent(-112, "Ulduar", 2191.7) -- temporary value
1784 VContinent(-113, "Ulduar1", 446.5) -- temporary value
1785 VContinent(-114, "Ulduar2", 885.6) -- temporary value
1786 VContinent(-115, "Ulduar3", 100) -- temporary value
1787 VContinent(-116, "Ulduar4", 100) -- temporary value
1790 VirtualContinentIndexes = { -- Don't change values here, since programs might want to store them
1791 ["ScarletEnclave"] = -77,
1793 ["UtgardeKeep1"] = -80,
1794 ["UtgardeKeep2"] = -81,
1795 ["UtgardeKeep3"] = -82,
1797 ["TheNexus"] = -83,
1799 ["AzjolNerub1"] = -84,
1800 ["AzjolNerub2"] = -85,
1801 ["AzjolNerub3"] = -86,
1803 ["Ahnkahet"] = -87,
1805 ["DrakTharonKeep1"] = -88,
1806 ["DrakTharonKeep2"] = -89,
1808 ["VioletHold"] = -90,
1810 ["Gundrak"] = -91,
1812 ["Ulduar77"] = -92, -- Halls of Stone
1814 ["HallsofLightning1"] = -93,
1815 ["HallsofLightning2"] = -94,
1817 ["Nexus801"] = -95, -- Oculus
1818 ["Nexus802"] = -96,
1819 ["Nexus803"] = -97,
1820 ["Nexus804"] = -98,
1822 ["CoTStratholme1"] = -99,
1823 ["CoTStratholme2"] = -100,
1825 ["UtgardePinnacle1"] = -101, -- hey they spelled it right
1826 ["UtgardePinnacle2"] = -102,
1828 ["VaultofArchavon"] = -103, -- Weirdly, Emalon is actually within the "Vault of Archavon"
1830 ["Naxxramas1"] = -104,
1831 ["Naxxramas2"] = -105,
1832 ["Naxxramas3"] = -106,
1833 ["Naxxramas4"] = -107,
1834 ["Naxxramas5"] = -108,
1835 ["Naxxramas6"] = -109,
1837 ["TheObsidianSanctum"] = -110,
1839 ["TheEyeOfEternity"] = -111,
1841 ["Ulduar"] = -112,
1842 ["Ulduar1"] = -113,
1843 ["Ulduar2"] = -114,
1844 ["Ulduar3"] = -115,
1845 ["Ulduar4"] = -116,
1848 DongleStub:Register(Astrolabe, activate)
1850 local zeroData;
1851 zeroData = { xOffset = 0, height = 0, yOffset = 0, width = 0, __index = function() return zeroData end };
1852 setmetatable(zeroData, zeroData);
1853 setmetatable(WorldMapSize, zeroData);
1855 for continent, zones in pairs(Astrolabe.ContinentList) do
1856 local mapData = WorldMapSize[continent];
1857 for index, mapName in pairs(zones) do
1858 if not ( mapData.zoneData[mapName] ) then
1859 --WE HAVE A PROBLEM!!!
1860 ChatFrame1:AddMessage("Astrolabe is missing data for "..select(index, GetMapZones(continent))..".");
1861 mapData.zoneData[mapName] = zeroData;
1863 mapData[index] = mapData.zoneData[mapName];
1864 mapData.zoneData[mapName] = nil;
1869 -- register this library with AstrolabeMapMonitor, this will cause a full update if PLAYER_LOGIN has already fired
1870 local AstrolabeMapMonitor = DongleStub("AstrolabeMapMonitor");
1871 AstrolabeMapMonitor:RegisterAstrolabeLibrary(Astrolabe, LIBRARY_VERSION_MAJOR);