vanilla-wow-addons – Blame information for rev 1
?pathlinks?
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 |