2012-01-01 04:01:18 +01:00
local GetTime = QuestHelper_GetTime
2010-11-08 14:28:59 +01:00
QuestHelper_File [ " flightpath.lua " ] = " 4.0.1.$svnversion$ "
2010-10-24 23:17:33 +02:00
QuestHelper_Loadtime [ " flightpath.lua " ] = GetTime ( )
local real_TakeTaxiNode = TakeTaxiNode
local real_TaxiNodeOnButtonEnter = TaxiNodeOnButtonEnter
--[[ assert(type(real_TakeTaxiNode) == "function") ]]
--[[ assert(type(real_TaxiNodeOnButtonEnter) == "function") ]]
local function LookupName ( x , y )
local best , d2
for i = 1 , NumTaxiNodes ( ) do
local u , v = TaxiNodePosition ( i )
u = u - x
v = v - y
u = u * u + v * v
if not best or u < d2 then
best , d2 = TaxiNodeName ( i ) , u
end
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
return best
end
local function getRoute ( id )
for i = 1 , NumTaxiNodes ( ) do
if GetNumRoutes ( i ) == 0 then
local routes = GetNumRoutes ( id )
if routes and routes > 0 and routes < 100 then
local origin , dest = TaxiNodeName ( i ) , TaxiNodeName ( id )
local path_hash = 0
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if routes > 1 then
local path_str = " "
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
for j = 1 , routes - 1 do
path_str = string.format ( " %s/%s " , path_str , LookupName ( TaxiGetDestX ( id , j ) , TaxiGetDestY ( id , j ) ) )
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
path_hash = QuestHelper : HashString ( path_str )
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
return origin , dest , path_hash
end
end
end
end
local function getSrcDest ( id )
local snode
for i = 1 , NumTaxiNodes ( ) do
if GetNumRoutes ( i ) == 0 then
snode = TaxiNodeName ( i )
break
end
end
local dnode = TaxiNodeName ( id )
return snode , dnode
end
local function getEtaEstimate ( snode , dnode )
local eta , estimate = nil , false
if QH_Flight_Distances [ snode ] and QH_Flight_Distances [ snode ] [ dnode ] then
eta , estimate = unpack ( QH_Flight_Distances [ snode ] [ dnode ] )
end
return eta , estimate
end
TaxiNodeOnButtonEnter = function ( btn , ... )
QuestHelper : Assert ( btn )
local rv = real_TaxiNodeOnButtonEnter ( btn , ... )
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if QuestHelper_Pref.flight_time then
local index = btn : GetID ( )
if TaxiNodeGetType ( index ) == " REACHABLE " then
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
local snode , dnode = getSrcDest ( index )
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
local eta , estimate = getEtaEstimate ( snode , dnode )
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if eta then -- Going to replace the tooltip.
GameTooltip : SetOwner ( btn , " ANCHOR_RIGHT " )
GameTooltip : ClearLines ( )
GameTooltip : AddLine ( TaxiNodeName ( index ) , " " , 1.0 , 1.0 , 1.0 )
GameTooltip : AddDoubleLine ( QHText ( " TRAVEL_ESTIMATE " ) , ( estimate and " |cffffffff≈|r " or " " ) .. QHFormat ( " TRAVEL_ESTIMATE_VALUE " , eta ) )
local cost = TaxiNodeCost ( index )
if cost > 0 then
SetTooltipMoney ( GameTooltip , cost )
end
GameTooltip : Show ( )
end
end
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
return rv
end
TakeTaxiNode = function ( id )
local src , dest = getSrcDest ( id )
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if src then
local flight_data = QuestHelper.flight_data
if not flight_data then
flight_data = QuestHelper : CreateTable ( )
QuestHelper.flight_data = flight_data
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
flight_data.src = src
flight_data.dest = dest
flight_data.start_time = nil
flight_data.end_time = nil
flight_data.end_time_estimate = nil
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
real_TakeTaxiNode ( id )
end
function QuestHelper : getFlightInstructor ( area )
local fi_table = QuestHelper_FlightInstructors_Local [ self.faction ]
if fi_table then
local npc = fi_table [ area ]
if npc then
return npc
end
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
local static = QuestHelper_StaticData [ QuestHelper_Locale ]
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if static then
fi_table = static.flight_instructors and static.flight_instructors [ self.faction ]
if fi_table then
return fi_table [ area ]
end
end
end
local function getTime ( tbl , orig , dest , hash )
tbl = tbl and tbl [ orig ]
tbl = tbl and tbl [ dest ]
return tbl and tbl [ hash ] ~= true and tbl [ hash ]
end
-- Okay, I think I've figured out what this is. Given fi1 and fi2, the standard horrifying "canonical/fallback" stuff that all this code does . . .
-- For each pair of "origin/dest" in tbl, determine if there is a direct path. (If there is, the hash will be 0.)
-- If so, find the flightpath distance and the "walking" distance. Add up walking and flightpath separately, and return the sums.
local function getWalkToFlight ( tbl , fi1 , fi2 )
local f , w = 0 , 0
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if tbl then
for origin , list in pairs ( tbl ) do
for dest , hashlist in pairs ( list ) do
if type ( hashlist [ 0 ] ) == " number " then
local npc1 , npc2 = ( fi1 and fi1 [ origin ] ) or ( fi2 and fi2 [ origin ] ) , ( fi1 and fi1 [ dest ] ) or ( fi2 and fi2 [ dest ] )
if npc1 and npc2 then
local obj1 , obj2 = QuestHelper : GetObjective ( " monster " , npc1 ) , QuestHelper : GetObjective ( " monster " , npc2 )
obj1 : PrepareRouting ( true , { failable = true } )
obj2 : PrepareRouting ( true , { failable = true } )
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
local pos1 , pos2 = obj1 : Position ( ) , obj2 : Position ( )
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if pos1 and pos2 then
local x , y = pos1 [ 3 ] - pos2 [ 3 ] , pos1 [ 4 ] - pos2 [ 4 ]
w = w + math.sqrt ( x * x + y * y )
f = f + hashlist [ 0 ]
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
obj2 : DoneRouting ( )
obj1 : DoneRouting ( )
end
end
end
end
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
return f , w
end
-- Determines the general multiple faster than flying is than walking.
function QuestHelper : computeWalkToFlightMult ( )
local l = QuestHelper_FlightRoutes_Local [ self.faction ]
local s = QuestHelper_StaticData [ self.locale ]
s = s and s.flight_routes
s = s and s [ self.faction ]
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
local fi1 = QuestHelper_FlightInstructors_Local [ self.faction ]
local fi2 = QuestHelper_StaticData [ self.locale ]
fi2 = fi2 and fi2.flight_instructors
fi2 = fi2 and fi2 [ self.faction ]
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
local f1 , w1 = getWalkToFlight ( l , fi1 , fi2 )
local f2 , w2 = getWalkToFlight ( s , fi1 , fi2 )
return ( f1 + f2 + 0.032876 ) / ( w1 + w2 + 0.1 )
end
function QuestHelper : computeLinkTime ( origin , dest , hash , fallback )
-- Only works for directly connected flight points.
if origin == dest then
return 0
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
local l = QuestHelper_FlightRoutes_Local [ self.faction ]
local s = QuestHelper_StaticData [ self.locale ]
s = s and s.flight_routes
s = s and s [ self.faction ]
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
hash = hash or 0
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
-- Will try to lookup flight time there, failing that, will use the time from there to here.
local t = getTime ( l , origin , dest , hash ) or getTime ( s , origin , dest , hash ) or
getTime ( l , dest , origin , hash ) or getTime ( s , dest , origin , hash ) or fallback
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if t == nil then -- Don't have any recored information on this flight time, will estimate based on distances.
l = QuestHelper_FlightInstructors_Local [ self.faction ]
s = QuestHelper_StaticData [ self.locale ]
s = s and s.flight_instructors
s = s and s [ self.faction ]
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
local npc1 , npc2 = ( l and l [ origin ] ) or ( s and s [ origin ] ) ,
( l and l [ dest ] ) or ( s and s [ dest ] )
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if npc1 and npc2 then
local obj1 , obj2 = self : GetObjective ( " monster " , npc1 ) , self : GetObjective ( " monster " , npc2 )
obj1 : PrepareRouting ( true )
obj2 : PrepareRouting ( true )
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
local pos1 , pos2 = obj1 : Position ( ) , obj2 : Position ( )
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if pos1 and pos2 then
local x , y = pos1 [ 3 ] - pos2 [ 3 ] , pos1 [ 4 ] - pos2 [ 4 ]
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
t = math.sqrt ( x * x + y * y ) * self.flight_scalar
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
obj2 : DoneRouting ( )
obj1 : DoneRouting ( )
end
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if t and type ( t ) ~= " number " then
QuestHelper : AppendNotificationError ( " 2008-10-11 computelinktime is not a number " , string.format ( " %s %s " , type ( t ) , fallback and type ( fallback ) or " (nil) " ) )
return nil
end
return t
end
local moonglade_fp = nil
function QuestHelper : addLinkInfo ( data , flight_times )
if data then
if select ( 2 , UnitClass ( " player " ) ) ~= " DRUID " then
-- As only druids can use the flight point in moonglade, we need to figure out
-- where it is so we can ignore it.
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if not moonglade_fp then
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
local fi_table = QuestHelper_FlightInstructors_Local [ self.faction ]
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if fi_table then for area , npc in pairs ( fi_table ) do
local npc_obj = self : GetObjective ( " monster " , npc )
npc_obj : PrepareRouting ( true , { failable = true } )
local pos = npc_obj : Position ( )
if pos and QuestHelper_IndexLookup [ pos [ 1 ] . c ] [ pos [ 1 ] . z ] == 20 and string.find ( area , " , " ) then -- I'm kind of guessing here
moonglade_fp = area
npc_obj : DoneRouting ( )
break
end
npc_obj : DoneRouting ( )
end end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if not moonglade_fp then
fi_table = QuestHelper_StaticData [ QuestHelper_Locale ]
fi_table = fi_table and fi_table.flight_instructors and fi_table.flight_instructors [ self.faction ]
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if fi_table then for area , npc in pairs ( fi_table ) do
local npc_obj = self : GetObjective ( " monster " , npc )
npc_obj : PrepareRouting ( true , { failable = true } )
local pos = npc_obj : Position ( )
if pos and QuestHelper_IndexLookup [ pos [ 1 ] . c ] [ pos [ 1 ] . z ] == 20 and string.find ( area , " , " ) then
moonglade_fp = area
npc_obj : DoneRouting ( )
break
end
npc_obj : DoneRouting ( )
end end
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if not moonglade_fp then
-- This will always be unknown for the session, even if you call buildFlightTimes again
-- but if it's unknown then you won't be able to
-- get the waypoint this session since you're not a druid
-- so its all good.
moonglade_fp = " unknown "
end
end
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
for origin , list in pairs ( data ) do
local tbl = flight_times [ origin ]
if not tbl then
tbl = self : CreateTable ( " Flightpath AddLinkInfo origin table " )
flight_times [ origin ] = tbl
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
for dest , hashs in pairs ( list ) do
if origin ~= moonglade_fp and QuestHelper_KnownFlightRoutes [ dest ] and hashs [ 0 ] then
local tbl2 = tbl [ dest ]
if not tbl2 then
local t = self : computeLinkTime ( origin , dest )
if t then
tbl2 = self : CreateTable ( " Flightpath AddLinkInfo origin->dest data table " )
tbl [ dest ] = tbl2
tbl2 [ 1 ] = t
tbl2 [ 2 ] = dest
end
end
end
end
end
end
end
local visited = { }
local function getDataTime ( ft , origin , dest )
local str = nil
local data = ft [ origin ] [ dest ]
local t = data [ 1 ]
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
for key in pairs ( visited ) do visited [ key ] = nil end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
while true do
local n = data [ 2 ]
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
-- We might be asked about a route that visits the same point multiple times, and
-- since this is effectively a linked list, we need to check for this to avoid
-- infinite loops.
if visited [ n ] then return end
visited [ n ] = true
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
local temp = QuestHelper : computeLinkTime ( origin , n , str and QuestHelper : HashString ( str ) or 0 , false )
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if temp then
t = temp + ( n == dest and 0 or ft [ n ] [ dest ] [ 1 ] )
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if n == dest then break end
str = string.format ( " %s/%s " , str or " " , n )
data = ft [ n ] [ dest ]
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
return t
end
-- Used for loading status results. This is a messy solution.
QuestHelper_Flight_Updates = 0
QuestHelper_Flight_Updates_Current = 0
function QuestHelper : buildFlightTimes ( )
self.flight_scalar = self : computeWalkToFlightMult ( )
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
local flight_times = self.flight_times
if not flight_times then
flight_times = self : CreateTable ( )
self.flight_times = flight_times
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
for key , list in pairs ( flight_times ) do
self : ReleaseTable ( list )
flight_times [ key ] = nil
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
local l = QuestHelper_FlightRoutes_Local [ self.faction ]
local s = QuestHelper_StaticData [ self.locale ]
s = s and s.flight_routes
s = s and s [ self.faction ]
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
self : addLinkInfo ( l , flight_times )
self : addLinkInfo ( s , flight_times )
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
QuestHelper_Flight_Updates_Current = 0
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
-- This appears to set up flight_times so it gives directions from any node to any other node. I'm not sure what the getDataTime() call is all about, and I'm also not sure what dat[2] is for. In any case, I don't see anything immediately suspicious about this, just dubious.
local cont = true
while cont do
cont = false
local origin = nil
while true do
origin = next ( flight_times , origin )
if not origin then break end
local list = flight_times [ origin ]
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
for dest , data in pairs ( list ) do
QuestHelper_Flight_Updates_Current = QuestHelper_Flight_Updates_Current + 1
if flight_times [ dest ] then for dest2 , data2 in pairs ( flight_times [ dest ] ) do
if dest2 ~= origin then
local dat = list [ dest2 ]
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if not dat then
dat = self : CreateTable ( )
dat [ 1 ] , dat [ 2 ] = data [ 1 ] + data2 [ 1 ] , dest
list [ dest2 ] = dat
dat [ 1 ] = getDataTime ( flight_times , origin , dest2 )
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if not dat [ 1 ] then
self : ReleaseTable ( dat )
list [ dest2 ] = nil
else
cont = true
end
else
local o1 , o2 = dat [ 1 ] , dat [ 2 ] -- Temporarly replace old data for the sake of looking up its time.
if o2 ~= dest then
dat [ 1 ] , dat [ 2 ] = data [ 1 ] + data2 [ 1 ] , dest
local t2 = getDataTime ( flight_times , origin , dest2 )
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if t2 and t2 < o1 then
dat [ 1 ] = t2
cont = true
else
dat [ 1 ] , dat [ 2 ] = o1 , o2
end
end
end
end
end end
QH_Timeslice_Yield ( )
end
end
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
QuestHelper_Flight_Updates = QuestHelper_Flight_Updates_Current
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
-- Replace the tables with simple times.
for orig , list in pairs ( flight_times ) do
for dest , data in pairs ( list ) do
local t = data [ 1 ]
self : ReleaseTable ( data )
list [ dest ] = t
end
end
end
function QuestHelper : taxiMapOpened ( )
for i = 1 , NumTaxiNodes ( ) do
local name = TaxiNodeName ( i )
if not QuestHelper_KnownFlightRoutes [ name ] then
QuestHelper_KnownFlightRoutes [ name ] = true
self : TextOut ( " New flight master: " .. name )
QH_Route_FlightPathRecalc ( )
end
end
end
local elapsed = 0
local function flight_updater ( frame , delta )
elapsed = elapsed + delta
if elapsed > 1 then
2011-04-28 03:40:23 +02:00
frame : SetSize ( 150 , 35 )
2010-10-24 23:17:33 +02:00
elapsed = elapsed - 1
local data = QuestHelper.flight_data
if data then
frame : SetText ( string.format ( " %s: %s " , QuestHelper : HighlightText ( select ( 3 , string.find ( data.dest , " ^(.-), " ) ) or data.dest ) ,
QuestHelper : TimeString ( math.max ( 0 , data.end_time_estimate - time ( ) ) ) ) )
else
frame : Hide ( )
QH_Hook ( frame , " OnUpdate " , nil )
end
end
end
function QuestHelper : flightBegan ( )
if self.flight_data and not self.flight_data . start_time then
2012-01-01 03:22:56 +01:00
self.flight_data . start_time = GetTime ( )
2010-10-24 23:17:33 +02:00
local src , dest = self.flight_data . src , self.flight_data . dest
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
local eta , estimate = getEtaEstimate ( src , dest )
2013-01-03 03:50:16 +01:00
--[[ NOTE: MUST CONVERT TO M(ap), F(loor) coordinate system before enabling again.
2010-10-24 23:17:33 +02:00
local npc = self : getFlightInstructor ( self.flight_data . dest ) -- Will inform QuestHelper that we're going to be at this NPC in whenever.
if npc then
local npc_obj = self : GetObjective ( " monster " , npc )
npc_obj : PrepareRouting ( true )
local pos = npc_obj : Position ( )
if pos then
local c , z = pos [ 1 ] . c , pos [ 1 ] . z
local x , y = self.Astrolabe : TranslateWorldMapPosition ( c , 0 ,
pos [ 3 ] / self.continent_scales_x [ c ] ,
pos [ 4 ] / self.continent_scales_y [ c ] , c , z )
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
self : SetTargetLocation ( QuestHelper_IndexLookup [ c ] [ z ] , x , y , eta )
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
end
npc_obj : DoneRouting ( )
end ] ]
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
do
local loc = QH_Flight_Destinations [ dest ]
if loc then -- sometimes we just don't have a loc, I think due to flightpath recalculations going on right then
QuestHelper.routing_ac , QuestHelper.routing_ax , QuestHelper.routing_ay , QuestHelper.routing_c , QuestHelper.routing_z = QuestHelper_ParentLookup [ loc.p ] , loc.x , loc.y , QuestHelper_ZoneLookup [ loc.p ] [ 1 ] , QuestHelper_ZoneLookup [ loc.p ] [ 2 ]
end
end
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
if eta and QuestHelper_Pref.flight_time then
self.flight_data . end_time_estimate = time ( ) + eta
self : PerformCustomSearch ( flight_updater ) -- Reusing the search status indicator to display ETA for flight.
end
end
end
function QuestHelper : flightEnded ( interrupted )
local flight_data = self.flight_data
if flight_data and not flight_data.end_time then
2012-01-01 03:22:56 +01:00
flight_data.end_time = GetTime ( )
2013-01-03 03:50:16 +01:00
2010-10-24 23:17:33 +02:00
self : UnsetTargetLocation ( )
self : StopCustomSearch ( )
end
end