vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --
2 -- FlightMap - AddOn to show inbound and outbound flightpaths from a given
3 -- zone on the World Map. Additionally shows flight costs and
4 -- zone level ranges.
5 -- Copyright (c) 2005 Byron Ellacott (Dhask of Uther)
6 --
7 -- An unlimited license to use, reproduce and copy this work is granted, on
8 -- the condition that the licensee accepts all responsibility and liability
9 -- for any damage that may arise from the use of this AddOn.
10  
11 -- Version number
12 FLIGHTMAP_VERSION = "1.12-1";
13  
14 -- Maximum lines to draw at once
15 FLIGHTMAP_MAX_PATHS = 15;
16  
17 -- Size and names for path texture files
18 FLIGHTMAP_LINE_SIZE = 256;
19 FLIGHTMAP_TEX_UP = "Interface\\AddOns\\FlightMap\\FlightMapUp";
20 FLIGHTMAP_TEX_DOWN = "Interface\\AddOns\\FlightMap\\FlightMapDown";
21  
22 -- Maximum POI buttons defined
23 FLIGHTMAP_MAX_POIS = 15;
24  
25 -- How many pixels is too close to another POI?
26 FLIGHTMAP_CLOSE = 16;
27  
28 -- Textures for flightmaster POI icons
29 FLIGHTMAP_POI_KNOWN = "Interface\\TaxiFrame\\UI-Taxi-Icon-Green";
30 FLIGHTMAP_POI_OTHER = "Interface\\TaxiFrame\\UI-Taxi-Icon-Gray";
31  
32 local lTYPE_HORDE = FLIGHTMAP_HORDE;
33 local lTYPE_ALLIANCE = FLIGHTMAP_ALLIANCE;
34 local lTYPE_CONTESTED = FLIGHTMAP_CONTESTED;
35  
36 -- According to http://www.worldofwarcraft.com/ at any rate...
37 FLIGHTMAP_RANGES = {
38 [FLIGHTMAP_ELWYNN] = { 1, 10, lTYPE_ALLIANCE},
39 [FLIGHTMAP_DUNMOROGH] = { 1, 10, lTYPE_ALLIANCE},
40 [FLIGHTMAP_TIRISFAL] = { 1, 10, lTYPE_HORDE},
41 [FLIGHTMAP_LOCHMODAN] = {10, 20, lTYPE_ALLIANCE},
42 [FLIGHTMAP_SILVERPINE] = {10, 20, lTYPE_HORDE},
43 [FLIGHTMAP_WESTFALL] = {10, 20, lTYPE_ALLIANCE},
44 [FLIGHTMAP_REDRIDGE] = {15, 25, lTYPE_CONTESTED},
45 [FLIGHTMAP_DUSKWOOD] = {18, 30, lTYPE_CONTESTED},
46 [FLIGHTMAP_HILLSBRAD] = {20, 30, lTYPE_CONTESTED},
47 [FLIGHTMAP_WETLANDS] = {20, 30, lTYPE_CONTESTED},
48 [FLIGHTMAP_ALTERAC] = {30, 40, lTYPE_CONTESTED},
49 [FLIGHTMAP_ARATHI] = {30, 40, lTYPE_CONTESTED},
50 [FLIGHTMAP_STRANGLETHORN] = {30, 45, lTYPE_CONTESTED},
51 [FLIGHTMAP_BADLANDS] = {35, 45, lTYPE_CONTESTED},
52 [FLIGHTMAP_SORROWS] = {35, 45, lTYPE_CONTESTED},
53 [FLIGHTMAP_HINTERLANDS] = {40, 50, lTYPE_CONTESTED},
54 [FLIGHTMAP_SEARINGGORGE] = {43, 50, lTYPE_CONTESTED},
55 [FLIGHTMAP_BLASTEDLANDS] = {45, 55, lTYPE_CONTESTED},
56 [FLIGHTMAP_BURNINGSTEPPE] = {50, 58, lTYPE_CONTESTED},
57 [FLIGHTMAP_WESTERNPLAGUE] = {51, 58, lTYPE_CONTESTED},
58 [FLIGHTMAP_EASTERNPLAGUE] = {53, 60, lTYPE_CONTESTED},
59 [FLIGHTMAP_DUROTAR] = { 1, 10, lTYPE_HORDE},
60 [FLIGHTMAP_MULGORE] = { 1, 10, lTYPE_HORDE},
61 [FLIGHTMAP_DARKSHORE] = {10, 20, lTYPE_ALLIANCE},
62 [FLIGHTMAP_BARRENS] = {10, 25, lTYPE_HORDE},
63 [FLIGHTMAP_STONETALON] = {15, 27, lTYPE_CONTESTED},
64 [FLIGHTMAP_ASHENVALE] = {18, 30, lTYPE_CONTESTED},
65 [FLIGHTMAP_1KNEEDLES] = {25, 35, lTYPE_CONTESTED},
66 [FLIGHTMAP_DESOLACE] = {30, 40, lTYPE_CONTESTED},
67 [FLIGHTMAP_DUSTWALLOW] = {35, 45, lTYPE_CONTESTED},
68 [FLIGHTMAP_FERALAS] = {40, 50, lTYPE_CONTESTED},
69 [FLIGHTMAP_TANARIS] = {40, 50, lTYPE_CONTESTED},
70 [FLIGHTMAP_AZSHARA] = {45, 55, lTYPE_CONTESTED},
71 [FLIGHTMAP_FELWOOD] = {48, 55, lTYPE_CONTESTED},
72 [FLIGHTMAP_UNGOROCRATER] = {48, 55, lTYPE_CONTESTED},
73 [FLIGHTMAP_SILITHUS] = {55, 60, lTYPE_CONTESTED},
74 [FLIGHTMAP_WINTERSPRING] = {55, 60, lTYPE_CONTESTED},
75 [FLIGHTMAP_TELDRASSIL] = { 1, 10, lTYPE_ALLIANCE},
76 [FLIGHTMAP_MOONGLADE] = { 1, 60, lTYPE_CONTESTED},
77 [FLIGHTMAP_DEADWINDPASS] = {55, 60, lTYPE_CONTESTED},
78 };
79  
80 -- Colours for zones
81 FLIGHTMAP_COLORS = {
82 Unknown = { r = 0.8, g = 0.8, b = 0.8 },
83 Hostile = { r = 0.9, g = 0.2, b = 0.2 },
84 Friendly = { r = 0.2, g = 0.9, b = 0.2 },
85 Contested = { r = 0.8, g = 0.6, b = 0.4 },
86 };
87  
88 -- Auto dismount for these buffs
89 FLIGHTMAP_DISMOUNTS = {
90 ["Interface\\Icons\\Ability_Mount_BlackDireWolf"] = 1,
91 ["Interface\\Icons\\Ability_Mount_BlackPanther"] = 1,
92 ["Interface\\Icons\\Ability_Mount_Charger"] = 1,
93 ["Interface\\Icons\\Ability_Mount_Dreadsteed"] = 1,
94 ["Interface\\Icons\\Ability_Mount_JungleTiger"] = 1,
95 ["Interface\\Icons\\Ability_Mount_Kodo_01"] = 1,
96 ["Interface\\Icons\\Ability_Mount_Kodo_02"] = 1,
97 ["Interface\\Icons\\Ability_Mount_Kodo_03"] = 1,
98 ["Interface\\Icons\\Ability_Mount_MechaStrider"] = 1,
99 ["Interface\\Icons\\INV_Misc_Horn_01"] = 1,
100 ["Interface\\Icons\\Ability_Mount_MountainRam"] = 1,
101 ["Interface\\Icons\\Spell_Nature_Swiftness"] = 1,
102 ["Interface\\Icons\\Ability_Mount_NightmareHorse"] = 1,
103 ["Interface\\Icons\\Ability_Mount_PinkTiger"] = 1,
104 ["Interface\\Icons\\Ability_Mount_Raptor"] = 1,
105 ["Interface\\Icons\\Ability_Mount_RidingHorse"] = 1,
106 ["Interface\\Icons\\Ability_Mount_Undeadhorse"] = 1,
107 ["Interface\\Icons\\Ability_Mount_WhiteDireWolf"] = 1,
108 ["Interface\\Icons\\Ability_Mount_WhiteTiger"] = 1,
109 }
110  
111 ------------------ Data access functions ------------------
112  
113 local function lStripPoint(map, point)
114 for k, v in map do
115 if v.Costs then v.Costs[point] = nil; end
116 if v.Flights then v.Flights[point] = nil; end
117 end
118 for k, v in FlightMap.Knowledge do
119 v[point] = nil;
120 end
121 map[point] = nil;
122 end
123  
124 local function lSetDefaultData()
125 -- Create an empty knowledge record
126 if not FlightMap["Knowledge"] then
127 FlightMap.Knowledge = {};
128 end
129  
130 -- Default option settings
131 if (not FlightMap["Opts"]) then
132 FlightMap["Opts"] = FLIGHTMAP_DEFAULT_OPTS;
133 end
134  
135 -- Any options that don't have a value at all should be defaulted
136 for k, v in pairs(FLIGHTMAP_DEFAULT_OPTS) do
137 if FlightMap.Opts[k] == nil then
138 FlightMap.Opts[k] = v;
139 end
140 end
141  
142 -- Patch 1.8: Remove any references to Valor's Rest
143 lStripPoint(FlightMap[FLIGHTMAP_HORDE] or {}, "1:461:226");
144 lStripPoint(FlightMap[FLIGHTMAP_ALLIANCE] or {}, "1:463:223");
145  
146 -- Patch 1.12: Remove any references to Alliance's misplaced Moonglade
147 lStripPoint(FlightMap[FLIGHTMAP_ALLIANCE] or {}, "1:552:793");
148  
149 -- Revision 1.8-2: Delete pre-1.7 data
150 FlightMap.Locs = nil;
151 FlightMap.Times = nil;
152 end
153  
154 -- Learn about the currently open taxi map
155 local function lLearnTaxiNode()
156 -- Get the faction appropriate map
157 local map = FlightMapUtil.getFlightMap();
158  
159 -- Save the old map settings
160 local oldCont, oldZone = GetCurrentMapContinent(),
161 GetCurrentMapZone();
162  
163 -- Ensure the map is set to the right place
164 SetMapToCurrentZone();
165  
166 -- Get the current continent number
167 local thisCont = GetCurrentMapContinent();
168  
169 -- Extract the taxi map information
170 local thisNode;
171 local destinations = {};
172 local numNodes = NumTaxiNodes();
173 for index = 1, numNodes, 1 do
174 local tType = TaxiNodeGetType(index);
175 if (tType == "CURRENT") then
176 thisNode = index;
177 elseif (tType == "REACHABLE") then
178 local mx, my = TaxiNodePosition(index);
179 local destName = FlightMapUtil.makeNodeName(thisCont, mx, my);
180  
181 -- Add to list of destinations
182 destinations[destName] = index;
183  
184 -- Note that the character knows of this node
185 FlightMapUtil.knownNode(destName, true);
186  
187 -- Create a dummy entry if the node isn't known. This helps
188 -- prevent bugs later, and ensures the name is going to be known
189 -- pretty much straight away. Most of the data is pure bunk, of
190 -- course, but with a continent number of -1, the node will
191 -- generally be ignored.
192 if not map[destName] then
193 map[destName] = {
194 Name = "Fix me",
195 Zone = "Unknown!",
196 Continent = -1,
197 Flights = {},
198 Costs = {},
199 Routes = {},
200 Location = {
201 Taxi = { x = mx, y = my },
202 Zone = { x = 0, y = 0 },
203 Continent = { x = 0, y = 0 },
204 },
205 };
206 end
207  
208 -- Update the name; doing this as often as possible
209 -- ensures translations are done as soon as a node is
210 -- recognised as known
211 if map[destName] then
212 map[destName].Name = TaxiNodeName(index);
213 end
214 end
215 end
216  
217 -- If the current node was found (should always be, but... eh.)
218 if (thisNode) then
219 -- Establish the coded name of this node
220 local mx, my = TaxiNodePosition(thisNode);
221 local thisName = FlightMapUtil.makeNodeName(thisCont, mx, my);
222 local zoneName = FlightMapUtil.getZoneName();
223 local zx, zy = GetPlayerMapPosition("player");
224 SetMapZoom(thisCont, nil);
225 local cx, cy = GetPlayerMapPosition("player");
226  
227 -- Player knows this node now
228 FlightMapUtil.knownNode(thisName, true);
229  
230 -- Get, or create, the info structure for the node
231 if not map[thisName] then
232 map[thisName] = {};
233 end
234 if not map[thisName].Flights then
235 map[thisName].Flights = {};
236 end
237 if not map[thisName].Costs then
238 map[thisName].Costs = {};
239 end
240 if not map[thisName].Routes then
241 map[thisName].Routes = {};
242 end
243  
244 -- Update all relevant details, to ensure mistakes are fixed
245 map[thisName].Name = TaxiNodeName(thisNode);
246 map[thisName].Zone = zoneName;
247 map[thisName].Continent = thisCont;
248 map[thisName].Location = {
249 Zone = { x = zx, y = zy },
250 Continent = { x = cx, y = cy },
251 Taxi = { x = mx, y = my },
252 };
253  
254 -- Create Costs index field if missing (thorarin@tiwaz.org)
255 if not map[thisName].Costs then
256 map[thisName].Costs = {};
257 end
258  
259 -- Record everywhere this node flies to
260 for k,v in pairs(destinations) do
261 -- Store cost
262 map[thisName].Costs[k] = TaxiNodeCost(v);
263  
264 -- If it's a multihop, store route and try to estimate time
265 local routes = GetNumRoutes(v);
266 if routes > 1 then
267 local totalTime = 0;
268 local prevSpot = thisName;
269 local newRoute = {};
270 for r = 1, routes do
271 local dest = FlightMapUtil.makeNodeName(thisCont,
272 TaxiGetDestX(v, r), TaxiGetDestY(v, r));
273 table.insert(newRoute, dest);
274  
275 -- Must know last spot, last spot must have a non-zero
276 -- time recorded for the new destination, and all
277 -- previous hops must have also been known
278 if map[prevSpot] and map[prevSpot].Flights[dest]
279 and map[prevSpot].Flights[dest] > 0
280 and totalTime then
281 totalTime = totalTime + map[prevSpot].Flights[dest];
282 else
283 totalTime = nil;
284 end
285  
286 -- Keep track of the last point in the flight chain
287 prevSpot = dest;
288 end
289  
290 -- Compare this route to the past one stored, if any
291 local oldRoute = map[thisName].Routes[k];
292 local isNewRoute = not oldRoute
293 or table.getn(oldRoute) ~= table.getn(newRoute)
294 or table.foreachi(newRoute, function(idx)
295 return newRoute[idx] ~= oldRoute[idx];
296 end);
297  
298 -- If it's a new route, store the new estimated time no
299 -- matter what was there, because what was there is for a
300 -- different flight anyway; this might mean the flight time
301 -- is removed entirely, but the general set-zero-time case
302 -- below will catch that.
303 if isNewRoute or map[thisName].Flights[k] == 0 then
304 map[thisName].Flights[k] = totalTime;
305 map[thisName].Routes[k] = newRoute;
306 end
307 else
308 -- Wipe out any previously stored route!
309 map[thisName].Routes[k] = nil;
310 end
311  
312 if not map[thisName].Flights[k] then
313 map[thisName].Flights[k] = 0; -- no duration yet
314 end
315 end
316 end
317  
318 -- Don't mess with the user's choice of zoom!
319 SetMapZoom(oldCont, oldZone);
320 end
321  
322 ------------------ Miscellaneous utility ------------------
323  
324 local function lAutoDismount()
325 if not FlightMap.Opts.autoDismount then return; end
326  
327 for i = 0, 15, 1 do
328 local id, isAura = GetPlayerBuff(i, "HELPFUL");
329 local texture = GetPlayerBuffTexture(id);
330 if isAura and FLIGHTMAP_DISMOUNTS[texture] then
331 CancelPlayerBuff(id);
332 end
333 end
334 end
335  
336 ------------------ Map drawing functions ------------------
337  
338 local function lFormatExtra(cost, secs)
339 local result = "";
340 local separator = "";
341 if cost ~= nil and FlightMap.Opts.showCosts then
342 local dosh = FlightMapUtil.formatMoney(cost);
343 if cost == 0 then dosh = FLIGHTMAP_NO_COST; end
344 result = result .. dosh;
345 separator = " ";
346 end
347 if secs ~= nil and FlightMap.Opts.showTimes then
348 local durn = FlightMapUtil.formatTime(secs);
349 result = result .. separator .. durn;
350 end
351 return result;
352 end
353  
354 -- Add node name and location into the given tooltip. If the source node is
355 -- given, also show any stop-off nodes along the way.
356 local function lAddFlightsForNode(tooltip, node, prefix, source)
357 -- Sanitize prefix
358 if not prefix then prefix = ""; end
359  
360 -- Need a map of flight nodes
361 local map = FlightMapUtil.getFlightMap();
362  
363 -- Get the node's data
364 local data = map[node];
365 if not data then return 0; end
366 if not data.Costs then data.Costs = {}; end
367  
368 -- Get name of node
369 local name = data.Name;
370  
371 -- And its zone location, if that's known
372 local locn = "";
373 if data.Location.Zone then
374 locn = string.format("%d, %d", data.Location.Zone.x * 100,
375 data.Location.Zone.y * 100);
376 end
377  
378 -- Add that info to the tooltip
379 if FlightMapUtil.knownNode(node) then
380 tooltip:AddDoubleLine(prefix .. name, locn);
381 else
382 local r = NORMAL_FONT_COLOR.r * 0.7;
383 local g = NORMAL_FONT_COLOR.g * 0.7;
384 local b = NORMAL_FONT_COLOR.b * 0.7;
385 tooltip:AddDoubleLine(prefix .. name, locn, r, g, b, r, g, b);
386 end
387  
388 -- Check for a route
389 prefix = prefix .. " ";
390 if source and map[source] then
391 if map[source].Flights[node] then
392 local durn = FlightMapUtil.formatTime(map[source].Flights[node]);
393 GameTooltip:AddLine(prefix .. FLIGHTMAP_FLIGHTTIME .. durn, 1, 1, 1);
394 end
395 if map[source].Routes[node] then
396 local src = map[source];
397 for i = 1, table.getn(src.Routes[node]) - 1 do
398 local hop = src.Routes[node][i];
399 GameTooltip:AddLine(prefix .. FLIGHTMAP_VIA .. map[hop].Name,
400 0.7, 0.7, 0.7);
401 end
402 end
403 end
404  
405 -- Check for flights from node
406 if not source and FlightMap.Opts.showDestinations then
407 for dest, secs in data.Flights do
408 local islocal = (not data.Routes or not data.Routes[dest]);
409 local destData = map[dest];
410 if destData and (islocal or FlightMap.Opts.showMultiHop) then
411 local name, _ = FlightMapUtil.getNameAndZone(destData.Name);
412 local cost = data.Costs[dest];
413 local extra = lFormatExtra(cost, secs);
414 if FlightMapUtil.knownNode(dest) then
415 tooltip:AddDoubleLine(prefix .. name, extra,
416 1, 1, 1, 1, 1, 1);
417 elseif FlightMap.Opts.showAllInfo then
418 tooltip:AddDoubleLine(prefix .. name, extra,
419 0.7, 0.7, 0.7, 0.7, 0.7, 0.7);
420 end
421 end
422 end
423 end
424  
425 return 1;
426 end
427 FlightMapUtil.addFlightsForNode = lAddFlightsForNode;
428  
429 -- Update the flight tooltip for a zone
430 local function lUpdateTooltip(zoneName)
431 -- No zone name, no tooltip!
432 if not zoneName or zoneName == "" then
433 FlightMapTooltip:Hide();
434 return;
435 end
436  
437 -- Doesn't matter which anchor point we use, none of them are
438 -- useful for what FlightMap needs...
439 FlightMapTooltip:SetOwner(this, "ANCHOR_LEFT");
440  
441 -- Determine colour and level range
442 local title = FLIGHTMAP_COLORS.Unknown;
443 local levels = nil;
444 if (FLIGHTMAP_RANGES[zoneName]) then
445 local _, faction = UnitFactionGroup("player");
446 local min = FLIGHTMAP_RANGES[zoneName][1];
447 local max = FLIGHTMAP_RANGES[zoneName][2];
448 local side = FLIGHTMAP_RANGES[zoneName][3];
449 if (side == lTYPE_CONTESTED) then
450 title = FLIGHTMAP_COLORS.Contested;
451 else
452 if (faction == side) then
453 title = FLIGHTMAP_COLORS.Friendly;
454 else
455 title = FLIGHTMAP_COLORS.Hostile;
456 end
457 end
458 levels = string.format(FLIGHTMAP_LEVELS, min, max);
459 end
460  
461 -- Show the zone title, add level range if known
462 FlightMapTooltip:SetText(zoneName, title.r, title.g, title.b);
463 if levels then
464 FlightMapTooltip:AddLine(levels, title.r, title.g, title.b);
465 end
466  
467 -- Discover and add all the flights, including subzones
468 local nodes = FlightMapUtil.getNodesInZone(zoneName, true);
469  
470 -- Outbound flights
471 local flights = 0;
472 -- FlightMapTooltip:AddLine("\n");
473 for node, data in nodes do
474 if FlightMapUtil.knownNode(node) or FlightMap.Opts.showAllInfo then
475 flights = flights + lAddFlightsForNode(FlightMapTooltip, node, "");
476 end
477 end
478  
479 -- This stuff seems to get reset each time, possibly by the SetOwner()
480 FlightMapTooltip:SetBackdropColor(0, 0, 0, 0.5);
481 FlightMapTooltip:SetBackdropBorderColor(0, 0, 0, 0);
482 FlightMapTooltip:ClearAllPoints();
483 FlightMapTooltip:SetPoint("BOTTOMLEFT", "WorldMapDetailFrame",
484 "BOTTOMLEFT", 0, 0);
485  
486 -- Only show if there's flight information or level information
487 if flights > 0 or levels then
488 FlightMapTooltip:Show();
489 else
490 FlightMapTooltip:Hide();
491 end
492  
493 -- Now go ahead and put the tooltip into the right location
494 FlightMapTooltip:ClearAllPoints();
495 FlightMapTooltip:SetPoint("BOTTOMLEFT", WorldMapDetailFrame);
496 end
497  
498 -- Returns true iff an existing world map POI icon is very close to the
499 -- given coordinates.
500 local function lCloseToExistingPOI(x, y)
501 for i = 1, NUM_WORLDMAP_POIS, 1 do
502 local button = getglobal("WorldMapFramePOI" .. i);
503 if button:IsVisible() then
504 local _, _, index, _, _ = GetMapLandmarkInfo(i);
505 -- Index 15 is an invisible POI
506 if index ~= 15 then
507 local px, py = button:GetCenter();
508 px = px - WorldMapDetailFrame:GetLeft();
509 py = py - WorldMapDetailFrame:GetBottom();
510 if abs(px - x) < FLIGHTMAP_CLOSE and
511 abs(py - y) < FLIGHTMAP_CLOSE then
512 return true;
513 end
514 end
515 end
516 end
517 return false;
518 end
519  
520 -- Try showing a POI node; returns true if the POI icon was displayed, or
521 -- false if it was too close to an existing POI icon, or there were no
522 -- known coordinates for the requested coordinate space, or the POI number
523 -- is out of range.
524 local function lShowNodePOI(node, data, space, num)
525 -- Ensure the coordinate space is known
526 if not data.Location[space] then return false; end
527  
528 -- Ensure the POI number is in range
529 if num > FLIGHTMAP_MAX_POIS then return false; end
530  
531 -- Get the coordinates
532 local x = data.Location[space].x;
533 local y = data.Location[space].y;
534  
535 -- Convert them to world map pixel-space
536 x = x * WorldMapDetailFrame:GetWidth();
537 y = (1 - y) * WorldMapDetailFrame:GetHeight();
538  
539 -- Ensure the point isn't close to an existing POI icon
540 if lCloseToExistingPOI(x, y) then return false; end
541  
542 -- Get the node name
543 local name, _ = FlightMapUtil.getNameAndZone(data.Name);
544  
545 -- Get the button
546 local button = getglobal("FlightMapPOI" .. num);
547  
548 -- Does the user know this flight node?
549 if not FlightMapUtil.knownNode(node) then
550 if not FlightMap.Opts.showAllInfo then
551 return false;
552 end
553 button:SetNormalTexture(FLIGHTMAP_POI_OTHER);
554 else
555 button:SetNormalTexture(FLIGHTMAP_POI_KNOWN);
556 end
557  
558 -- Set all data
559 button.name = name;
560 button.data = data;
561 button.node = node;
562 button:SetPoint("CENTER", "WorldMapDetailFrame",
563 "BOTTOMLEFT", x, y);
564 button:Show();
565  
566 -- Done!
567 return true;
568 end
569  
570 -- Show locations of flight masters for either continent or zone level maps
571 local function lUpdateFlightPOIs(zoneName)
572 local continent = GetCurrentMapContinent();
573 local mapZone = GetCurrentMapZone();
574 local POI = 1;
575  
576 if mapZone ~= 0 and FlightMap.Opts.showPOIs then
577 -- Zone level map
578 local nodes = FlightMapUtil.getNodesInZone(zoneName, false);
579 for node, data in nodes do
580 if lShowNodePOI(node, data, "Zone", POI) then
581 POI = POI + 1;
582 end
583 end
584 elseif continent ~= 0 and FlightMap.Opts.showPOIs then
585 -- Continent level map
586 local map = FlightMapUtil.getFlightMap();
587 for node, data in map do
588 -- Filter list by continent
589 if data.Continent == continent then
590 if lShowNodePOI(node, data, "Continent", POI) then
591 POI = POI + 1;
592 end
593 end
594 end
595 end
596  
597 -- Hide any remaining unused POI buttons
598 for i = POI, FLIGHTMAP_MAX_POIS, 1 do
599 getglobal("FlightMapPOI" .. i):Hide();
600 end
601 end
602  
603 -- Draw a line from one flight node to another; returns true if the line
604 -- was drawn, false if it could not be: number out of range, coordinates
605 -- not known, etc.
606 local function lDrawFlightLine(from, to, num)
607 -- Check range before doing any real work
608 if num > FLIGHTMAP_MAX_PATHS then return false; end
609  
610 -- Get the flight map
611 local map = FlightMapUtil.getFlightMap();
612  
613 -- Make sure both ends are known about
614 if not map[from] or not map[to] then return false; end
615  
616 -- Get the continent coordinate sets
617 local src = map[from].Location.Continent;
618 local dst = map[to].Location.Continent;
619  
620 -- Make sure both are known
621 if not src or not dst then return false; end;
622  
623 -- Get the texture to work with
624 local tex = getglobal("FlightMapPath" .. num);
625  
626 return FlightMapUtil.drawLine(WorldMapDetailFrame, tex,
627 src.x, src.y, dst.x, dst.y);
628 end
629  
630 -- Fill in flight map lines
631 local function lDrawFlightLines(zoneName)
632 local lineNum = 1;
633  
634 -- Only if the zone name is known
635 if zoneName and FlightMap.Opts.showPaths then
636 -- Iterate over nodes in the current zone
637 local nodes = FlightMapUtil.getNodesInZone(zoneName, true);
638 for node, data in nodes do
639 -- If the source node is known
640 if FlightMap.Opts.showAllInfo or FlightMapUtil.knownNode(node) then
641 -- ... then iterate over that node's outbound flights
642 for dest, duration in data.Flights do
643 -- If the destination node is known
644 if not (data.Routes and data.Routes[dest])
645 and (FlightMap.Opts.showAllInfo
646 or FlightMapUtil.knownNode(dest)) then
647 -- ... and the flight line can be drawn
648 if lDrawFlightLine(node, dest, lineNum) then
649 -- ... then increment the line number
650 lineNum = lineNum + 1;
651 end
652 end
653 end
654 end
655 end
656 end
657  
658 -- Hide remaining flight paths
659 for i = lineNum, FLIGHTMAP_MAX_PATHS, 1 do
660 getglobal("FlightMapPath" .. i):Hide();
661 end
662 end
663  
664 -- Last drawn info for tooltip
665 lFM_CurrentZone = nil;
666 lFM_CurrentArea = nil;
667 local lFM_OldUpdate = function() end;
668  
669 -- Replacement function to draw all the extra goodies of FlightMap
670 function FlightMap_WorldMapButton_OnUpdate(arg1)
671 lFM_OldUpdate(arg1);
672 local areaName = WorldMapFrame.areaName;
673 local zoneNum = GetCurrentMapZone();
674  
675 -- zone name equivalence map
676 if FLIGHTMAP_SUBZONES[areaName] then
677 areaName = FLIGHTMAP_SUBZONES[areaName];
678 end
679  
680 -- Bail out if nothing has changed
681 if zoneNum == lFM_CurrentZone and areaName == lFM_CurrentArea then
682 return;
683 end
684  
685 -- Continent or zone map?
686 if zoneNum == 0 then
687 lUpdateTooltip(areaName);
688 lUpdateFlightPOIs(areaName);
689 lDrawFlightLines(areaName);
690 else
691 lUpdateFlightPOIs(FlightMapUtil.getZoneName());
692 lUpdateTooltip(nil); -- hide it
693 lDrawFlightLines(nil); -- hide them
694 end
695 end
696  
697 function FlightMapPOIButton_OnEnter()
698 local x, y = this:GetCenter();
699 local parentX, parentY = WorldMapDetailFrame:GetCenter();
700 if (x > parentX) then
701 WorldMapTooltip:SetOwner(this, "ANCHOR_LEFT");
702 else
703 WorldMapTooltip:SetOwner(this, "ANCHOR_RIGHT");
704 end
705 lAddFlightsForNode(WorldMapTooltip, this.node, "");
706 WorldMapTooltip:Show();
707 end
708  
709 ---------------- Initialization functions -----------------
710  
711 -- /flightmap handler
712 function FlightMap_OnSlashCmd(args)
713 if args == FLIGHTMAP_RESET then
714 -- Reset the flight timer window's position
715 FlightMapTimesFrame:ClearAllPoints();
716 FlightMapTimesFrame:SetPoint("TOP", PVPArenaTextString, "BOTTOM");
717 elseif args == FLIGHTMAP_SHOWMAP then
718 FlightMapTaxi_ShowContinent();
719 elseif args == FLIGHTMAP_LOCKTIMES then
720 FlightMap.Opts.lockFlightTimes = not FlightMap.Opts.lockFlightTimes;
721 DEFAULT_CHAT_FRAME:AddMessage(
722 FLIGHTMAP_TIMESLOCKED[FlightMap.Opts.lockFlightTimes],
723 1.0, 1.0, 1.0);
724 elseif args == FLIGHTMAP_GETHELP then
725 for cmd, desc in FLIGHTMAP_SUBCOMMANDS do
726 DEFAULT_CHAT_FRAME:AddMessage("|cffcc9010" .. cmd .. "|r " .. desc,
727 1.0, 1.0, 1.0);
728 end
729 elseif (FlightMapOptionsFrame:IsVisible()) then
730 HideUIPanel(FlightMapOptionsFrame);
731 else
732 ShowUIPanel(FlightMapOptionsFrame);
733 end
734 end
735  
736 function FlightMap_OnLoad()
737 -- Hook TAXIMAP_OPENED to learn flight paths
738 this:RegisterEvent("TAXIMAP_OPENED");
739  
740 -- Override the world map function
741 if (Sea) then
742 Sea.util.hook("WorldMapButton_OnUpdate",
743 "FlightMap_WorldMapButton_OnUpdate",
744 "after");
745 else
746 lFM_OldUpdate = WorldMapButton_OnUpdate;
747 WorldMapButton_OnUpdate = FlightMap_WorldMapButton_OnUpdate;
748 end
749  
750 -- Set up my slash command
751 SLASH_FLIGHTMAP1 = "/fmap";
752 SLASH_FLIGHTMAP2 = "/flightmap";
753 SlashCmdList["FLIGHTMAP"] = FlightMap_OnSlashCmd;
754  
755 -- Register for VARIABLES_LOADED to talk to myAddOns
756 this:RegisterEvent("VARIABLES_LOADED");
757  
758 -- Register the options frame
759 UIPanelWindows["FlightMapOptionsFrame"] = {
760 area = "center",
761 pushable = 0,
762 };
763 end
764  
765 function FlightMap_OnEvent(event)
766 if (event == "TAXIMAP_OPENED") then
767 lAutoDismount();
768 lLearnTaxiNode();
769 elseif (event == "VARIABLES_LOADED") then
770 lSetDefaultData();
771  
772 -- Register with myAddOns
773 if (myAddOnsFrame_Register) then
774 myAddOnsFrame_Register({
775 name = "FlightMap",
776 version = FLIGHTMAP_VERSION,
777 releaseDate = FLIGHTMAP_RELEASE,
778 author = "Dhask",
779 category = MYADDONS_CATEGORY_MAP,
780 optionsframe = "FlightMapOptionsFrame",
781 });
782 end
783 end
784 end
785  
786 ----------------- Options panel functions -----------------
787  
788 function FlightMapOptionsFrame_OnShow()
789 -- Set localised strings
790 FlightMapOptionsFrameClose:SetText(FLIGHTMAP_OPTIONS_CLOSE);
791 FlightMapOptionsFrameTitle:SetText(FLIGHTMAP_OPTIONS_TITLE);
792  
793 -- Set up options from localised data
794 local base = "FlightMapOptionsFrame"
795 for optid, option in pairs(FLIGHTMAP_OPTIONS) do
796 local name = base .. "Opt" .. optid;
797 local button = getglobal(name);
798 local label = getglobal(name .. "Text");
799 OptionsFrame_EnableCheckBox(button, 1, FlightMap.Opts[option.option]);
800  
801 -- Simple stuff
802 label:SetText(option.label);
803 button.tooltipText = option.tooltip;
804 button.option = option.option;
805 button.children = option.children or {};
806 end
807  
808 for optid, option in pairs(FLIGHTMAP_OPTIONS) do
809 -- Enable/disable any children
810 for _, child in option.children or {} do
811 local other = getglobal(base .. "Opt" .. child);
812 if other then
813 if FlightMap.Opts[option.option] then
814 OptionsFrame_EnableCheckBox(other, 1,
815 FlightMap.Opts[FLIGHTMAP_OPTIONS[child].option]);
816 else
817 OptionsFrame_DisableCheckBox(other);
818 end
819 end
820 end
821 end
822 end
823  
824 function FlightMapOptionsFrame_OnHide()
825 if (MYADDONS_ACTIVE_OPTIONSFRAME == this) then
826 ShowUIPanel(myAddOnsFrame);
827 end
828 end
829  
830 function FlightMapOptionsCheckButton_OnClick()
831 if (this:GetChecked()) then
832 FlightMap.Opts[this.option] = true;
833 else
834 FlightMap.Opts[this.option] = false;
835 end
836  
837 local base = "FlightMapOptionsFrame";
838 for _, child in this.children do
839 local other = getglobal(base .. "Opt" .. child);
840 if other then
841 if FlightMap.Opts[this.option] then
842 OptionsFrame_EnableCheckBox(other, 1,
843 FlightMap.Opts[FLIGHTMAP_OPTIONS[child].option]);
844 else
845 OptionsFrame_DisableCheckBox(other);
846 end
847 end
848 end
849 end
850