vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 -- Kwraz's Flightpath Tracker Addon
2 --
3 -- Displays known flightpaths on the world map, etc.
4 --
5 -- All contents property of Kwraz of Icecrown. You are hereby given permission
6 -- to make changes to this addon provided proper credit is given if you
7 -- disseminate your changes publicly.
8 --
9 -- Comments and questions can be addressed to kwraz@kjware.net (or via in game
10 -- mail to Kwraz if you play Horde on Icecrown)
11 --
12  
13 local VERSION = "1.16";
14  
15 -- Date Rev Comments
16 -- ------ ---- -----------------------------------
17 -- 06/22/06 1.16 Updated for UI version 1.11
18 --
19 -- 10/12/05 1.15 Fixed error that occurred in 1.8 when reporting bound key
20 -- Updated TOC to 1.8
21 --
22 -- 9/13/05 1.14 Fixed WorldMapButton error in 1.7. UI version set to 1.7
23 --
24 -- 6/27/05 1.13 OnPlayerEnteringWorld logic only done once
25 -- Flight timer now takes UI delays into account
26 -- On screen destination width increased
27 --
28 -- 4/18/05 1.12 Removed debug statement causing user errors
29 --
30 -- 4/18/05 1.11 Fixed bug with /fp load introduced in 1.10
31 --
32 -- 4/18/05 1.10 SetMapToCurrentZone fixup
33 -- Bound key now toggles dialog on/off
34 -- Alt-z to hide UI now also hides time remaining counter
35 -- Cleaned up appearance of zone map connection tooltip
36 -- Changed regular expressions to correction handle foreign characters
37 -- Version number displayed on the dialog
38 -- Other faction's paths were showing on Booty Bay flightmaster. Fixed.
39 -- Changing zones with the dialog up caused the drop down to display
40 -- zones instead of locations. Fixed.
41 --
42 -- 4/14/05 1.09 Fixed incompatibility with VisibleFlightMap
43 -- Zone map icons changed to the same as the flight master's map
44 -- Undiscovered flight path locations show as grey on zone map
45 -- Dialog drop down box scaled down to fit within dialog
46 --
47 -- 4/13/05 1.08 Added bindings.xml to support hotkey assignment via wow menu
48 -- Flight master icons will no longer display on the zone map if greyed
49 -- Added flight times and costs to zone map tooltips
50 -- Added hideremaining and showremaining commands
51 -- Fixed problem with incorrect locations being stored for flight masters (hopefully)
52 --
53 -- 4/12/05 1.07 Cleaned up shared variable names to respect conventions. Added confirmation
54 -- dialog to /fp erase. Added MyAddons support. Flight time now shown when talking
55 -- to flight master. Flight time remaining countdown timer added. Flight duration
56 -- tracked separately for each direction. Added /fp load command. Fixed drop down
57 -- problem when more than 32 locations.
58 --
59 -- 4/8/05 1.06 Added /fp erase, /fp showgrey, /fp hidegray. Additional changes
60 -- made to location matching logic to try and avoid duplicate path
61 -- creation for Stormwind, Ironforge, and Moonglade.
62 --
63 -- 4/7/05 1.05 Fixed string error the first time FlightPath is installed.
64 --
65 -- 4/7/05 1.04 Whether or not to gray out a connection is now tracked by character,
66 -- since different characters will have different routes available to them.
67 -- Escape key now closes dialog.
68 --
69 -- 4/7/05 1.03 Greyed out unavailable routes in map tooltip as well.
70 --
71 -- 4/7/05 1.02 Preloaded flights you cannot take are now coded in a different color.
72 --
73 -- 4/6/05 1.01 Fixed a problem with the drop down list running off screen with a large number
74 -- of entries
75 -- 4/6/05 1.0 Initial release
76 --
77 ---------------------------------------------------------------------
78  
79 -- Info to show on the WoW Key Bindings menu
80  
81 BINDING_HEADER_FPHEADER = "Kwraz's Flightpath Tracker";
82 BINDING_NAME_FPDIALOG = "Query dialog";
83  
84 -- Constants
85  
86 local STATUS_COLOR = "|c0033CCFF";
87 local CONNECTION_COLOR = "|c0033FF66";
88 local MONEY_COLOR = "|c00FFCC33";
89 local DEBUG_COLOR = "|c0000FF00";
90 local GREY = "|c00909090";
91 local BRIGHTGREY = "|c00D0D0D0";
92 local WHITE = "|c00FFFFFF";
93 local MAX_CONNECTIONS = 16;
94 local MAX_POI_BUTTONS = 48;
95 local CONNECTION_START_ID = 1101;
96 local FP_LEARNED_FROM_FLIGHTMASTER = 1;
97 local FP_LEARNED_FROM_PRELOAD = 2;
98 local NOCOORDS = "0,0";
99 local MAXDROPDOWN = 21;
100 local MORECOLOR = "|c00FF9900";
101 local MOREUP = MORECOLOR.."--- More ^ ---";
102 local MOREDOWN = MORECOLOR.."--- More v ---";
103  
104 -- Shared variables
105  
106 local StartRecording = false;
107 local Recording = false;
108 local TaxiOrigin = "";
109 local TaxiDestination = "";
110 local TaxiDirection = 0;
111 local CurrentFlight = {};
112 local StartTime;
113 local Connections = {};
114 local LastTime = time();
115 local PopulatePass = 0;
116 local TaximapOpen = false;
117 local TimeRemaining = 0;
118 local DropDownStartIndex = 1;
119  
120 -- Hooked functions
121  
122 local Original_WorldMapButton_OnUpdate;
123 local Original_TaxiNodeOnButtonEnter;
124 local Original_TakeTaxiNode;
125  
126 ---------------------------------------------------------------------
127  
128 function FP_Help()
129 FPCHAT(" ");
130 FPCHAT(BINDING_HEADER_FPHEADER.." commands:");
131 FPCHAT("/fp" ..FPSPACE(50)..WHITE.." - Displays the FlightPath dialog");
132 FPCHAT("/fp enable | disable" ..FPSPACE(24)..WHITE.." - Enables or disables the FlightPath addon");
133 FPCHAT("/fp showgrey | hidegrey" ..FPSPACE(17)..WHITE.." - Show greyed out flight paths or not");
134 FPCHAT("/fp showremaining | hideremaining"..FPSPACE(0) ..WHITE.." - Show or hide in flight time remaining");
135 FPCHAT("/fp erase" ..FPSPACE(40)..WHITE.." - Erases all remembered flight paths");
136 FPCHAT("/fp load misc | horde | alliance" ..FPSPACE(7) ..WHITE.." - Load a supplied flight path list");
137 FPCHAT("/fp status" ..FPSPACE(40)..WHITE.." - Displays various flight path statistics");
138 FPCHAT(" ");
139 end
140  
141 ---------------------------------------------------------------------
142  
143 -- Handle command line arguments
144  
145 function FP_SlashHandler(msg)
146  
147 local _,_,command,options = string.find(msg,"([%w%p]+)%s*(.*)$");
148  
149 if (command == nil) then FP_FlightPathDialog();
150 elseif (string.lower(command) == 'enable') then FP_Enable();
151 elseif (string.lower(command) == 'disable') then FP_Disable();
152 elseif (string.lower(command) == 'status') then FP_Status();
153 elseif (string.lower(command) == 'erase') then FP_Erase();
154 elseif (string.lower(command) == 'showgrey') then FP_SetShowGrey();
155 elseif (string.lower(command) == 'hidegrey') then FP_SetHideGrey();
156 elseif (string.lower(command) == 'showremaining') then FP_SetShowRemaining();
157 elseif (string.lower(command) == 'hideremaining') then FP_SetHideRemaining();
158 elseif (string.lower(command) == 'load') then FP_Load(options);
159 elseif (string.lower(command) == 'check') then FP_Check(); -- Undocumented: Validate current flight master
160 elseif (string.lower(command) == 'debug') then FP_Debug(); -- Undocumented: Enable debug output
161 elseif (string.lower(command) == 'test') then FP_Test(options); -- Undocumented: Invoke testbed
162 else FP_Help();
163 end
164  
165 end
166  
167 ---------------------------------------------------------------------
168  
169 -- Tell WoW what events we are interested in being notified of
170  
171 function FP_EventFrame_OnLoad(frameName)
172  
173 -- Register for any event we want to be notified of
174  
175 this:RegisterEvent("VARIABLES_LOADED");
176 this:RegisterEvent("PLAYER_ENTERING_WORLD");
177 this:RegisterEvent("TAXIMAP_OPENED");
178 this:RegisterEvent("TAXIMAP_CLOSED");
179 this:RegisterEvent("WORLD_MAP_UPDATE");
180  
181 end
182  
183 ---------------------------------------------------------------------
184  
185 -- Map events to the appropriate internal handler
186  
187 function FP_EventFrame_OnEvent()
188  
189 if (event == "VARIABLES_LOADED") then FP_VariablesLoaded();
190 elseif (event == "PLAYER_ENTERING_WORLD") then FP_PlayerEnteringWorld();
191 elseif (event == "TAXIMAP_OPENED") then FP_TaxiMapOpened();
192 elseif (event == "TAXIMAP_CLOSED") then FP_TaxiMapClosed();
193 elseif (event == "WORLD_MAP_UPDATE") then FP_WorldMapUpdate();
194  
195 else FPDEBUG("Unhandled Event "..WHITE..event); end;
196 end
197  
198 ---------------------------------------------------------------------
199  
200 function FP_VariablesLoaded()
201  
202 -- Update the global WoW command handler table so it knows about us
203  
204 SlashCmdList["FLIGHTPATH"] = FP_SlashHandler;
205 SLASH_FLIGHTPATH1 = "/flightpath";
206 SLASH_FLIGHTPATH2 = "/fp";
207  
208 if (FlightPath_Config == nil) then FP_InitializeConfig(); end
209  
210 -- Let them know we're up and running
211  
212 FPCHAT(BINDING_HEADER_FPHEADER.." version "..VERSION.." loaded. Type "..WHITE.."/fp ?"..STATUS_COLOR.." for more info.");
213 local sbool = "disabled.";
214 if(FlightPath_Config.Enabled) then sbool = "enabled."; end;
215 FPCHAT("Flightpath hooks are "..sbool);
216  
217 -- MyAddons support
218  
219 if(myAddOnsFrame) then
220 myAddOnsList.FlightPath = { name = "FlightPath", description = BINDING_HEADER_FPHEADER, version = VERSION, category = MYADDONS_CATEGORY_MAP, frame = "FP_UIFrame"};
221 end
222  
223 end
224  
225 ---------------------------------------------------------------------
226  
227 function FP_InitializeConfig()
228 FlightPath_Config = {};
229 FlightPath_Config.Version = VERSION;
230 FlightPath_Config.Enabled = true;
231 FlightPath_Config.HideRemaining = false;
232 FlightPath_Config.FlightPaths = {};
233 end
234  
235 ---------------------------------------------------------------------
236  
237 function FP_PlayerEnteringWorld()
238 FP_DoVersionFixups(); -- Perform any version specific fixups on the users database
239 FlightPath_Config.Version = VERSION;
240 FP_EnableHooks();
241 FP_ReportBindingKey();
242 this:UnregisterEvent("PLAYER_ENTERING_WORLD"); -- So boat/zep zoning doesn't call this again
243 end
244  
245 ---------------------------------------------------------------------
246  
247 function FP_Enable()
248 FlightPath_Config.Enabled = true;
249 FP_EnableHooks();
250 FPCHAT("FlightPath hooks have been enabled.");
251 end
252  
253 ---------------------------------------------------------------------
254  
255 function FP_Disable()
256 FlightPath_Config.Enabled = false;
257 Recording = false; -- Just in case we are in flight
258 FP_DisableHooks();
259 FPCHAT("FlightPath hooks have been disabled.");
260 end
261  
262 ---------------------------------------------------------------------
263  
264 function FP_SetShowRemaining()
265 FlightPath_Config.HideRemaining = false;
266 FPCHAT("The in flight time remaining display has been enabled.");
267 end
268  
269 ---------------------------------------------------------------------
270  
271 function FP_SetHideRemaining()
272 FlightPath_Config.HideRemaining = true;
273 FPCHAT("The in flight time remaining display has been disabled.");
274 end
275  
276 ---------------------------------------------------------------------
277  
278 -- User typed /fp erase. Clear out the old database and start from scratch.
279 -- But first make sure they really really want to
280  
281 function FP_Erase()
282 StaticPopupDialogs["FP_CONFIRM_ERASE"] = {
283 text = "\nThis will erase ALL of your recorded flight paths.\n\nAre you sure?",
284 button1 = TEXT(ACCEPT),
285 button2 = TEXT(CANCEL),
286 OnAccept = function()
287 FP_EraseConfirmed();
288 end,
289 timeout = 0,
290 };
291 StaticPopup_Show("FP_CONFIRM_ERASE")
292 end
293  
294 function FP_EraseConfirmed()
295 FP_InitializeConfig();
296 FPCHAT("All FlightPath routes have been cleared.");
297 end
298  
299 ---------------------------------------------------------------------
300  
301 function FP_SetShowGrey()
302 if(FlightPath_Config.HideGrey ~= nil) then
303 for i in FlightPath_Config.HideGrey do
304 if(FlightPath_Config.HideGrey[i] == FP_GetNameServer()) then
305 table.remove(FlightPath_Config.HideGrey,i);
306 break;
307 end
308 end
309 end
310 FPCHAT("Greyed out flight paths will now be shown for "..UnitName("player"));
311 end
312  
313 ---------------------------------------------------------------------
314  
315 function FP_SetHideGrey()
316 if(FlightPath_Config.HideGrey == nil) then FlightPath_Config.HideGrey = {}; end;
317  
318 local found = false;
319 for i in FlightPath_Config.HideGrey do
320 if(FlightPath_Config.HideGrey[i] == FP_GetNameServer()) then
321 found = true;
322 break;
323 end
324 end
325 if(not found) then
326 table.insert(FlightPath_Config.HideGrey,FP_GetNameServer());
327 end
328 FPCHAT("Greyed out flight paths will now be hidden for "..UnitName("player"));
329 end
330  
331 ---------------------------------------------------------------------
332  
333 function FP_HideGrey()
334 if(FlightPath_Config.HideGrey == nil) then return false; end;
335  
336 for i in FlightPath_Config.HideGrey do
337 if(FlightPath_Config.HideGrey[i] == FP_GetNameServer()) then
338 return true;
339 end
340 end
341 return false;
342 end
343  
344 ---------------------------------------------------------------------
345  
346 function FP_EnableHooks()
347  
348 -- Hook the world map button so we know when map zones change via pulldown menu
349 if(WorldMapButton_OnUpdate ~= FP_WorldMapButton_OnUpdate) then
350 Original_WorldMapButton_OnUpdate = WorldMapButton_OnUpdate;
351 WorldMapButton_OnUpdate = FP_WorldMapButton_OnUpdate;
352 end
353  
354 -- Hook the TaxiMap tooltip function so we can add duration info
355 if(TaxiNodeOnButtonEnter ~= FP_TaxiNodeOnButtonEnter) then
356 Original_TaxiNodeOnButtonEnter = TaxiNodeOnButtonEnter
357 TaxiNodeOnButtonEnter = FP_TaxiNodeOnButtonEnter;
358 end
359  
360 -- Hook the TakeTaxiNode function so we know where we are going
361 if(TakeTaxiNode ~= FP_TakeTaxiNode) then
362 Original_TakeTaxiNode = TakeTaxiNode;
363 TakeTaxiNode = FP_TakeTaxiNode;
364 end
365  
366 end
367  
368 ---------------------------------------------------------------------
369  
370 function FP_DisableHooks()
371  
372 -- Unhook the world map button
373 if(Original_WorldMapButton_OnUpdate and (WorldMapButton_OnUpdate == FP_WorldMapButton_OnUpdate)) then
374 WorldMapButton_OnUpdate = Original_WorldMapButton_OnUpdate;
375 end
376  
377 -- Unhook the TaxiMap tooltip
378 if(Original_TaxiNodeOnButtonEnter and (TaxiNodeOnButtonEnter == FP_TaxiNodeOnButtonEnter)) then
379 TaxiNodeOnButtonEnter = Original_TaxiNodeOnButtonEnter;
380 end
381  
382 -- Unhook the TakeTaxiNode function
383 if(Original_TakeTaxiNode and (TakeTaxiNode == FP_TakeTaxiNode)) then
384 TakeTaxiNode = Original_TakeTaxiNode;
385 end
386  
387 end
388  
389 ---------------------------------------------------------------------
390  
391 function FP_DoVersionFixups()
392  
393 -- Check what version the user was running previously and do any needed upgrades
394  
395 if(FlightPath_Config.Version == nil) then return; end;
396  
397 local dbversion;
398 _,_,dbversion = string.find(FlightPath_Config.Version,"(%d*.%d+)"); -- Beta had format ".98 beta" so only grab the number part
399 dbversion = tonumber(dbversion);
400  
401 -- If this is a user that participated in the beta test, clear out the old (incompatible) database
402  
403 if(dbversion < 1.0) then
404 FlightPath_Config.FlightPaths = {};
405 FPCHAT("\nThanks for beta testing FlightPath!");
406 FPCHAT("All of your flight path information has been reset.");
407 FPCHAT("If you don't want to have to relearn all of your flight paths use the '/fp load' command.");
408  
409 -- Versions lower than 1.04 did not have the KnownBy field, and some had the deprecated Greyed flag.
410  
411 elseif(dbversion < 1.04) then
412 FPCHAT("Converting FlightPath data from version "..dbversion.." to version "..VERSION);
413 for i in FlightPath_Config.FlightPaths do
414  
415 local greyed = false;
416 local thisPathKnownBy = FP_GetNameServer();
417  
418 if(FlightPath_Config.FlightPaths[i].Greyed ~= nil) then
419 greyed = FlightPath_Config.FlightPaths[i].Greyed;
420 if(not greyed) then
421 -- This item was forced to be not greyed in prior versions. We accomplish
422 -- the same thing in 1.04 by setting the KnownBy to ALL
423 thisPathKnownBy = "ALL";
424 end
425 end
426 if(not greyed) then
427 if(not FP_UserKnowsPath(FlightPath_Config.FlightPaths[i])) then
428 if(FlightPath_Config.FlightPaths[i].KnownBy == nil) then
429 FlightPath_Config.FlightPaths[i].KnownBy = {}
430 end
431 table.insert(FlightPath_Config.FlightPaths[i].KnownBy,thisPathKnownBy);
432 end
433 end
434 FlightPath_Config.FlightPaths[i].Greyed = nil; -- Remove deprecated field
435 end
436  
437 -- Version 1.07 changed the duration from a single value to a value for each direction
438  
439 elseif(dbversion < 1.07) then
440  
441 FPCHAT("Converting FlightPath data from version "..dbversion.." to version "..VERSION);
442 for i in FlightPath_Config.FlightPaths do
443 if(FlightPath_Config.FlightPaths[i].Duration ~= nil) then
444 local duration = FlightPath_Config.FlightPaths[i].Duration;
445 FlightPath_Config.FlightPaths[i].Duration = { duration, duration }; -- start with both directions the same
446 end
447 end
448  
449 end
450  
451 FlightPath_Config.Version = VERSION;
452  
453 end
454  
455 ---------------------------------------------------------------------
456  
457 function FP_EventFrame_OnUpdate()
458  
459 -- This function is called frequently so be cpu and memory friendly
460  
461 if (FlightPath_Config.Enabled) then
462  
463 FP_CheckInFlightStatus();
464  
465 local ctime = time();
466 if(ctime ~= LastTime) then
467  
468 -- Do periodic processing (once per second or so)
469  
470 FP_ShowInFlightCountdown(ctime-LastTime);
471 LastTime = ctime;
472  
473 end
474  
475 end
476 end
477  
478 ---------------------------------------------------------------------
479  
480 function FP_CheckInFlightStatus()
481  
482 -- Check the flight recording state.
483  
484 if (Recording and not UnitOnTaxi("player")) then
485 -- End of the road, stop recording
486 FP_FlightPathEnd();
487 Recording = false;
488 StartRecording = false;
489 end
490  
491 if (StartRecording) then
492 if (UnitOnTaxi("player")) then
493 FPDEBUG("Beginning flight from "..TaxiOrigin.." to "..TaxiDestination);
494 StartRecording = false;
495 Recording = true;
496 end
497 end
498  
499 end
500  
501 ---------------------------------------------------------------------
502  
503 function FP_ShowInFlightCountdown(secondsElapsed)
504  
505 if(TimeRemaining > 0) then
506 TimeRemaining = TimeRemaining - secondsElapsed; -- Decrease in flight time remaining
507 end
508  
509 if((not FlightPath_Config.HideRemaining) and (TimeRemaining > 0)) then
510 if(UnitOnTaxi("player")) then
511 FP_CountdownTimerCity:SetText(TaxiDestination);
512 FP_CountdownTimerCity:Show();
513 local text = "Arriving in ";
514 text = text..FP_FormatTime(TimeRemaining);
515 FP_CountdownTimerRemaining:SetText(text);
516 FP_CountdownTimerRemaining:Show();
517 end
518 else
519 FP_CountdownTimerCity:Hide();
520 FP_CountdownTimerRemaining:Hide();
521 end
522  
523 end
524  
525 ---------------------------------------------------------------------
526  
527 function FP_GetNameServer()
528 local name, server;
529 name = UnitName("player");
530 server = GetCVar("realmName");
531 return name..","..server
532 end
533  
534 ---------------------------------------------------------------------
535  
536 function FP_TaxiMapOpened()
537  
538 if(FlightPath_Config.Enabled) then
539  
540 TaximapOpen = true;
541  
542 -- Loop through the displayed buttons on the taxi map and
543 -- add any routes that we haven't yet recorded
544  
545 local numButtons = NumTaxiNodes();
546  
547 -- Find out what flight masters like to call this location
548  
549 for i = 1, numButtons, 1 do
550 if(TaxiNodeGetType(i) == "CURRENT") then
551 TaxiOrigin = TaxiNodeName(i);
552 break;
553 end
554 end
555  
556 -- Now check for new flight paths between here and all the ones shown on the taxi map
557  
558 local x, y = FP_GetPlayerCoords();
559 local continent,zone = FP_GetRealContinentZone();
560 FPDEBUG("Setting continent,zone,coords for current flight master to:",continent,zone,x,y);
561  
562 for i = 1, numButtons, 1 do
563 if(TaxiNodeGetType(i) == "REACHABLE") then
564 local index, path;
565 index,path = FP_FindPath(TaxiOrigin, TaxiNodeName(i));
566 if(index == 0) then
567 path = {};
568 path.Endpoints = {TaxiOrigin,TaxiNodeName(i)};
569 path.Continent = {continent,0};
570 path.Zone = {zone,0};
571 path.Faction = UnitFactionGroup("player");
572 path.Coords = {x..","..y,NOCOORDS};
573 else
574 if(path.Endpoints[1] == TaxiOrigin) then
575 path.Coords[1] = x..","..y;
576 path.Continent[1] = continent;
577 path.Zone[1] = zone;
578 else
579 path.Coords[2] = x..","..y;
580 path.Continent[2] = continent;
581 path.Zone[2] = zone;
582 end
583 end
584 path.Cost = TaxiNodeCost(i);
585 FP_AddNewFlightPath(path,FP_LEARNED_FROM_FLIGHTMASTER);
586 end
587 end
588  
589 end
590 end
591  
592 ---------------------------------------------------------------------
593  
594 function FP_TaxiMapClosed()
595 if(FlightPath_Config.Enabled) then
596 TaximapOpen = false;
597 end
598 end
599  
600 ---------------------------------------------------------------------
601  
602 function FP_TakeTaxiNode(nodeID)
603  
604 TaxiDestination = TaxiNodeName(nodeID);
605 StartTime = time();
606 TimeRemaining = 0;
607 local index;
608  
609 index,CurrentFlight = FP_FindPath(TaxiOrigin, TaxiDestination);
610  
611 if(index == 0) then
612 FPDEBUG("Internal error! FlightPath could not locate the flight path from "..TaxiOrigin.." to "..TaxiDestination);
613 else
614  
615 -- Durations are stored associated with the from endpoint. TaxiDirection tells whether we are
616 -- traveling from the first endpoint or the second endpoint of the connection pair.
617  
618 if(TaxiOrigin == CurrentFlight.Endpoints[1]) then TaxiDirection = 1; else TaxiDirection = 2; end
619  
620 if(CurrentFlight.Duration == nil) then
621 CurrentFlight.Duration = {"",""};
622 else
623 local _,_,minutes,seconds = string.find(CurrentFlight.Duration[TaxiDirection],"(%d+):(%d+)");
624 if(minutes and seconds) then
625 TimeRemaining = (minutes*60) + seconds;
626 end
627 end
628 end
629  
630 StartRecording =true; -- Tell the OnUpdate handler to start checking flight status
631  
632 Original_TakeTaxiNode(nodeID); -- Call the original handler now
633 end
634  
635 ---------------------------------------------------------------------
636  
637 function FP_FlightPathEnd()
638  
639 -- We've landed. Record the duration of the flight and the coordinates of the
640 -- flightmaster here if not already known.
641  
642 local x, y = FP_GetPlayerCoords();
643 local destIndex = 2;
644 if(TaxiDirection == 2) then destIndex = 1; end;
645  
646 TimeRemaining = 0;
647 CurrentFlight.Duration[TaxiDirection] = FP_Elapsed(StartTime,time());
648 CurrentFlight.Coords[destIndex] = x..","..y;
649 CurrentFlight.Continent[destIndex],CurrentFlight.Zone[destIndex] = FP_GetRealContinentZone();
650  
651 FP_AddNewFlightPath(CurrentFlight,FP_LEARNED_FROM_FLIGHTMASTER);
652 end
653  
654 ---------------------------------------------------------------------
655  
656 function FP_AddNewFlightPath(new,source)
657  
658 -- We have a potential new flight path. If we already know the path then update any
659 -- relevant information, otherwise add the path to our data base.
660  
661 local index,foundPath = FP_FindPath(new.Endpoints[1],new.Endpoints[2]);
662 local isNewPath = (foundPath == nil);
663  
664 -- Update the coords, continent, and zone of any connection point that matches one in the new path.
665 -- (The need for this will go away once I properly normalize the data tables)
666  
667 if(source == FP_LEARNED_FROM_FLIGHTMASTER) then
668 for i in FlightPath_Config.FlightPaths do
669 for j = 1, 2, 1 do
670 for k = 1, 2, 1 do
671 if (new.Endpoints[j] == FlightPath_Config.FlightPaths[i].Endpoints[k]) then
672 if(new.Coords[j] ~= NOCOORDS) then
673 if(new.Coords[j] ~= FlightPath_Config.FlightPaths[i].Coords[k]) then
674 FPDEBUG("Replacing coords on flightpath "..i.." ("..FlightPath_Config.FlightPaths[i].Endpoints[k]..")\nFrom "..FlightPath_Config.FlightPaths[i].Coords[k].." to "..new.Coords[j]);
675 FlightPath_Config.FlightPaths[i].Coords[k] = new.Coords[j];
676 end
677 end
678 if(new.Continent[j] ~= 0) then FlightPath_Config.FlightPaths[i].Continent[k] = new.Continent[j]; end
679 if(new.Zone[j] ~= 0) then FlightPath_Config.FlightPaths[i].Zone[k] = new.Zone[j]; end
680 end
681 end
682 end
683 end
684 end
685  
686 -- Either add it to our flight path database or update the cost and duration
687  
688 if(isNewPath) then
689 table.insert(FlightPath_Config.FlightPaths,new);
690 index = table.getn(FlightPath_Config.FlightPaths);
691 if (source == FP_LEARNED_FROM_FLIGHTMASTER) then
692 FPCHAT("FlightPath learned a new route between "..new.Endpoints[1].." and "..new.Endpoints[2]);
693 end
694 else
695  
696 -- I'm going to go on the assumption that information coming in from a flightmaster is more current
697 -- than the potentially stale preloaded flight path information. If the user types a /fp load command
698 -- after having learned their own flight paths I want to ensure that the preloaded data does not overwrite
699 -- the learned data. For this reason costs and times are only updated in the database when the path is
700 -- one we obtained from a flight master. (On an initial /fp load the paths will be unknown so the the
701 -- times and costs from the preloaded paths will be the initial values).
702  
703 if(source == FP_LEARNED_FROM_FLIGHTMASTER) then
704  
705 -- Cost
706 if(new.Cost) then FlightPath_Config.FlightPaths[index].Cost = new.Cost; end;
707  
708 -- Duration
709 if(new.Duration and (table.getn(new.Duration) >= 1)) then
710  
711 if(FlightPath_Config.FlightPaths[index].Duration == nil) then FlightPath_Config.FlightPaths[index].Duration = {"",""}; end
712  
713 if(new.Endpoints[1] == FlightPath_Config.FlightPaths[index].Endpoints[1]) then
714 FlightPath_Config.FlightPaths[index].Duration[1] = new.Duration[1];
715 if(FlightPath_Config.FlightPaths[index].Duration[2] == "") then FlightPath_Config.FlightPaths[index].Duration[2] = new.Duration[2]; end
716 else
717 FlightPath_Config.FlightPaths[index].Duration[2] = new.Duration[1];
718 if(FlightPath_Config.FlightPaths[index].Duration[1] == "") then FlightPath_Config.FlightPaths[index].Duration[1] = new.Duration[2]; end
719 end
720  
721 -- If one direction still has no duration, assume it's the same as the other leg
722  
723 if(FlightPath_Config.FlightPaths[index].Duration[1] == "") then
724 FlightPath_Config.FlightPaths[index].Duration[1] = FlightPath_Config.FlightPaths[index].Duration[2];
725 end
726 if(FlightPath_Config.FlightPaths[index].Duration[2] == "") then
727 FlightPath_Config.FlightPaths[index].Duration[2] = FlightPath_Config.FlightPaths[index].Duration[1];
728 end
729 end;
730  
731 end
732 end
733  
734 -- Record the fact that the user knows this flight so it no longer displays as grey or shows up if they have hidegrey set
735  
736 if(source == FP_LEARNED_FROM_FLIGHTMASTER) then
737 if(not FP_UserKnowsPath(FlightPath_Config.FlightPaths[index])) then
738 if(FlightPath_Config.FlightPaths[index].KnownBy == nil) then FlightPath_Config.FlightPaths[index].KnownBy = {}; end
739 table.insert(FlightPath_Config.FlightPaths[index].KnownBy,FP_GetNameServer());
740 end
741 end
742  
743 end
744  
745 ---------------------------------------------------------------------
746  
747 function FP_Elapsed(start,finish)
748 return FP_FormatTime(finish-start);
749 end
750  
751 ---------------------------------------------------------------------
752  
753 function FP_FormatTime(duration)
754 local minutes = floor(duration / 60);
755 local seconds = duration - (minutes * 60);
756 local tens = floor(seconds/10);
757 local single = seconds - (tens * 10);
758 return minutes..":"..tens..single;
759 end
760  
761 ---------------------------------------------------------------------
762  
763 function FP_GetPlayerCoords()
764  
765 FP_ForceMapToCurrentZone(); -- voodoo here to try and address WoW bug
766  
767 local px, py = GetPlayerMapPosition("player");
768  
769 -- normalize coords to ##.## format
770  
771 px = floor(px * 10000);
772 py = floor(py * 10000);
773 px = px / 100;
774 py = py / 100;
775 return px,py
776 end
777  
778 ---------------------------------------------------------------------
779  
780 function FP_ForceMapToCurrentZone()
781 if(GetCurrentMapZone() ~= 0) then
782 SetMapToCurrentZone(); -- Normally this is what will happen
783 else
784 local continent,zone = FP_GetRealContinentZone();
785 SetMapZoom(continent,zone);
786 end
787 end
788  
789 ---------------------------------------------------------------------
790  
791 function FP_GetRealContinentZone()
792  
793 if(GetCurrentMapZone() ~= 0) then
794 return GetCurrentMapContinent(),GetCurrentMapZone();
795 end
796  
797 -- We're bugged. Fortunately GetRealZoneText() is accurate so we can use that
798 -- to figure out our zone. (Remember, continent and zone numbers equate to their
799 -- index number in the world map pulldowns). This idea was first implemented by
800 -- Legorol in his ZoneFix mod.
801  
802 local continent,zone,name
803 local zoneText = GetRealZoneText();
804 for continent in ipairs{GetMapContinents()} do
805 for zone,name in ipairs{GetMapZones(continent)} do
806 if(name == zoneText) then
807 FPDEBUG("SetMapToCurrentZone bug detected!! You should zone or logout before talking to any flight masters. Returning fixed up continent,zone:",continent,zone);
808 return continent,zone;
809 end
810 end
811 end
812 return 0,0; -- Should never happen
813 end
814  
815 ---------------------------------------------------------------------
816  
817 function FP_GetLocale()
818  
819 local zone = GetRealZoneText();
820 local city = GetMinimapZoneText();
821  
822 if (zone == city) then
823 return zone;
824 else
825 return city..", "..zone;
826 end
827  
828 end
829  
830 ---------------------------------------------------------------------
831  
832 function FP_Load(option)
833  
834 if(option == nil or option == "") then
835 FPCHAT("Usage: "..WHITE.."/fp load name"..STATUS_COLOR.." where name is one of 'misc', 'horde', or 'alliance'.");
836 return;
837 end
838  
839 local toLoad = getglobal("FP_"..option.."Paths");
840 if(toLoad == nil) then
841 FPCHAT("Could not find a list called "..option.."Paths in the KnownPaths.lua file.\nType '/fp ?' for help.");
842 return;
843 end
844  
845 for i in toLoad do
846 FP_AddNewFlightPath(toLoad[i],FP_LEARNED_FROM_PRELOAD);
847 end
848  
849 FPCHAT("Scanned "..table.getn(toLoad).." "..option.." paths for new connections.");
850  
851 end
852  
853 ---------------------------------------------------------------------
854  
855 function FP_IsSameLocation(location1, location2)
856  
857 -- So why are we going through all these gyrations to see if one location is
858 -- the same as another? Because WoW uses different location names in the taxi
859 -- map than they do for the city and zone names proper. For example. the
860 -- flight master in Orgrimmar is located in the "Valley of Strength, Orgrimmar"
861 -- if you use GetZoneText() or other API calls. However the name of the
862 -- taxi node for that flight master is "Orgrimmar, Durotar". There are many
863 -- other locations in Azeroth that have similar discrepancies. (Ironforge,
864 -- Moonglade, ...)
865 --
866 -- In order to bring up the correct dialog page for the location the player
867 -- is currently, we need to correlate these two different versions of a place name.
868  
869 -- See if they just match
870  
871 if(location1 == location2) then return true; end
872  
873 local c1, z1, c2, z2;
874 c1,z1 = FP_ParseLocation(location1);
875 c2,z2 = FP_ParseLocation(location2);
876  
877 -- See if city names match (since city names are unique)
878  
879 if((c1 ~= "") and (c1 == c2)) then return true; end
880  
881 -- See if the city and zones are swapped (e.g. Orgrimmar)
882  
883 if((c1 ~= "") and (c1 == z2)) then return true; end
884 if((c2 ~= "") and (c2 == z1)) then return true; end
885  
886 -- See if parts of city match (e.g. "The Crossroads", "Crossroads")
887  
888 local b, e, match;
889 if(c1 ~= "" and c2 ~= "") then
890 b,e,match = string.find(c1,c2,1,true);
891 if(b) then return true; end;
892  
893 b,e,match = string.find(c2,c1,1,true);
894 if(b) then return true; end;
895 end
896  
897 -- See if parts of zone match city (e.g. "Stormwind,Elwynn", "Trade District,Stormwind City")
898  
899 if(c1 ~= "" and z2 ~= "") then
900 b,e,match = string.find(c1,z2,1,true);
901 if(b) then return true; end;
902 b,e,match = string.find(z2,c1,1,true);
903 if(b) then return true; end;
904 end
905 if(c2 ~= "" and z1 ~= "") then
906 b,e,match = string.find(c2,z1,1,true);
907 if(b) then return true; end;
908 b,e,match = string.find(z1,c2,1,true);
909 if(b) then return true; end;
910 end
911  
912 -- Give up and assume the locations are not the same
913  
914 return false;
915 end
916  
917 ---------------------------------------------------------------------
918  
919 function FP_FindCoords(location)
920  
921 -- Loop through all known flight paths and see if we can find the
922 -- coordinate position of the specified flight master.
923 -- This will be uneccessary when I properly normalize the data tables
924  
925 local coords = NOCOORDS;
926  
927 for i in FlightPath_Config.FlightPaths do
928  
929 if(FP_IsSameLocation(location,FlightPath_Config.FlightPaths[i].Endpoints[1])) then
930 if(FlightPath_Config.FlightPaths[i].Coords[1] ~= NOCOORDS) then
931 coords = FlightPath_Config.FlightPaths[i].Coords[1];
932 break;
933 end
934 end
935  
936 if(FP_IsSameLocation(location,FlightPath_Config.FlightPaths[i].Endpoints[2])) then
937 if(FlightPath_Config.FlightPaths[i].Coords[2] ~= NOCOORDS) then
938 coords = FlightPath_Config.FlightPaths[i].Coords[2];
939 break;
940 end
941 end
942  
943 end
944  
945 return coords;
946 end
947  
948 ---------------------------------------------------------------------
949  
950 function FP_GetRecordedFlightpaths()
951  
952 -- Return a list of known locations for display in the dialog drop down list
953  
954 local flightpaths = {}; -- reverse zone and city so list is sorted by zone
955 local lookup = {}
956  
957 for i in FlightPath_Config.FlightPaths do
958 if (FlightPath_Config.FlightPaths[i].Faction == UnitFactionGroup("player")) then
959 if(FP_UserKnowsPath(FlightPath_Config.FlightPaths[i]) or (not FP_HideGrey())) then
960 for j = 1, 2, 1 do
961 local tcity, tzone = FP_ParseLocation(FlightPath_Config.FlightPaths[i].Endpoints[j]);
962 local zoneFirst = tzone;
963 if(tcity ~= "") then zoneFirst = zoneFirst.." - "..tcity; end
964 if (lookup[zoneFirst] == nil) then
965 lookup[zoneFirst] = true;
966 table.insert(flightpaths,zoneFirst);
967 end
968 end
969 end
970 end
971 end
972  
973 table.sort(flightpaths);
974 return flightpaths;
975 end
976  
977 ---------------------------------------------------------------------
978  
979 function FP_ParseCoord(coord)
980 local _, _, x, y = string.find(coord,"(%d*.%d*),(%d*.%d*)");
981 return tonumber(x), tonumber(y);
982 end
983  
984 ---------------------------------------------------------------------
985  
986 function FP_ParseLocation(locale)
987  
988 -- locale can be:
989 -- zone we are in an unnamed area of a zone
990 -- city, zone normal parse
991 -- zone - city dash means swap zone city order (is dropdown entry)
992  
993 if(not locale) then return "",""; end
994  
995 local b, e, city, zone, dash;
996  
997 b,e,dash = string.find(locale," - ", 1, true); -- it's in dropdown format if it has a dash
998 if(b) then
999 zone = FP_Trim(string.sub(locale,1,b-1));
1000 city = FP_Trim(string.sub(locale,e+1,string.len(locale)));
1001 return city,zone;
1002 end
1003  
1004 b,e,city = string.find(locale, "([%P%'%` ]+),"); -- This should be good for foreign character sets as well
1005 if(not b) then
1006 city = "";
1007 zone = FP_Trim(locale);
1008 else
1009 b,e,zone = string.find(locale, "[%P%'%` ]+,%s*([%P%'%` ]+)");
1010 if(not b) then
1011 -- For degenerate cases like Moonglade that don't return a zone use the city name as the zone
1012 zone = FP_Trim(city);
1013 end;
1014 end
1015  
1016 return city,zone;
1017 end
1018  
1019 ---------------------------------------------------------------------
1020  
1021 function FP_Trim(text)
1022 -- Remove trailing spaces from a string
1023 if(text == nil) then return; end;
1024 local _,_,trimmed = string.find(text,"(.-)%s*$");
1025 return trimmed;
1026 end
1027  
1028 ---------------------------------------------------------------------
1029 --
1030 -- User Interface logic
1031 --
1032 ---------------------------------------------------------------------
1033  
1034 function FP_FlightPathDialog()
1035  
1036 if (FP_UIFrame:IsVisible() ) then
1037 FP_UIFrame:Hide(); -- So bound key toggles dialog on/off
1038 else
1039 DropDownStartIndex=1;
1040 FP_DisplayDialogConnections();
1041 FP_UIFrame:Show();
1042 end
1043  
1044 end
1045  
1046 ---------------------------------------------------------------------
1047  
1048 function FP_WorldMapUpdate()
1049  
1050 -- Changing zones overwrites DropDownList1 which the dialog uses.
1051 -- If we change zones with the dialog open, repopulate the list
1052  
1053 if (FP_UIFrame:IsVisible() ) then
1054 FP_UIZoneDropDown_Initialize();
1055 end
1056  
1057 end
1058  
1059 ---------------------------------------------------------------------
1060  
1061 function FP_UIZoneDropDown_Initialize()
1062  
1063 -- Called from the frames OnShow
1064  
1065 FP_UIZoneDropDownClear();
1066  
1067 local index=DropDownStartIndex;
1068 local info = {};
1069 local buttonCount = 0;
1070  
1071 local flightpaths = FP_GetRecordedFlightpaths();
1072 local knownCount = table.getn(flightpaths);
1073  
1074 for i = 1, MAXDROPDOWN, 1 do
1075  
1076 local buttonText = "";
1077  
1078 if((i == 1) and (DropDownStartIndex > 1)) then
1079 buttonText = MOREUP;
1080  
1081 elseif((i == MAXDROPDOWN) and (index < (knownCount-1))) then
1082 buttonText = MOREDOWN;
1083  
1084 else
1085 if(index > knownCount) then break;end;
1086 buttonText = flightpaths[index];
1087 index = index + 1;
1088 end
1089  
1090 info={};
1091 info.text = buttonText;
1092 info.func = FP_UIZoneDropDown_OnClick;
1093 info.keepShownOnClick = true;
1094 info.notCheckable = true; -- WoW is bugged and will still show check but at least save width
1095 UIDropDownMenu_AddButton(info,1);
1096 end
1097  
1098 DropDownList1:ClearAllPoints();
1099 DropDownList1:SetPoint("CENTER","FP_UIFrame","CENTER",0,0);
1100  
1101 end
1102  
1103 ---------------------------------------------------------------------
1104  
1105 function FP_UIZoneDropDownClear()
1106 local text = UIDropDownMenu_GetText(FP_UIZoneDropDown);
1107 UIDropDownMenu_ClearAll(FP_UIZoneDropDown);
1108 UIDropDownMenu_SetText(text,FP_UIZoneDropDown);
1109 DropDownList1.numButtons = 0;
1110 end
1111  
1112 ---------------------------------------------------------------------
1113  
1114 function FP_UIZoneDropDown_OnClick()
1115  
1116 if(this:GetText() == MOREDOWN) then
1117 FPDEBUG("Down arrow detected");
1118 local flightpaths = FP_GetRecordedFlightpaths();
1119 local knownCount = table.getn(flightpaths);
1120 DropDownStartIndex = knownCount - MAXDROPDOWN+1;
1121 FP_UIZoneDropDown_Initialize();
1122 return;
1123  
1124 elseif(this:GetText() == MOREUP) then
1125 FPDEBUG("Up arrow detected");
1126 DropDownStartIndex = 1;
1127 FP_UIZoneDropDown_Initialize();
1128 return;
1129  
1130 else
1131 FP_DisplayDialogConnections(this:GetText());
1132 DropDownList1:Hide();
1133 end
1134  
1135 end
1136  
1137 ---------------------------------------------------------------------
1138  
1139 function FP_FormatMoney(money)
1140  
1141 if(money == nil) then return ""; end;
1142  
1143 amount = tonumber(money);
1144 if(amount == nil) then
1145 FPDEBUG("Failed to convert money string to number: ",money);
1146 return ""
1147 end
1148  
1149 local gold, silver, copper, text;
1150  
1151 gold = floor(amount / (100*100));
1152 silver = mod(floor(amount / 100), 100);
1153 copper = mod(floor(amount + .5), 100);
1154  
1155 text = "";
1156 if(gold > 0) then text = gold.."g"; end
1157 if(silver > 0) then
1158 if(text ~= "") then text = text.." "; end;
1159 text = text..silver.."s";
1160 end
1161 if(copper > 0) then
1162 if(text ~= "") then text = text.." "; end;
1163 text = text..copper.."c";
1164 end
1165 return text;
1166 end
1167 ---------------------------------------------------------------------
1168  
1169 function FP_DisplayDialogConnections(location)
1170  
1171 -- Display the appropriate connections text on the FlightPath dialog box
1172  
1173 local text;
1174 local routeFound = false;
1175 local databaseIsEmpty = true;
1176 local tlocation, b, e, i, j, duration;
1177  
1178 -- Used to make a second attempt based on zone number if we fail to populate based on location text
1179  
1180 PopulatePass = PopulatePass+1;
1181 local _,currentMapZone = FP_GetRealContinentZone();
1182 local repopulateLoc = "";
1183  
1184 if(not location) then
1185 location = FP_GetLocale(); -- On initial call determine where we are
1186 end
1187 tlocation = location;
1188  
1189 Connections = {}; -- clear shared table
1190 local connectionCost = {};
1191 local connectionColor = {};
1192  
1193 for i in FlightPath_Config.FlightPaths do
1194 if (FlightPath_Config.FlightPaths[i].Faction == UnitFactionGroup("player")) then
1195 for j = 1, 2, 1 do
1196  
1197 if(FlightPath_Config.FlightPaths[i].Zone[j] == currentMapZone) then
1198 repopulateLoc = FlightPath_Config.FlightPaths[i].Endpoints[j]; -- In case we need to make 2nd pass cause 1st came up empty
1199 end
1200  
1201 if(FP_IsSameLocation(location,FlightPath_Config.FlightPaths[i].Endpoints[j])) then
1202 tlocation = FlightPath_Config.FlightPaths[i].Endpoints[j];
1203 local which = 1;
1204 if(j == 1) then which = 2; end;
1205  
1206 -- If this is a preloaded flight can't yet fly, grey it out
1207  
1208 local color = CONNECTION_COLOR;
1209 local mcolor = MONEY_COLOR;
1210 if(not FP_UserKnowsPath(FlightPath_Config.FlightPaths[i])) then
1211 color = GREY;
1212 mcolor = GREY;
1213 end
1214  
1215 -- Only show greyed paths if user hasn't disabled displaying them
1216  
1217 if((color ~= GREY) or (not FP_HideGrey())) then
1218  
1219 connectionColor[FlightPath_Config.FlightPaths[i].Endpoints[which]] = color;
1220  
1221 -- Add this endpoint to our available connections
1222  
1223 table.insert(Connections,FlightPath_Config.FlightPaths[i].Endpoints[which]);
1224  
1225 -- If we know the cost, display it
1226  
1227 text = ""
1228 if(FlightPath_Config.FlightPaths[i].Cost) then
1229 text = mcolor..FP_FormatMoney(FlightPath_Config.FlightPaths[i].Cost);
1230 end
1231  
1232 -- If we know the duration, display it
1233  
1234 duration = FP_GetDuration(FlightPath_Config.FlightPaths[i], FlightPath_Config.FlightPaths[i].Endpoints[j]);
1235 if(duration == nil or duration == "") then duration = " "; end; -- Because column is right justified
1236 text = text..color.." "..duration;
1237 connectionCost[FlightPath_Config.FlightPaths[i].Endpoints[which]] = text;
1238  
1239 routeFound = true;
1240 end
1241 end
1242 databaseIsEmpty = false;
1243 end
1244 end
1245 end
1246 table.sort(Connections);
1247  
1248 FP_ClearDialogScreen();
1249  
1250 if (routeFound) then
1251  
1252 local text;
1253  
1254 FP_UIStaticConnections:SetText(WHITE.."Connections");
1255 FP_UIStaticZone:SetText(WHITE.."Zone");
1256 for i in Connections do
1257 if(i > MAX_CONNECTIONS) then
1258 FPCHAT("Maximum number of connections ("..MAX_CONNECTIONS..") exceeded for zone "..zone..".");
1259 break;
1260 end
1261 text = getglobal("FP_Connection"..i);
1262 text:SetText(connectionColor[Connections[i]]..Connections[i]);
1263 text = getglobal("FP_ConnectionCost"..i);
1264 text:SetText(connectionCost[Connections[i]]);
1265 end
1266  
1267 local tcity, tzone = FP_ParseLocation(tlocation);
1268 tlocation = tzone;
1269 if(tcity ~= "") then
1270 tlocation = tlocation.." - "..tcity;
1271 end
1272 UIDropDownMenu_SetText(tlocation,FP_UIZoneDropDown);
1273 FP_UIErrorText:SetText("");
1274 PopulatePass = 0;
1275 else
1276 UIDropDownMenu_SetText("",FP_UIZoneDropDown);
1277 if (databaseIsEmpty) then
1278 -- If we don't know any flight paths at all display a reassuring message to the user
1279 text = WHITE..
1280 "FlightPath has not yet learned any flight paths for the "..UnitFactionGroup("player")..".\n\n"..
1281 "As you travel around Azeroth, FlightPath will learn the flight paths available to you.\n\n"..
1282 "Soon it will know connections for each flight master, how much each flight costs, and how long it takes to get from one point to another.\n\n"..
1283 "If you would rather not wait and would like to load any of the optional flight paths available, use the "..STATUS_COLOR.."/fp load"..WHITE.." command.";
1284 FP_UIErrorText:SetText(text);
1285 return;
1286 else
1287 if((PopulatePass == 1) and (repopulateLoc ~= "")) then
1288 -- Didn't match anything first time through
1289 FP_DisplayDialogConnections(repopulateLoc);
1290 PopulatePass = 0;
1291 end
1292 end
1293 end
1294  
1295 end
1296  
1297 ---------------------------------------------------------------------
1298  
1299 function FP_ClearDialogScreen()
1300  
1301 local button;
1302  
1303 FP_UIErrorText:SetText("");
1304 FP_UIStaticConnections:SetText("");
1305  
1306 for i = 1, MAX_CONNECTIONS, 1 do
1307  
1308 button = getglobal("FP_Connection"..i);
1309 button:SetText("");
1310  
1311 button = getglobal("FP_ConnectionCost"..i);
1312 button:SetText("");
1313  
1314 end
1315  
1316 FP_DialogHeading:SetText(BRIGHTGREY..BINDING_HEADER_FPHEADER);
1317 FP_DialogVersion:SetText(GREY.."Version "..VERSION);
1318 end
1319  
1320 ---------------------------------------------------------------------
1321  
1322 function FP_UIFrame_OnShow()
1323 -- UIDropDownMenu_Initialize(FP_UIZoneDropDown, FP_UIZoneDropDown_Initialize);
1324 end
1325  
1326 ---------------------------------------------------------------------
1327  
1328 function FP_Clicked(leftOrRight, buttonID)
1329  
1330 -- Process left mouse button click
1331  
1332 local index = 0;
1333 local tzone, tcity;
1334  
1335 if (tonumber(buttonID) >= CONNECTION_START_ID) then
1336 index = buttonID - CONNECTION_START_ID + 1;
1337 if(index <= table.getn(Connections)) then
1338  
1339 if ( leftOrRight == "LeftButton" ) then
1340  
1341 FP_DisplayDialogConnections(Connections[index]);
1342  
1343 else
1344 local continent, zone = GetMapNumbers(Connections[index]);
1345 if((continent ~= 0) and (zone ~= 0)) then
1346 -- Display appropriate map on right click
1347 ToggleWorldMap();
1348 SetMapZoom(continent,zone);
1349 end
1350 end
1351 end
1352  
1353 else
1354 FPCHAT("FlightPath received a click from unknown button "..buttonID.."");
1355 end
1356  
1357 return;
1358 end
1359  
1360 ---------------------------------------------------------------------
1361  
1362 function FP_UserKnowsPath(path)
1363  
1364 if(path.KnownBy == nil) then return false; end;
1365  
1366 for i in path.KnownBy do
1367 if(path.KnownBy[i] == "ALL") then return true; end;
1368 if(path.KnownBy[i] == FP_GetNameServer()) then return true; end;
1369 end
1370  
1371 return false;
1372 end
1373  
1374 ---------------------------------------------------------------------
1375  
1376 function GetMapNumbers(location)
1377 local continent, zone;
1378 for i in FlightPath_Config.FlightPaths do
1379 for j = 1, 2, 1 do
1380 if( FP_IsSameLocation(location,FlightPath_Config.FlightPaths[i].Endpoints[j])) then
1381 continent = FlightPath_Config.FlightPaths[i].Continent[j];
1382 zone = FlightPath_Config.FlightPaths[i].Zone[j];
1383 if(continent ~= 0 and zone ~= 0) then
1384 return continent, zone;
1385 end
1386 end
1387 end
1388 end
1389 return 0,0;
1390 end
1391  
1392 ---------------------------------------------------------------------
1393  
1394 function FP_Status()
1395  
1396 if (FlightPath_Config.Enabled) then
1397 FPCHAT(BINDING_HEADER_FPHEADER.." version "..VERSION.." is enabled.");
1398 else
1399 FPCHAT(BINDING_HEADER_FPHEADER.." version "..VERSION.." is disabled.");
1400 end
1401  
1402 FP_ReportBindingKey();
1403  
1404 if(FlightPath_Config.HideRemaining) then FPCHAT("The in flight time remaining counter is disabled."); end;
1405  
1406 local x, y = FP_GetPlayerCoords();
1407 FPCHAT("You are now at location "..x..","..y.." in "..FP_GetLocale());
1408 if (UnitOnTaxi("player")) then
1409 FPCHAT("You are currently flying from "..TaxiOrigin.." to "..TaxiDestination);
1410 end
1411  
1412 local flightpaths = FP_GetRecordedFlightpaths();
1413 if (flightpaths) then
1414 faction = UnitFactionGroup("player")
1415 local factionRoutes = 0
1416 for i in FlightPath_Config.FlightPaths do
1417 if (FlightPath_Config.FlightPaths[i].Faction == faction) then
1418 factionRoutes = factionRoutes + 1;
1419 end
1420 end
1421 FPCHAT("FlightPath knows of "..factionRoutes.." "..faction.." routes between "..table.getn(flightpaths).." locations.");
1422 end
1423 end
1424  
1425 ---------------------------------------------------------------------
1426  
1427 function FP_ReportBindingKey()
1428 if(GetBindingKey("FPDIALOG")) then
1429 FPCHAT("The FlightPath dialog is bound to key "..GetBindingKey("FPDIALOG"));
1430 end
1431 end
1432  
1433 ---------------------------------------------------------------------
1434 --
1435 -- World Map logic
1436 --
1437 ---------------------------------------------------------------------
1438  
1439 function FP_Round(value)
1440 return math.floor(value*10000.0)/10000.0;
1441 end
1442  
1443 ---------------------------------------------------------------------
1444  
1445 function FP_GetPOITooltipText(location)
1446  
1447 local leftSide = {};
1448 local rightSide = {}
1449 local text;
1450 local routeFound = false;
1451 local city, zone, tcity, tzone, i;
1452  
1453 if(not location) then
1454 city = "";
1455 zone = WorldMapZoneDropDownText:GetText();
1456 else
1457 city, zone = FP_ParseLocation(location);
1458 end
1459  
1460 leftSide[1] = location; rightSide[1] = " ";
1461 leftSide[2] = " "; rightSide[3] = " ";
1462 leftSide[3] = WHITE.."Connections:";rightSide[4] = " ";
1463 leftSide[4] = " "; rightSide[5] = " ";
1464 local lineNumber = 5;
1465  
1466 local poiConnections = {};
1467 local poiCosts = {}
1468 local unique = {};
1469 local connectionColors = {};
1470 local connection,cost,duration, pline;
1471  
1472 for i in FlightPath_Config.FlightPaths do
1473 if (FlightPath_Config.FlightPaths[i].Faction == UnitFactionGroup("player")) then
1474 local color = STATUS_COLOR;
1475 if(not FP_UserKnowsPath(FlightPath_Config.FlightPaths[i])) then color = GREY; end
1476  
1477 if((color ~= GREY) or (not FP_HideGrey())) then
1478 tcity, tzone = FP_ParseLocation(FlightPath_Config.FlightPaths[i].Endpoints[1]);
1479 if ((((city == "") or (city == tcity)) and (zone == tzone))) then
1480 if(unique[FlightPath_Config.FlightPaths[i].Endpoints[2]] == nil) then
1481 unique[FlightPath_Config.FlightPaths[i].Endpoints[2]] = true;
1482 connection, cost = FP_BuildPOITipLine(FlightPath_Config.FlightPaths[i],2,location, color == GREY)
1483 table.insert(poiConnections,connection);
1484 poiCosts[connection] = cost;
1485 connectionColors[connection] = color;
1486 routeFound = true;
1487 end
1488 end
1489 tcity, tzone = FP_ParseLocation(FlightPath_Config.FlightPaths[i].Endpoints[2]);
1490 if ((((city == "") or (city == tcity)) and (zone == tzone))) then
1491 if(unique[FlightPath_Config.FlightPaths[i].Endpoints[1]] == nil) then
1492 unique[FlightPath_Config.FlightPaths[i].Endpoints[1]] = true;
1493 connection, cost = FP_BuildPOITipLine(FlightPath_Config.FlightPaths[i],1,location, color == GREY)
1494 table.insert(poiConnections,connection);
1495 poiCosts[connection] = cost;
1496 connectionColors[connection] = color;
1497 routeFound = true;
1498 end
1499 end
1500 end
1501 end
1502 end
1503 table.sort(poiConnections);
1504  
1505 if (routeFound) then
1506 for i in poiConnections do
1507 leftSide[lineNumber] = connectionColors[poiConnections[i]]..poiConnections[i];
1508 rightSide[lineNumber] = poiCosts[poiConnections[i]];
1509 lineNumber = lineNumber + 1;
1510 end
1511 else
1512 leftSide[lineNumber] = STATUS_COLOR.."No connections found.";
1513 rightSide[lineNumber] = " ";
1514 end
1515  
1516 return leftSide,rightSide;
1517 end
1518  
1519 ---------------------------------------------------------------------
1520  
1521 function FP_BuildPOITipLine(path,index,location,isGrey)
1522 local text = "";
1523 local color;
1524 local duration = FP_GetDuration(path,location);
1525 if(path.Cost ~= nil and path.Cost ~= "") then
1526 color = MONEY_COLOR;
1527 if(isGrey) then color = GREY; end;
1528 text = color..FP_FormatMoney(path.Cost);
1529 end
1530  
1531 if(duration ~= nil and duration ~="") then
1532 color = WHITE;
1533 if(isGrey) then color = GREY; end;
1534 text = text.." "..color..duration;
1535 else
1536 text = text.." ";
1537 end
1538 return path.Endpoints[index],text;
1539 end
1540  
1541 ---------------------------------------------------------------------
1542  
1543 function FP_WorldMapButton_OnUpdate(elapsed)
1544  
1545 Original_WorldMapButton_OnUpdate(elapsed); -- Call the original owner of the hook so we don't break other mods
1546  
1547 if(FlightPath_Config.Enabled) then
1548  
1549 local path;
1550 local buttonCount = 0;
1551  
1552 -- Hide all the POI buttons
1553  
1554 for i = 1, MAX_POI_BUTTONS, 1 do
1555 POI = getglobal("FP_POI"..i);
1556 if(POI) then
1557 POI:ClearAllPoints();
1558 POI.Location = nil;
1559 POI:Hide();
1560 end
1561 end
1562  
1563 -- What map are we looking at?
1564  
1565 -- Returns 0=world, 1=kalimdor or a zone in kalimdor, 2=ek or a zone in ek
1566 local continent = GetCurrentMapContinent();
1567 if(continent ==0) then return; end; -- We don't support the world map yet
1568  
1569 -- Returns zone index or 0 if showing the entire continent
1570 local zone = GetCurrentMapZone();
1571 if(zone == 0) then return; end; -- We don't support the continent map yet
1572  
1573 -- Create a table of locations to display in this zone
1574  
1575 local displayable = {};
1576  
1577 for i in FlightPath_Config.FlightPaths do
1578 if((FlightPath_Config.FlightPaths[i].Faction == UnitFactionGroup("player")) and (FP_UserKnowsPath(FlightPath_Config.FlightPaths[i]) or not FP_HideGrey()) ) then
1579 for j = 1, 2, 1 do
1580 if(FlightPath_Config.FlightPaths[i].Continent[j] == continent) then
1581 if(FlightPath_Config.FlightPaths[i].Zone[j] == zone) then
1582 dindex = FlightPath_Config.FlightPaths[i].Endpoints[j];
1583 if(displayable[dindex] == nil) then
1584 displayable[dindex] = {};
1585 displayable[dindex].Coords = FlightPath_Config.FlightPaths[i].Coords[j];
1586 end
1587 if(FP_UserKnowsPath(FlightPath_Config.FlightPaths[i])) then
1588 displayable[dindex].Texture = "Interface\\TaxiFrame\\UI-Taxi-Icon-Yellow";
1589 else
1590 if(displayable[dindex].Texture == nil) then
1591 displayable[dindex].Texture = "Interface\\TaxiFrame\\UI-Taxi-Icon-Gray";
1592 end
1593 end
1594 end
1595 end
1596 end
1597 end
1598 end
1599  
1600 -- Create POI's for found flight masters
1601  
1602 for j in displayable do
1603 buttonCount = buttonCount + 1;
1604 local POI = getglobal("FP_POI"..buttonCount);
1605 local POITexture = getglobal("FP_POI"..buttonCount.."Icon");
1606 local x, y = FP_ParseCoord(displayable[j].Coords);
1607 POITexture:SetTexture(displayable[j].Texture);
1608 POI:ClearAllPoints();
1609 POI:SetPoint("CENTER", "WorldMapDetailFrame", "TOPLEFT", x/100*WorldMapButton:GetWidth(), -y/100*WorldMapButton:GetHeight());
1610 POI:Show();
1611  
1612 -- Add a location field to the POI so we can query it when creating a tooltip
1613  
1614 POI.Location = j;
1615  
1616 end
1617  
1618 end
1619 end
1620  
1621 ---------------------------------------------------------------------
1622  
1623 function FP_POIOnEnter()
1624  
1625 -- Called when the mouse hovers over the flight path POI button on the zone map
1626  
1627 local px, py = this:GetCenter();
1628 local wx, wy = WorldMapButton:GetCenter();
1629 local align = "ANCHOR_LEFT";
1630 if(px <= wx) then align = "ANCHOR_RIGHT"; end
1631  
1632 WorldMapFrameAreaLabel:SetText(this.Location);
1633 WorldMapTooltip:SetOwner(this, align);
1634 local leftSide,rightSide = FP_GetPOITooltipText(this.Location);
1635 for i in leftSide do
1636 if(rightSide[i] ~= nil) then
1637 WorldMapTooltip:AddDoubleLine(leftSide[i],rightSide[i]);
1638 else
1639 WorldMapTooltip:AddLine(leftSide[i]);
1640 end
1641 end
1642 WorldMapTooltip:Show();
1643 end
1644  
1645 ---------------------------------------------------------------------
1646  
1647 function FP_POIOnLeave()
1648 WorldMapTooltip:Hide();
1649 WorldMapTooltip:SetText("");
1650 end
1651  
1652 ---------------------------------------------------------------------
1653  
1654 function FP_TaxiNodeOnButtonEnter(button)
1655  
1656 local showDuration = false;
1657 local buttonLocation = TaxiNodeName(this:GetID());
1658  
1659 if(buttonLocation ~= "INVALID") then
1660 if(not FP_IsSameLocation(TaxiOrigin,buttonLocation)) then
1661 local index,path = FP_FindPath(TaxiOrigin,buttonLocation);
1662 if(index ~= 0) then
1663 local duration = FP_GetDuration(path,TaxiOrigin);
1664 if((duration ~= nil) and (duration ~= "")) then
1665 ShoppingTooltip2:SetOwner(GameTooltip, "ANCHOR_BOTTOMRIGHT");
1666 ShoppingTooltip2:ClearAllPoints();
1667 ShoppingTooltip2:SetPoint("TOPLEFT", "GameTooltip", "TOPRIGHT", 0, -10);
1668 ShoppingTooltip2:AddLine("Flight time: "..duration, "", 0.5, 1.0, 0.5);
1669 showDuration = true;
1670 end
1671 end
1672 end
1673 end
1674  
1675 Original_TaxiNodeOnButtonEnter(button); -- call original handler
1676  
1677 if(showDuration) then
1678 ShoppingTooltip2:Show();
1679 end
1680  
1681 end
1682  
1683 ---------------------------------------------------------------------
1684  
1685 function FP_GetDuration(path,location)
1686  
1687 if(path.Duration == nil) then
1688 return nil;
1689  
1690 end
1691  
1692 if(location == path.Endpoints[1]) then
1693 return path.Duration[1]; -- Duration is always stored as 'time from' the corresponding endpoint
1694 else
1695 return path.Duration[2];
1696 end
1697 end
1698  
1699 ---------------------------------------------------------------------
1700  
1701 function FP_FindPath(location1, location2)
1702 for i in FlightPath_Config.FlightPaths do
1703 local endpoint1 = FlightPath_Config.FlightPaths[i].Endpoints[1];
1704 local endpoint2 = FlightPath_Config.FlightPaths[i].Endpoints[2];
1705 if((FP_IsSameLocation(location1,endpoint1) and FP_IsSameLocation(location2,endpoint2)) or
1706 (FP_IsSameLocation(location1,endpoint2) and FP_IsSameLocation(location2,endpoint1))) then
1707 return i,FlightPath_Config.FlightPaths[i];
1708 end
1709 end
1710 return 0,nil;
1711 end
1712  
1713 ---------------------------------------------------------------------
1714  
1715 function FP_Debug()
1716 FPDebugShow = true;
1717 FPDEBUG("Debug output enabled.");
1718 end
1719  
1720 ---------------------------------------------------------------------
1721  
1722 -- Debug routine to help users debug flightmaster/zone name mismatches.
1723  
1724 function FP_Check()
1725  
1726 if(not TaximapOpen) then
1727 FPCHAT("Please open up the flight master's connections map then type /fp check again.");
1728 return;
1729 end
1730  
1731 -- Find out what flight masters like to call this location
1732  
1733 local where;
1734 local numButtons = NumTaxiNodes();
1735 for i = 1, numButtons, 1 do
1736 if(TaxiNodeGetType(i) == "CURRENT") then
1737 where = TaxiNodeName(i);
1738 break;
1739 end
1740 end
1741  
1742 if(FP_IsSameLocation(where, FP_GetLocale())) then
1743 FPCHAT("FlightPath was able to match the flight master's name for this location ("..where..") to the map location ("..FP_GetLocale().."). No problems detected.");
1744 else
1745 FPCHAT("FlightPath was unable to match the flight master's name for this location ("..where..") to the map location ("..FP_GetLocale().."). Please report this to Kwraz!!!");
1746 end
1747 end
1748  
1749 ---------------------------------------------------------------------
1750  
1751 -- Display functions
1752  
1753 function FPCHAT(text)
1754 DEFAULT_CHAT_FRAME:AddMessage(STATUS_COLOR..text);
1755 end
1756  
1757  
1758 FPDebugShow = false;
1759  
1760 function FPDEBUG(...)
1761 if(FPDebugShow) then
1762 local text = "";
1763 for i = 1, arg.n, 1 do
1764 if(i>2) then text = text..", ";end;
1765 local value="";
1766 local vtype = type(arg[i]);
1767 if (vtype == "nil") then text = text.."(nil)";
1768 elseif(vtype == "number") then text = text..tostring(arg[i]);
1769 elseif(vtype == "string") then text = text..arg[i];
1770 elseif(vtype == "boolean") then if(arg[i]) then text = text.."true"; else text = text.."false"; end
1771 elseif(vtype == "table" or
1772 vtype == "function" or
1773 vtype == "thread" or
1774 vtype == "userdata") then text = text.."("..vtype..")";
1775 else text = text.."(unknown)";end
1776 end
1777 DEFAULT_CHAT_FRAME:AddMessage(DEBUG_COLOR.."FPDBG: "..text);
1778 end
1779 end
1780  
1781 function FPSPACE(count)
1782 return string.rep(" ",count);
1783 end
1784  
1785 ---------------------------------------------------------------------
1786 -- Development only
1787 ---------------------------------------------------------------------
1788  
1789 function FP_Test(text)
1790 local txt = "";
1791 if(text ~= nil) then txt=text;end;
1792 FPDEBUG("In FP_Test("..txt..")...");
1793  
1794 -- Begin test code
1795  
1796 local testForeign = "Grom'Gul, Mitteilung Mu\195\159sein";
1797 local tcity, tzone = FP_ParseLocation(testForeign);
1798 FPDEBUG(testForeign.." parsed to city '"..tcity.."' zone='"..tzone.."'");
1799  
1800 -- End test code
1801  
1802 FPDEBUG("...Exited FP_Test");
1803 end
1804