-- Kwraz's Flightpath Tracker Addon -- -- Displays known flightpaths on the world map, etc. -- -- All contents property of Kwraz of Icecrown. You are hereby given permission -- to make changes to this addon provided proper credit is given if you -- disseminate your changes publicly. -- -- Comments and questions can be addressed to kwraz@kjware.net (or via in game -- mail to Kwraz if you play Horde on Icecrown) -- local VERSION = "1.16"; -- Date Rev Comments -- ------ ---- ----------------------------------- -- 06/22/06 1.16 Updated for UI version 1.11 -- -- 10/12/05 1.15 Fixed error that occurred in 1.8 when reporting bound key -- Updated TOC to 1.8 -- -- 9/13/05 1.14 Fixed WorldMapButton error in 1.7. UI version set to 1.7 -- -- 6/27/05 1.13 OnPlayerEnteringWorld logic only done once -- Flight timer now takes UI delays into account -- On screen destination width increased -- -- 4/18/05 1.12 Removed debug statement causing user errors -- -- 4/18/05 1.11 Fixed bug with /fp load introduced in 1.10 -- -- 4/18/05 1.10 SetMapToCurrentZone fixup -- Bound key now toggles dialog on/off -- Alt-z to hide UI now also hides time remaining counter -- Cleaned up appearance of zone map connection tooltip -- Changed regular expressions to correction handle foreign characters -- Version number displayed on the dialog -- Other faction's paths were showing on Booty Bay flightmaster. Fixed. -- Changing zones with the dialog up caused the drop down to display -- zones instead of locations. Fixed. -- -- 4/14/05 1.09 Fixed incompatibility with VisibleFlightMap -- Zone map icons changed to the same as the flight master's map -- Undiscovered flight path locations show as grey on zone map -- Dialog drop down box scaled down to fit within dialog -- -- 4/13/05 1.08 Added bindings.xml to support hotkey assignment via wow menu -- Flight master icons will no longer display on the zone map if greyed -- Added flight times and costs to zone map tooltips -- Added hideremaining and showremaining commands -- Fixed problem with incorrect locations being stored for flight masters (hopefully) -- -- 4/12/05 1.07 Cleaned up shared variable names to respect conventions. Added confirmation -- dialog to /fp erase. Added MyAddons support. Flight time now shown when talking -- to flight master. Flight time remaining countdown timer added. Flight duration -- tracked separately for each direction. Added /fp load command. Fixed drop down -- problem when more than 32 locations. -- -- 4/8/05 1.06 Added /fp erase, /fp showgrey, /fp hidegray. Additional changes -- made to location matching logic to try and avoid duplicate path -- creation for Stormwind, Ironforge, and Moonglade. -- -- 4/7/05 1.05 Fixed string error the first time FlightPath is installed. -- -- 4/7/05 1.04 Whether or not to gray out a connection is now tracked by character, -- since different characters will have different routes available to them. -- Escape key now closes dialog. -- -- 4/7/05 1.03 Greyed out unavailable routes in map tooltip as well. -- -- 4/7/05 1.02 Preloaded flights you cannot take are now coded in a different color. -- -- 4/6/05 1.01 Fixed a problem with the drop down list running off screen with a large number -- of entries -- 4/6/05 1.0 Initial release -- --------------------------------------------------------------------- -- Info to show on the WoW Key Bindings menu BINDING_HEADER_FPHEADER = "Kwraz's Flightpath Tracker"; BINDING_NAME_FPDIALOG = "Query dialog"; -- Constants local STATUS_COLOR = "|c0033CCFF"; local CONNECTION_COLOR = "|c0033FF66"; local MONEY_COLOR = "|c00FFCC33"; local DEBUG_COLOR = "|c0000FF00"; local GREY = "|c00909090"; local BRIGHTGREY = "|c00D0D0D0"; local WHITE = "|c00FFFFFF"; local MAX_CONNECTIONS = 16; local MAX_POI_BUTTONS = 48; local CONNECTION_START_ID = 1101; local FP_LEARNED_FROM_FLIGHTMASTER = 1; local FP_LEARNED_FROM_PRELOAD = 2; local NOCOORDS = "0,0"; local MAXDROPDOWN = 21; local MORECOLOR = "|c00FF9900"; local MOREUP = MORECOLOR.."--- More ^ ---"; local MOREDOWN = MORECOLOR.."--- More v ---"; -- Shared variables local StartRecording = false; local Recording = false; local TaxiOrigin = ""; local TaxiDestination = ""; local TaxiDirection = 0; local CurrentFlight = {}; local StartTime; local Connections = {}; local LastTime = time(); local PopulatePass = 0; local TaximapOpen = false; local TimeRemaining = 0; local DropDownStartIndex = 1; -- Hooked functions local Original_WorldMapButton_OnUpdate; local Original_TaxiNodeOnButtonEnter; local Original_TakeTaxiNode; --------------------------------------------------------------------- function FP_Help() FPCHAT(" "); FPCHAT(BINDING_HEADER_FPHEADER.." commands:"); FPCHAT("/fp" ..FPSPACE(50)..WHITE.." - Displays the FlightPath dialog"); FPCHAT("/fp enable | disable" ..FPSPACE(24)..WHITE.." - Enables or disables the FlightPath addon"); FPCHAT("/fp showgrey | hidegrey" ..FPSPACE(17)..WHITE.." - Show greyed out flight paths or not"); FPCHAT("/fp showremaining | hideremaining"..FPSPACE(0) ..WHITE.." - Show or hide in flight time remaining"); FPCHAT("/fp erase" ..FPSPACE(40)..WHITE.." - Erases all remembered flight paths"); FPCHAT("/fp load misc | horde | alliance" ..FPSPACE(7) ..WHITE.." - Load a supplied flight path list"); FPCHAT("/fp status" ..FPSPACE(40)..WHITE.." - Displays various flight path statistics"); FPCHAT(" "); end --------------------------------------------------------------------- -- Handle command line arguments function FP_SlashHandler(msg) local _,_,command,options = string.find(msg,"([%w%p]+)%s*(.*)$"); if (command == nil) then FP_FlightPathDialog(); elseif (string.lower(command) == 'enable') then FP_Enable(); elseif (string.lower(command) == 'disable') then FP_Disable(); elseif (string.lower(command) == 'status') then FP_Status(); elseif (string.lower(command) == 'erase') then FP_Erase(); elseif (string.lower(command) == 'showgrey') then FP_SetShowGrey(); elseif (string.lower(command) == 'hidegrey') then FP_SetHideGrey(); elseif (string.lower(command) == 'showremaining') then FP_SetShowRemaining(); elseif (string.lower(command) == 'hideremaining') then FP_SetHideRemaining(); elseif (string.lower(command) == 'load') then FP_Load(options); elseif (string.lower(command) == 'check') then FP_Check(); -- Undocumented: Validate current flight master elseif (string.lower(command) == 'debug') then FP_Debug(); -- Undocumented: Enable debug output elseif (string.lower(command) == 'test') then FP_Test(options); -- Undocumented: Invoke testbed else FP_Help(); end end --------------------------------------------------------------------- -- Tell WoW what events we are interested in being notified of function FP_EventFrame_OnLoad(frameName) -- Register for any event we want to be notified of this:RegisterEvent("VARIABLES_LOADED"); this:RegisterEvent("PLAYER_ENTERING_WORLD"); this:RegisterEvent("TAXIMAP_OPENED"); this:RegisterEvent("TAXIMAP_CLOSED"); this:RegisterEvent("WORLD_MAP_UPDATE"); end --------------------------------------------------------------------- -- Map events to the appropriate internal handler function FP_EventFrame_OnEvent() if (event == "VARIABLES_LOADED") then FP_VariablesLoaded(); elseif (event == "PLAYER_ENTERING_WORLD") then FP_PlayerEnteringWorld(); elseif (event == "TAXIMAP_OPENED") then FP_TaxiMapOpened(); elseif (event == "TAXIMAP_CLOSED") then FP_TaxiMapClosed(); elseif (event == "WORLD_MAP_UPDATE") then FP_WorldMapUpdate(); else FPDEBUG("Unhandled Event "..WHITE..event); end; end --------------------------------------------------------------------- function FP_VariablesLoaded() -- Update the global WoW command handler table so it knows about us SlashCmdList["FLIGHTPATH"] = FP_SlashHandler; SLASH_FLIGHTPATH1 = "/flightpath"; SLASH_FLIGHTPATH2 = "/fp"; if (FlightPath_Config == nil) then FP_InitializeConfig(); end -- Let them know we're up and running FPCHAT(BINDING_HEADER_FPHEADER.." version "..VERSION.." loaded. Type "..WHITE.."/fp ?"..STATUS_COLOR.." for more info."); local sbool = "disabled."; if(FlightPath_Config.Enabled) then sbool = "enabled."; end; FPCHAT("Flightpath hooks are "..sbool); -- MyAddons support if(myAddOnsFrame) then myAddOnsList.FlightPath = { name = "FlightPath", description = BINDING_HEADER_FPHEADER, version = VERSION, category = MYADDONS_CATEGORY_MAP, frame = "FP_UIFrame"}; end end --------------------------------------------------------------------- function FP_InitializeConfig() FlightPath_Config = {}; FlightPath_Config.Version = VERSION; FlightPath_Config.Enabled = true; FlightPath_Config.HideRemaining = false; FlightPath_Config.FlightPaths = {}; end --------------------------------------------------------------------- function FP_PlayerEnteringWorld() FP_DoVersionFixups(); -- Perform any version specific fixups on the users database FlightPath_Config.Version = VERSION; FP_EnableHooks(); FP_ReportBindingKey(); this:UnregisterEvent("PLAYER_ENTERING_WORLD"); -- So boat/zep zoning doesn't call this again end --------------------------------------------------------------------- function FP_Enable() FlightPath_Config.Enabled = true; FP_EnableHooks(); FPCHAT("FlightPath hooks have been enabled."); end --------------------------------------------------------------------- function FP_Disable() FlightPath_Config.Enabled = false; Recording = false; -- Just in case we are in flight FP_DisableHooks(); FPCHAT("FlightPath hooks have been disabled."); end --------------------------------------------------------------------- function FP_SetShowRemaining() FlightPath_Config.HideRemaining = false; FPCHAT("The in flight time remaining display has been enabled."); end --------------------------------------------------------------------- function FP_SetHideRemaining() FlightPath_Config.HideRemaining = true; FPCHAT("The in flight time remaining display has been disabled."); end --------------------------------------------------------------------- -- User typed /fp erase. Clear out the old database and start from scratch. -- But first make sure they really really want to function FP_Erase() StaticPopupDialogs["FP_CONFIRM_ERASE"] = { text = "\nThis will erase ALL of your recorded flight paths.\n\nAre you sure?", button1 = TEXT(ACCEPT), button2 = TEXT(CANCEL), OnAccept = function() FP_EraseConfirmed(); end, timeout = 0, }; StaticPopup_Show("FP_CONFIRM_ERASE") end function FP_EraseConfirmed() FP_InitializeConfig(); FPCHAT("All FlightPath routes have been cleared."); end --------------------------------------------------------------------- function FP_SetShowGrey() if(FlightPath_Config.HideGrey ~= nil) then for i in FlightPath_Config.HideGrey do if(FlightPath_Config.HideGrey[i] == FP_GetNameServer()) then table.remove(FlightPath_Config.HideGrey,i); break; end end end FPCHAT("Greyed out flight paths will now be shown for "..UnitName("player")); end --------------------------------------------------------------------- function FP_SetHideGrey() if(FlightPath_Config.HideGrey == nil) then FlightPath_Config.HideGrey = {}; end; local found = false; for i in FlightPath_Config.HideGrey do if(FlightPath_Config.HideGrey[i] == FP_GetNameServer()) then found = true; break; end end if(not found) then table.insert(FlightPath_Config.HideGrey,FP_GetNameServer()); end FPCHAT("Greyed out flight paths will now be hidden for "..UnitName("player")); end --------------------------------------------------------------------- function FP_HideGrey() if(FlightPath_Config.HideGrey == nil) then return false; end; for i in FlightPath_Config.HideGrey do if(FlightPath_Config.HideGrey[i] == FP_GetNameServer()) then return true; end end return false; end --------------------------------------------------------------------- function FP_EnableHooks() -- Hook the world map button so we know when map zones change via pulldown menu if(WorldMapButton_OnUpdate ~= FP_WorldMapButton_OnUpdate) then Original_WorldMapButton_OnUpdate = WorldMapButton_OnUpdate; WorldMapButton_OnUpdate = FP_WorldMapButton_OnUpdate; end -- Hook the TaxiMap tooltip function so we can add duration info if(TaxiNodeOnButtonEnter ~= FP_TaxiNodeOnButtonEnter) then Original_TaxiNodeOnButtonEnter = TaxiNodeOnButtonEnter TaxiNodeOnButtonEnter = FP_TaxiNodeOnButtonEnter; end -- Hook the TakeTaxiNode function so we know where we are going if(TakeTaxiNode ~= FP_TakeTaxiNode) then Original_TakeTaxiNode = TakeTaxiNode; TakeTaxiNode = FP_TakeTaxiNode; end end --------------------------------------------------------------------- function FP_DisableHooks() -- Unhook the world map button if(Original_WorldMapButton_OnUpdate and (WorldMapButton_OnUpdate == FP_WorldMapButton_OnUpdate)) then WorldMapButton_OnUpdate = Original_WorldMapButton_OnUpdate; end -- Unhook the TaxiMap tooltip if(Original_TaxiNodeOnButtonEnter and (TaxiNodeOnButtonEnter == FP_TaxiNodeOnButtonEnter)) then TaxiNodeOnButtonEnter = Original_TaxiNodeOnButtonEnter; end -- Unhook the TakeTaxiNode function if(Original_TakeTaxiNode and (TakeTaxiNode == FP_TakeTaxiNode)) then TakeTaxiNode = Original_TakeTaxiNode; end end --------------------------------------------------------------------- function FP_DoVersionFixups() -- Check what version the user was running previously and do any needed upgrades if(FlightPath_Config.Version == nil) then return; end; local dbversion; _,_,dbversion = string.find(FlightPath_Config.Version,"(%d*.%d+)"); -- Beta had format ".98 beta" so only grab the number part dbversion = tonumber(dbversion); -- If this is a user that participated in the beta test, clear out the old (incompatible) database if(dbversion < 1.0) then FlightPath_Config.FlightPaths = {}; FPCHAT("\nThanks for beta testing FlightPath!"); FPCHAT("All of your flight path information has been reset."); FPCHAT("If you don't want to have to relearn all of your flight paths use the '/fp load' command."); -- Versions lower than 1.04 did not have the KnownBy field, and some had the deprecated Greyed flag. elseif(dbversion < 1.04) then FPCHAT("Converting FlightPath data from version "..dbversion.." to version "..VERSION); for i in FlightPath_Config.FlightPaths do local greyed = false; local thisPathKnownBy = FP_GetNameServer(); if(FlightPath_Config.FlightPaths[i].Greyed ~= nil) then greyed = FlightPath_Config.FlightPaths[i].Greyed; if(not greyed) then -- This item was forced to be not greyed in prior versions. We accomplish -- the same thing in 1.04 by setting the KnownBy to ALL thisPathKnownBy = "ALL"; end end if(not greyed) then if(not FP_UserKnowsPath(FlightPath_Config.FlightPaths[i])) then if(FlightPath_Config.FlightPaths[i].KnownBy == nil) then FlightPath_Config.FlightPaths[i].KnownBy = {} end table.insert(FlightPath_Config.FlightPaths[i].KnownBy,thisPathKnownBy); end end FlightPath_Config.FlightPaths[i].Greyed = nil; -- Remove deprecated field end -- Version 1.07 changed the duration from a single value to a value for each direction elseif(dbversion < 1.07) then FPCHAT("Converting FlightPath data from version "..dbversion.." to version "..VERSION); for i in FlightPath_Config.FlightPaths do if(FlightPath_Config.FlightPaths[i].Duration ~= nil) then local duration = FlightPath_Config.FlightPaths[i].Duration; FlightPath_Config.FlightPaths[i].Duration = { duration, duration }; -- start with both directions the same end end end FlightPath_Config.Version = VERSION; end --------------------------------------------------------------------- function FP_EventFrame_OnUpdate() -- This function is called frequently so be cpu and memory friendly if (FlightPath_Config.Enabled) then FP_CheckInFlightStatus(); local ctime = time(); if(ctime ~= LastTime) then -- Do periodic processing (once per second or so) FP_ShowInFlightCountdown(ctime-LastTime); LastTime = ctime; end end end --------------------------------------------------------------------- function FP_CheckInFlightStatus() -- Check the flight recording state. if (Recording and not UnitOnTaxi("player")) then -- End of the road, stop recording FP_FlightPathEnd(); Recording = false; StartRecording = false; end if (StartRecording) then if (UnitOnTaxi("player")) then FPDEBUG("Beginning flight from "..TaxiOrigin.." to "..TaxiDestination); StartRecording = false; Recording = true; end end end --------------------------------------------------------------------- function FP_ShowInFlightCountdown(secondsElapsed) if(TimeRemaining > 0) then TimeRemaining = TimeRemaining - secondsElapsed; -- Decrease in flight time remaining end if((not FlightPath_Config.HideRemaining) and (TimeRemaining > 0)) then if(UnitOnTaxi("player")) then FP_CountdownTimerCity:SetText(TaxiDestination); FP_CountdownTimerCity:Show(); local text = "Arriving in "; text = text..FP_FormatTime(TimeRemaining); FP_CountdownTimerRemaining:SetText(text); FP_CountdownTimerRemaining:Show(); end else FP_CountdownTimerCity:Hide(); FP_CountdownTimerRemaining:Hide(); end end --------------------------------------------------------------------- function FP_GetNameServer() local name, server; name = UnitName("player"); server = GetCVar("realmName"); return name..","..server end --------------------------------------------------------------------- function FP_TaxiMapOpened() if(FlightPath_Config.Enabled) then TaximapOpen = true; -- Loop through the displayed buttons on the taxi map and -- add any routes that we haven't yet recorded local numButtons = NumTaxiNodes(); -- Find out what flight masters like to call this location for i = 1, numButtons, 1 do if(TaxiNodeGetType(i) == "CURRENT") then TaxiOrigin = TaxiNodeName(i); break; end end -- Now check for new flight paths between here and all the ones shown on the taxi map local x, y = FP_GetPlayerCoords(); local continent,zone = FP_GetRealContinentZone(); FPDEBUG("Setting continent,zone,coords for current flight master to:",continent,zone,x,y); for i = 1, numButtons, 1 do if(TaxiNodeGetType(i) == "REACHABLE") then local index, path; index,path = FP_FindPath(TaxiOrigin, TaxiNodeName(i)); if(index == 0) then path = {}; path.Endpoints = {TaxiOrigin,TaxiNodeName(i)}; path.Continent = {continent,0}; path.Zone = {zone,0}; path.Faction = UnitFactionGroup("player"); path.Coords = {x..","..y,NOCOORDS}; else if(path.Endpoints[1] == TaxiOrigin) then path.Coords[1] = x..","..y; path.Continent[1] = continent; path.Zone[1] = zone; else path.Coords[2] = x..","..y; path.Continent[2] = continent; path.Zone[2] = zone; end end path.Cost = TaxiNodeCost(i); FP_AddNewFlightPath(path,FP_LEARNED_FROM_FLIGHTMASTER); end end end end --------------------------------------------------------------------- function FP_TaxiMapClosed() if(FlightPath_Config.Enabled) then TaximapOpen = false; end end --------------------------------------------------------------------- function FP_TakeTaxiNode(nodeID) TaxiDestination = TaxiNodeName(nodeID); StartTime = time(); TimeRemaining = 0; local index; index,CurrentFlight = FP_FindPath(TaxiOrigin, TaxiDestination); if(index == 0) then FPDEBUG("Internal error! FlightPath could not locate the flight path from "..TaxiOrigin.." to "..TaxiDestination); else -- Durations are stored associated with the from endpoint. TaxiDirection tells whether we are -- traveling from the first endpoint or the second endpoint of the connection pair. if(TaxiOrigin == CurrentFlight.Endpoints[1]) then TaxiDirection = 1; else TaxiDirection = 2; end if(CurrentFlight.Duration == nil) then CurrentFlight.Duration = {"",""}; else local _,_,minutes,seconds = string.find(CurrentFlight.Duration[TaxiDirection],"(%d+):(%d+)"); if(minutes and seconds) then TimeRemaining = (minutes*60) + seconds; end end end StartRecording =true; -- Tell the OnUpdate handler to start checking flight status Original_TakeTaxiNode(nodeID); -- Call the original handler now end --------------------------------------------------------------------- function FP_FlightPathEnd() -- We've landed. Record the duration of the flight and the coordinates of the -- flightmaster here if not already known. local x, y = FP_GetPlayerCoords(); local destIndex = 2; if(TaxiDirection == 2) then destIndex = 1; end; TimeRemaining = 0; CurrentFlight.Duration[TaxiDirection] = FP_Elapsed(StartTime,time()); CurrentFlight.Coords[destIndex] = x..","..y; CurrentFlight.Continent[destIndex],CurrentFlight.Zone[destIndex] = FP_GetRealContinentZone(); FP_AddNewFlightPath(CurrentFlight,FP_LEARNED_FROM_FLIGHTMASTER); end --------------------------------------------------------------------- function FP_AddNewFlightPath(new,source) -- We have a potential new flight path. If we already know the path then update any -- relevant information, otherwise add the path to our data base. local index,foundPath = FP_FindPath(new.Endpoints[1],new.Endpoints[2]); local isNewPath = (foundPath == nil); -- Update the coords, continent, and zone of any connection point that matches one in the new path. -- (The need for this will go away once I properly normalize the data tables) if(source == FP_LEARNED_FROM_FLIGHTMASTER) then for i in FlightPath_Config.FlightPaths do for j = 1, 2, 1 do for k = 1, 2, 1 do if (new.Endpoints[j] == FlightPath_Config.FlightPaths[i].Endpoints[k]) then if(new.Coords[j] ~= NOCOORDS) then if(new.Coords[j] ~= FlightPath_Config.FlightPaths[i].Coords[k]) then FPDEBUG("Replacing coords on flightpath "..i.." ("..FlightPath_Config.FlightPaths[i].Endpoints[k]..")\nFrom "..FlightPath_Config.FlightPaths[i].Coords[k].." to "..new.Coords[j]); FlightPath_Config.FlightPaths[i].Coords[k] = new.Coords[j]; end end if(new.Continent[j] ~= 0) then FlightPath_Config.FlightPaths[i].Continent[k] = new.Continent[j]; end if(new.Zone[j] ~= 0) then FlightPath_Config.FlightPaths[i].Zone[k] = new.Zone[j]; end end end end end end -- Either add it to our flight path database or update the cost and duration if(isNewPath) then table.insert(FlightPath_Config.FlightPaths,new); index = table.getn(FlightPath_Config.FlightPaths); if (source == FP_LEARNED_FROM_FLIGHTMASTER) then FPCHAT("FlightPath learned a new route between "..new.Endpoints[1].." and "..new.Endpoints[2]); end else -- I'm going to go on the assumption that information coming in from a flightmaster is more current -- than the potentially stale preloaded flight path information. If the user types a /fp load command -- after having learned their own flight paths I want to ensure that the preloaded data does not overwrite -- the learned data. For this reason costs and times are only updated in the database when the path is -- one we obtained from a flight master. (On an initial /fp load the paths will be unknown so the the -- times and costs from the preloaded paths will be the initial values). if(source == FP_LEARNED_FROM_FLIGHTMASTER) then -- Cost if(new.Cost) then FlightPath_Config.FlightPaths[index].Cost = new.Cost; end; -- Duration if(new.Duration and (table.getn(new.Duration) >= 1)) then if(FlightPath_Config.FlightPaths[index].Duration == nil) then FlightPath_Config.FlightPaths[index].Duration = {"",""}; end if(new.Endpoints[1] == FlightPath_Config.FlightPaths[index].Endpoints[1]) then FlightPath_Config.FlightPaths[index].Duration[1] = new.Duration[1]; if(FlightPath_Config.FlightPaths[index].Duration[2] == "") then FlightPath_Config.FlightPaths[index].Duration[2] = new.Duration[2]; end else FlightPath_Config.FlightPaths[index].Duration[2] = new.Duration[1]; if(FlightPath_Config.FlightPaths[index].Duration[1] == "") then FlightPath_Config.FlightPaths[index].Duration[1] = new.Duration[2]; end end -- If one direction still has no duration, assume it's the same as the other leg if(FlightPath_Config.FlightPaths[index].Duration[1] == "") then FlightPath_Config.FlightPaths[index].Duration[1] = FlightPath_Config.FlightPaths[index].Duration[2]; end if(FlightPath_Config.FlightPaths[index].Duration[2] == "") then FlightPath_Config.FlightPaths[index].Duration[2] = FlightPath_Config.FlightPaths[index].Duration[1]; end end; end end -- Record the fact that the user knows this flight so it no longer displays as grey or shows up if they have hidegrey set if(source == FP_LEARNED_FROM_FLIGHTMASTER) then if(not FP_UserKnowsPath(FlightPath_Config.FlightPaths[index])) then if(FlightPath_Config.FlightPaths[index].KnownBy == nil) then FlightPath_Config.FlightPaths[index].KnownBy = {}; end table.insert(FlightPath_Config.FlightPaths[index].KnownBy,FP_GetNameServer()); end end end --------------------------------------------------------------------- function FP_Elapsed(start,finish) return FP_FormatTime(finish-start); end --------------------------------------------------------------------- function FP_FormatTime(duration) local minutes = floor(duration / 60); local seconds = duration - (minutes * 60); local tens = floor(seconds/10); local single = seconds - (tens * 10); return minutes..":"..tens..single; end --------------------------------------------------------------------- function FP_GetPlayerCoords() FP_ForceMapToCurrentZone(); -- voodoo here to try and address WoW bug local px, py = GetPlayerMapPosition("player"); -- normalize coords to ##.## format px = floor(px * 10000); py = floor(py * 10000); px = px / 100; py = py / 100; return px,py end --------------------------------------------------------------------- function FP_ForceMapToCurrentZone() if(GetCurrentMapZone() ~= 0) then SetMapToCurrentZone(); -- Normally this is what will happen else local continent,zone = FP_GetRealContinentZone(); SetMapZoom(continent,zone); end end --------------------------------------------------------------------- function FP_GetRealContinentZone() if(GetCurrentMapZone() ~= 0) then return GetCurrentMapContinent(),GetCurrentMapZone(); end -- We're bugged. Fortunately GetRealZoneText() is accurate so we can use that -- to figure out our zone. (Remember, continent and zone numbers equate to their -- index number in the world map pulldowns). This idea was first implemented by -- Legorol in his ZoneFix mod. local continent,zone,name local zoneText = GetRealZoneText(); for continent in ipairs{GetMapContinents()} do for zone,name in ipairs{GetMapZones(continent)} do if(name == zoneText) then FPDEBUG("SetMapToCurrentZone bug detected!! You should zone or logout before talking to any flight masters. Returning fixed up continent,zone:",continent,zone); return continent,zone; end end end return 0,0; -- Should never happen end --------------------------------------------------------------------- function FP_GetLocale() local zone = GetRealZoneText(); local city = GetMinimapZoneText(); if (zone == city) then return zone; else return city..", "..zone; end end --------------------------------------------------------------------- function FP_Load(option) if(option == nil or option == "") then FPCHAT("Usage: "..WHITE.."/fp load name"..STATUS_COLOR.." where name is one of 'misc', 'horde', or 'alliance'."); return; end local toLoad = getglobal("FP_"..option.."Paths"); if(toLoad == nil) then FPCHAT("Could not find a list called "..option.."Paths in the KnownPaths.lua file.\nType '/fp ?' for help."); return; end for i in toLoad do FP_AddNewFlightPath(toLoad[i],FP_LEARNED_FROM_PRELOAD); end FPCHAT("Scanned "..table.getn(toLoad).." "..option.." paths for new connections."); end --------------------------------------------------------------------- function FP_IsSameLocation(location1, location2) -- So why are we going through all these gyrations to see if one location is -- the same as another? Because WoW uses different location names in the taxi -- map than they do for the city and zone names proper. For example. the -- flight master in Orgrimmar is located in the "Valley of Strength, Orgrimmar" -- if you use GetZoneText() or other API calls. However the name of the -- taxi node for that flight master is "Orgrimmar, Durotar". There are many -- other locations in Azeroth that have similar discrepancies. (Ironforge, -- Moonglade, ...) -- -- In order to bring up the correct dialog page for the location the player -- is currently, we need to correlate these two different versions of a place name. -- See if they just match if(location1 == location2) then return true; end local c1, z1, c2, z2; c1,z1 = FP_ParseLocation(location1); c2,z2 = FP_ParseLocation(location2); -- See if city names match (since city names are unique) if((c1 ~= "") and (c1 == c2)) then return true; end -- See if the city and zones are swapped (e.g. Orgrimmar) if((c1 ~= "") and (c1 == z2)) then return true; end if((c2 ~= "") and (c2 == z1)) then return true; end -- See if parts of city match (e.g. "The Crossroads", "Crossroads") local b, e, match; if(c1 ~= "" and c2 ~= "") then b,e,match = string.find(c1,c2,1,true); if(b) then return true; end; b,e,match = string.find(c2,c1,1,true); if(b) then return true; end; end -- See if parts of zone match city (e.g. "Stormwind,Elwynn", "Trade District,Stormwind City") if(c1 ~= "" and z2 ~= "") then b,e,match = string.find(c1,z2,1,true); if(b) then return true; end; b,e,match = string.find(z2,c1,1,true); if(b) then return true; end; end if(c2 ~= "" and z1 ~= "") then b,e,match = string.find(c2,z1,1,true); if(b) then return true; end; b,e,match = string.find(z1,c2,1,true); if(b) then return true; end; end -- Give up and assume the locations are not the same return false; end --------------------------------------------------------------------- function FP_FindCoords(location) -- Loop through all known flight paths and see if we can find the -- coordinate position of the specified flight master. -- This will be uneccessary when I properly normalize the data tables local coords = NOCOORDS; for i in FlightPath_Config.FlightPaths do if(FP_IsSameLocation(location,FlightPath_Config.FlightPaths[i].Endpoints[1])) then if(FlightPath_Config.FlightPaths[i].Coords[1] ~= NOCOORDS) then coords = FlightPath_Config.FlightPaths[i].Coords[1]; break; end end if(FP_IsSameLocation(location,FlightPath_Config.FlightPaths[i].Endpoints[2])) then if(FlightPath_Config.FlightPaths[i].Coords[2] ~= NOCOORDS) then coords = FlightPath_Config.FlightPaths[i].Coords[2]; break; end end end return coords; end --------------------------------------------------------------------- function FP_GetRecordedFlightpaths() -- Return a list of known locations for display in the dialog drop down list local flightpaths = {}; -- reverse zone and city so list is sorted by zone local lookup = {} for i in FlightPath_Config.FlightPaths do if (FlightPath_Config.FlightPaths[i].Faction == UnitFactionGroup("player")) then if(FP_UserKnowsPath(FlightPath_Config.FlightPaths[i]) or (not FP_HideGrey())) then for j = 1, 2, 1 do local tcity, tzone = FP_ParseLocation(FlightPath_Config.FlightPaths[i].Endpoints[j]); local zoneFirst = tzone; if(tcity ~= "") then zoneFirst = zoneFirst.." - "..tcity; end if (lookup[zoneFirst] == nil) then lookup[zoneFirst] = true; table.insert(flightpaths,zoneFirst); end end end end end table.sort(flightpaths); return flightpaths; end --------------------------------------------------------------------- function FP_ParseCoord(coord) local _, _, x, y = string.find(coord,"(%d*.%d*),(%d*.%d*)"); return tonumber(x), tonumber(y); end --------------------------------------------------------------------- function FP_ParseLocation(locale) -- locale can be: -- zone we are in an unnamed area of a zone -- city, zone normal parse -- zone - city dash means swap zone city order (is dropdown entry) if(not locale) then return "",""; end local b, e, city, zone, dash; b,e,dash = string.find(locale," - ", 1, true); -- it's in dropdown format if it has a dash if(b) then zone = FP_Trim(string.sub(locale,1,b-1)); city = FP_Trim(string.sub(locale,e+1,string.len(locale))); return city,zone; end b,e,city = string.find(locale, "([%P%'%` ]+),"); -- This should be good for foreign character sets as well if(not b) then city = ""; zone = FP_Trim(locale); else b,e,zone = string.find(locale, "[%P%'%` ]+,%s*([%P%'%` ]+)"); if(not b) then -- For degenerate cases like Moonglade that don't return a zone use the city name as the zone zone = FP_Trim(city); end; end return city,zone; end --------------------------------------------------------------------- function FP_Trim(text) -- Remove trailing spaces from a string if(text == nil) then return; end; local _,_,trimmed = string.find(text,"(.-)%s*$"); return trimmed; end --------------------------------------------------------------------- -- -- User Interface logic -- --------------------------------------------------------------------- function FP_FlightPathDialog() if (FP_UIFrame:IsVisible() ) then FP_UIFrame:Hide(); -- So bound key toggles dialog on/off else DropDownStartIndex=1; FP_DisplayDialogConnections(); FP_UIFrame:Show(); end end --------------------------------------------------------------------- function FP_WorldMapUpdate() -- Changing zones overwrites DropDownList1 which the dialog uses. -- If we change zones with the dialog open, repopulate the list if (FP_UIFrame:IsVisible() ) then FP_UIZoneDropDown_Initialize(); end end --------------------------------------------------------------------- function FP_UIZoneDropDown_Initialize() -- Called from the frames OnShow FP_UIZoneDropDownClear(); local index=DropDownStartIndex; local info = {}; local buttonCount = 0; local flightpaths = FP_GetRecordedFlightpaths(); local knownCount = table.getn(flightpaths); for i = 1, MAXDROPDOWN, 1 do local buttonText = ""; if((i == 1) and (DropDownStartIndex > 1)) then buttonText = MOREUP; elseif((i == MAXDROPDOWN) and (index < (knownCount-1))) then buttonText = MOREDOWN; else if(index > knownCount) then break;end; buttonText = flightpaths[index]; index = index + 1; end info={}; info.text = buttonText; info.func = FP_UIZoneDropDown_OnClick; info.keepShownOnClick = true; info.notCheckable = true; -- WoW is bugged and will still show check but at least save width UIDropDownMenu_AddButton(info,1); end DropDownList1:ClearAllPoints(); DropDownList1:SetPoint("CENTER","FP_UIFrame","CENTER",0,0); end --------------------------------------------------------------------- function FP_UIZoneDropDownClear() local text = UIDropDownMenu_GetText(FP_UIZoneDropDown); UIDropDownMenu_ClearAll(FP_UIZoneDropDown); UIDropDownMenu_SetText(text,FP_UIZoneDropDown); DropDownList1.numButtons = 0; end --------------------------------------------------------------------- function FP_UIZoneDropDown_OnClick() if(this:GetText() == MOREDOWN) then FPDEBUG("Down arrow detected"); local flightpaths = FP_GetRecordedFlightpaths(); local knownCount = table.getn(flightpaths); DropDownStartIndex = knownCount - MAXDROPDOWN+1; FP_UIZoneDropDown_Initialize(); return; elseif(this:GetText() == MOREUP) then FPDEBUG("Up arrow detected"); DropDownStartIndex = 1; FP_UIZoneDropDown_Initialize(); return; else FP_DisplayDialogConnections(this:GetText()); DropDownList1:Hide(); end end --------------------------------------------------------------------- function FP_FormatMoney(money) if(money == nil) then return ""; end; amount = tonumber(money); if(amount == nil) then FPDEBUG("Failed to convert money string to number: ",money); return "" end local gold, silver, copper, text; gold = floor(amount / (100*100)); silver = mod(floor(amount / 100), 100); copper = mod(floor(amount + .5), 100); text = ""; if(gold > 0) then text = gold.."g"; end if(silver > 0) then if(text ~= "") then text = text.." "; end; text = text..silver.."s"; end if(copper > 0) then if(text ~= "") then text = text.." "; end; text = text..copper.."c"; end return text; end --------------------------------------------------------------------- function FP_DisplayDialogConnections(location) -- Display the appropriate connections text on the FlightPath dialog box local text; local routeFound = false; local databaseIsEmpty = true; local tlocation, b, e, i, j, duration; -- Used to make a second attempt based on zone number if we fail to populate based on location text PopulatePass = PopulatePass+1; local _,currentMapZone = FP_GetRealContinentZone(); local repopulateLoc = ""; if(not location) then location = FP_GetLocale(); -- On initial call determine where we are end tlocation = location; Connections = {}; -- clear shared table local connectionCost = {}; local connectionColor = {}; for i in FlightPath_Config.FlightPaths do if (FlightPath_Config.FlightPaths[i].Faction == UnitFactionGroup("player")) then for j = 1, 2, 1 do if(FlightPath_Config.FlightPaths[i].Zone[j] == currentMapZone) then repopulateLoc = FlightPath_Config.FlightPaths[i].Endpoints[j]; -- In case we need to make 2nd pass cause 1st came up empty end if(FP_IsSameLocation(location,FlightPath_Config.FlightPaths[i].Endpoints[j])) then tlocation = FlightPath_Config.FlightPaths[i].Endpoints[j]; local which = 1; if(j == 1) then which = 2; end; -- If this is a preloaded flight can't yet fly, grey it out local color = CONNECTION_COLOR; local mcolor = MONEY_COLOR; if(not FP_UserKnowsPath(FlightPath_Config.FlightPaths[i])) then color = GREY; mcolor = GREY; end -- Only show greyed paths if user hasn't disabled displaying them if((color ~= GREY) or (not FP_HideGrey())) then connectionColor[FlightPath_Config.FlightPaths[i].Endpoints[which]] = color; -- Add this endpoint to our available connections table.insert(Connections,FlightPath_Config.FlightPaths[i].Endpoints[which]); -- If we know the cost, display it text = "" if(FlightPath_Config.FlightPaths[i].Cost) then text = mcolor..FP_FormatMoney(FlightPath_Config.FlightPaths[i].Cost); end -- If we know the duration, display it duration = FP_GetDuration(FlightPath_Config.FlightPaths[i], FlightPath_Config.FlightPaths[i].Endpoints[j]); if(duration == nil or duration == "") then duration = " "; end; -- Because column is right justified text = text..color.." "..duration; connectionCost[FlightPath_Config.FlightPaths[i].Endpoints[which]] = text; routeFound = true; end end databaseIsEmpty = false; end end end table.sort(Connections); FP_ClearDialogScreen(); if (routeFound) then local text; FP_UIStaticConnections:SetText(WHITE.."Connections"); FP_UIStaticZone:SetText(WHITE.."Zone"); for i in Connections do if(i > MAX_CONNECTIONS) then FPCHAT("Maximum number of connections ("..MAX_CONNECTIONS..") exceeded for zone "..zone.."."); break; end text = getglobal("FP_Connection"..i); text:SetText(connectionColor[Connections[i]]..Connections[i]); text = getglobal("FP_ConnectionCost"..i); text:SetText(connectionCost[Connections[i]]); end local tcity, tzone = FP_ParseLocation(tlocation); tlocation = tzone; if(tcity ~= "") then tlocation = tlocation.." - "..tcity; end UIDropDownMenu_SetText(tlocation,FP_UIZoneDropDown); FP_UIErrorText:SetText(""); PopulatePass = 0; else UIDropDownMenu_SetText("",FP_UIZoneDropDown); if (databaseIsEmpty) then -- If we don't know any flight paths at all display a reassuring message to the user text = WHITE.. "FlightPath has not yet learned any flight paths for the "..UnitFactionGroup("player")..".\n\n".. "As you travel around Azeroth, FlightPath will learn the flight paths available to you.\n\n".. "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".. "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."; FP_UIErrorText:SetText(text); return; else if((PopulatePass == 1) and (repopulateLoc ~= "")) then -- Didn't match anything first time through FP_DisplayDialogConnections(repopulateLoc); PopulatePass = 0; end end end end --------------------------------------------------------------------- function FP_ClearDialogScreen() local button; FP_UIErrorText:SetText(""); FP_UIStaticConnections:SetText(""); for i = 1, MAX_CONNECTIONS, 1 do button = getglobal("FP_Connection"..i); button:SetText(""); button = getglobal("FP_ConnectionCost"..i); button:SetText(""); end FP_DialogHeading:SetText(BRIGHTGREY..BINDING_HEADER_FPHEADER); FP_DialogVersion:SetText(GREY.."Version "..VERSION); end --------------------------------------------------------------------- function FP_UIFrame_OnShow() -- UIDropDownMenu_Initialize(FP_UIZoneDropDown, FP_UIZoneDropDown_Initialize); end --------------------------------------------------------------------- function FP_Clicked(leftOrRight, buttonID) -- Process left mouse button click local index = 0; local tzone, tcity; if (tonumber(buttonID) >= CONNECTION_START_ID) then index = buttonID - CONNECTION_START_ID + 1; if(index <= table.getn(Connections)) then if ( leftOrRight == "LeftButton" ) then FP_DisplayDialogConnections(Connections[index]); else local continent, zone = GetMapNumbers(Connections[index]); if((continent ~= 0) and (zone ~= 0)) then -- Display appropriate map on right click ToggleWorldMap(); SetMapZoom(continent,zone); end end end else FPCHAT("FlightPath received a click from unknown button "..buttonID..""); end return; end --------------------------------------------------------------------- function FP_UserKnowsPath(path) if(path.KnownBy == nil) then return false; end; for i in path.KnownBy do if(path.KnownBy[i] == "ALL") then return true; end; if(path.KnownBy[i] == FP_GetNameServer()) then return true; end; end return false; end --------------------------------------------------------------------- function GetMapNumbers(location) local continent, zone; for i in FlightPath_Config.FlightPaths do for j = 1, 2, 1 do if( FP_IsSameLocation(location,FlightPath_Config.FlightPaths[i].Endpoints[j])) then continent = FlightPath_Config.FlightPaths[i].Continent[j]; zone = FlightPath_Config.FlightPaths[i].Zone[j]; if(continent ~= 0 and zone ~= 0) then return continent, zone; end end end end return 0,0; end --------------------------------------------------------------------- function FP_Status() if (FlightPath_Config.Enabled) then FPCHAT(BINDING_HEADER_FPHEADER.." version "..VERSION.." is enabled."); else FPCHAT(BINDING_HEADER_FPHEADER.." version "..VERSION.." is disabled."); end FP_ReportBindingKey(); if(FlightPath_Config.HideRemaining) then FPCHAT("The in flight time remaining counter is disabled."); end; local x, y = FP_GetPlayerCoords(); FPCHAT("You are now at location "..x..","..y.." in "..FP_GetLocale()); if (UnitOnTaxi("player")) then FPCHAT("You are currently flying from "..TaxiOrigin.." to "..TaxiDestination); end local flightpaths = FP_GetRecordedFlightpaths(); if (flightpaths) then faction = UnitFactionGroup("player") local factionRoutes = 0 for i in FlightPath_Config.FlightPaths do if (FlightPath_Config.FlightPaths[i].Faction == faction) then factionRoutes = factionRoutes + 1; end end FPCHAT("FlightPath knows of "..factionRoutes.." "..faction.." routes between "..table.getn(flightpaths).." locations."); end end --------------------------------------------------------------------- function FP_ReportBindingKey() if(GetBindingKey("FPDIALOG")) then FPCHAT("The FlightPath dialog is bound to key "..GetBindingKey("FPDIALOG")); end end --------------------------------------------------------------------- -- -- World Map logic -- --------------------------------------------------------------------- function FP_Round(value) return math.floor(value*10000.0)/10000.0; end --------------------------------------------------------------------- function FP_GetPOITooltipText(location) local leftSide = {}; local rightSide = {} local text; local routeFound = false; local city, zone, tcity, tzone, i; if(not location) then city = ""; zone = WorldMapZoneDropDownText:GetText(); else city, zone = FP_ParseLocation(location); end leftSide[1] = location; rightSide[1] = " "; leftSide[2] = " "; rightSide[3] = " "; leftSide[3] = WHITE.."Connections:";rightSide[4] = " "; leftSide[4] = " "; rightSide[5] = " "; local lineNumber = 5; local poiConnections = {}; local poiCosts = {} local unique = {}; local connectionColors = {}; local connection,cost,duration, pline; for i in FlightPath_Config.FlightPaths do if (FlightPath_Config.FlightPaths[i].Faction == UnitFactionGroup("player")) then local color = STATUS_COLOR; if(not FP_UserKnowsPath(FlightPath_Config.FlightPaths[i])) then color = GREY; end if((color ~= GREY) or (not FP_HideGrey())) then tcity, tzone = FP_ParseLocation(FlightPath_Config.FlightPaths[i].Endpoints[1]); if ((((city == "") or (city == tcity)) and (zone == tzone))) then if(unique[FlightPath_Config.FlightPaths[i].Endpoints[2]] == nil) then unique[FlightPath_Config.FlightPaths[i].Endpoints[2]] = true; connection, cost = FP_BuildPOITipLine(FlightPath_Config.FlightPaths[i],2,location, color == GREY) table.insert(poiConnections,connection); poiCosts[connection] = cost; connectionColors[connection] = color; routeFound = true; end end tcity, tzone = FP_ParseLocation(FlightPath_Config.FlightPaths[i].Endpoints[2]); if ((((city == "") or (city == tcity)) and (zone == tzone))) then if(unique[FlightPath_Config.FlightPaths[i].Endpoints[1]] == nil) then unique[FlightPath_Config.FlightPaths[i].Endpoints[1]] = true; connection, cost = FP_BuildPOITipLine(FlightPath_Config.FlightPaths[i],1,location, color == GREY) table.insert(poiConnections,connection); poiCosts[connection] = cost; connectionColors[connection] = color; routeFound = true; end end end end end table.sort(poiConnections); if (routeFound) then for i in poiConnections do leftSide[lineNumber] = connectionColors[poiConnections[i]]..poiConnections[i]; rightSide[lineNumber] = poiCosts[poiConnections[i]]; lineNumber = lineNumber + 1; end else leftSide[lineNumber] = STATUS_COLOR.."No connections found."; rightSide[lineNumber] = " "; end return leftSide,rightSide; end --------------------------------------------------------------------- function FP_BuildPOITipLine(path,index,location,isGrey) local text = ""; local color; local duration = FP_GetDuration(path,location); if(path.Cost ~= nil and path.Cost ~= "") then color = MONEY_COLOR; if(isGrey) then color = GREY; end; text = color..FP_FormatMoney(path.Cost); end if(duration ~= nil and duration ~="") then color = WHITE; if(isGrey) then color = GREY; end; text = text.." "..color..duration; else text = text.." "; end return path.Endpoints[index],text; end --------------------------------------------------------------------- function FP_WorldMapButton_OnUpdate(elapsed) Original_WorldMapButton_OnUpdate(elapsed); -- Call the original owner of the hook so we don't break other mods if(FlightPath_Config.Enabled) then local path; local buttonCount = 0; -- Hide all the POI buttons for i = 1, MAX_POI_BUTTONS, 1 do POI = getglobal("FP_POI"..i); if(POI) then POI:ClearAllPoints(); POI.Location = nil; POI:Hide(); end end -- What map are we looking at? -- Returns 0=world, 1=kalimdor or a zone in kalimdor, 2=ek or a zone in ek local continent = GetCurrentMapContinent(); if(continent ==0) then return; end; -- We don't support the world map yet -- Returns zone index or 0 if showing the entire continent local zone = GetCurrentMapZone(); if(zone == 0) then return; end; -- We don't support the continent map yet -- Create a table of locations to display in this zone local displayable = {}; for i in FlightPath_Config.FlightPaths do if((FlightPath_Config.FlightPaths[i].Faction == UnitFactionGroup("player")) and (FP_UserKnowsPath(FlightPath_Config.FlightPaths[i]) or not FP_HideGrey()) ) then for j = 1, 2, 1 do if(FlightPath_Config.FlightPaths[i].Continent[j] == continent) then if(FlightPath_Config.FlightPaths[i].Zone[j] == zone) then dindex = FlightPath_Config.FlightPaths[i].Endpoints[j]; if(displayable[dindex] == nil) then displayable[dindex] = {}; displayable[dindex].Coords = FlightPath_Config.FlightPaths[i].Coords[j]; end if(FP_UserKnowsPath(FlightPath_Config.FlightPaths[i])) then displayable[dindex].Texture = "Interface\\TaxiFrame\\UI-Taxi-Icon-Yellow"; else if(displayable[dindex].Texture == nil) then displayable[dindex].Texture = "Interface\\TaxiFrame\\UI-Taxi-Icon-Gray"; end end end end end end end -- Create POI's for found flight masters for j in displayable do buttonCount = buttonCount + 1; local POI = getglobal("FP_POI"..buttonCount); local POITexture = getglobal("FP_POI"..buttonCount.."Icon"); local x, y = FP_ParseCoord(displayable[j].Coords); POITexture:SetTexture(displayable[j].Texture); POI:ClearAllPoints(); POI:SetPoint("CENTER", "WorldMapDetailFrame", "TOPLEFT", x/100*WorldMapButton:GetWidth(), -y/100*WorldMapButton:GetHeight()); POI:Show(); -- Add a location field to the POI so we can query it when creating a tooltip POI.Location = j; end end end --------------------------------------------------------------------- function FP_POIOnEnter() -- Called when the mouse hovers over the flight path POI button on the zone map local px, py = this:GetCenter(); local wx, wy = WorldMapButton:GetCenter(); local align = "ANCHOR_LEFT"; if(px <= wx) then align = "ANCHOR_RIGHT"; end WorldMapFrameAreaLabel:SetText(this.Location); WorldMapTooltip:SetOwner(this, align); local leftSide,rightSide = FP_GetPOITooltipText(this.Location); for i in leftSide do if(rightSide[i] ~= nil) then WorldMapTooltip:AddDoubleLine(leftSide[i],rightSide[i]); else WorldMapTooltip:AddLine(leftSide[i]); end end WorldMapTooltip:Show(); end --------------------------------------------------------------------- function FP_POIOnLeave() WorldMapTooltip:Hide(); WorldMapTooltip:SetText(""); end --------------------------------------------------------------------- function FP_TaxiNodeOnButtonEnter(button) local showDuration = false; local buttonLocation = TaxiNodeName(this:GetID()); if(buttonLocation ~= "INVALID") then if(not FP_IsSameLocation(TaxiOrigin,buttonLocation)) then local index,path = FP_FindPath(TaxiOrigin,buttonLocation); if(index ~= 0) then local duration = FP_GetDuration(path,TaxiOrigin); if((duration ~= nil) and (duration ~= "")) then ShoppingTooltip2:SetOwner(GameTooltip, "ANCHOR_BOTTOMRIGHT"); ShoppingTooltip2:ClearAllPoints(); ShoppingTooltip2:SetPoint("TOPLEFT", "GameTooltip", "TOPRIGHT", 0, -10); ShoppingTooltip2:AddLine("Flight time: "..duration, "", 0.5, 1.0, 0.5); showDuration = true; end end end end Original_TaxiNodeOnButtonEnter(button); -- call original handler if(showDuration) then ShoppingTooltip2:Show(); end end --------------------------------------------------------------------- function FP_GetDuration(path,location) if(path.Duration == nil) then return nil; end if(location == path.Endpoints[1]) then return path.Duration[1]; -- Duration is always stored as 'time from' the corresponding endpoint else return path.Duration[2]; end end --------------------------------------------------------------------- function FP_FindPath(location1, location2) for i in FlightPath_Config.FlightPaths do local endpoint1 = FlightPath_Config.FlightPaths[i].Endpoints[1]; local endpoint2 = FlightPath_Config.FlightPaths[i].Endpoints[2]; if((FP_IsSameLocation(location1,endpoint1) and FP_IsSameLocation(location2,endpoint2)) or (FP_IsSameLocation(location1,endpoint2) and FP_IsSameLocation(location2,endpoint1))) then return i,FlightPath_Config.FlightPaths[i]; end end return 0,nil; end --------------------------------------------------------------------- function FP_Debug() FPDebugShow = true; FPDEBUG("Debug output enabled."); end --------------------------------------------------------------------- -- Debug routine to help users debug flightmaster/zone name mismatches. function FP_Check() if(not TaximapOpen) then FPCHAT("Please open up the flight master's connections map then type /fp check again."); return; end -- Find out what flight masters like to call this location local where; local numButtons = NumTaxiNodes(); for i = 1, numButtons, 1 do if(TaxiNodeGetType(i) == "CURRENT") then where = TaxiNodeName(i); break; end end if(FP_IsSameLocation(where, FP_GetLocale())) then FPCHAT("FlightPath was able to match the flight master's name for this location ("..where..") to the map location ("..FP_GetLocale().."). No problems detected."); else 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!!!"); end end --------------------------------------------------------------------- -- Display functions function FPCHAT(text) DEFAULT_CHAT_FRAME:AddMessage(STATUS_COLOR..text); end FPDebugShow = false; function FPDEBUG(...) if(FPDebugShow) then local text = ""; for i = 1, arg.n, 1 do if(i>2) then text = text..", ";end; local value=""; local vtype = type(arg[i]); if (vtype == "nil") then text = text.."(nil)"; elseif(vtype == "number") then text = text..tostring(arg[i]); elseif(vtype == "string") then text = text..arg[i]; elseif(vtype == "boolean") then if(arg[i]) then text = text.."true"; else text = text.."false"; end elseif(vtype == "table" or vtype == "function" or vtype == "thread" or vtype == "userdata") then text = text.."("..vtype..")"; else text = text.."(unknown)";end end DEFAULT_CHAT_FRAME:AddMessage(DEBUG_COLOR.."FPDBG: "..text); end end function FPSPACE(count) return string.rep(" ",count); end --------------------------------------------------------------------- -- Development only --------------------------------------------------------------------- function FP_Test(text) local txt = ""; if(text ~= nil) then txt=text;end; FPDEBUG("In FP_Test("..txt..")..."); -- Begin test code local testForeign = "Grom'Gul, Mitteilung Mu\195\159sein"; local tcity, tzone = FP_ParseLocation(testForeign); FPDEBUG(testForeign.." parsed to city '"..tcity.."' zone='"..tzone.."'"); -- End test code FPDEBUG("...Exited FP_Test"); end