Merge branch 'master' of git://cams.pavlovian.net/questhelper
[QuestHelper.git] / graph_flightpath.lua
blob6883534d42e4954e89ed8b926287020571f85593
1 QuestHelper_File["graph_flightpath.lua"] = "Development Version"
2 QuestHelper_Loadtime["graph_flightpath.lua"] = GetTime()
4 -- Name to Name, gives {time, accurate}
5 QH_Flight_Distances = {}
6 QH_Flight_Destinations = {}
8 local rlevel = nil
9 local cwf = nil
11 function QH_debug_flightpath()
12 print(rlevel, cwf)
13 end
15 function QH_redo_flightpath()
16 QuestHelper: Assert(DB_Ready())
18 local globby
19 if QuestHelper.loading_flightpath then
20 globby = QuestHelper.loading_flightpath
21 else
22 QuestHelper.flightpathing = QuestHelper.CreateLoadingCounter()
23 globby = QuestHelper.flightpathing
24 end
26 local load_earlyload = globby:MakeSubcategory(1)
27 local load_preroll = globby:MakeSubcategory(1)
28 local load_floyd = globby:MakeSubcategory(1)
29 local load_postroll = globby:MakeSubcategory(0.1)
31 load_preroll:SetPercentage(0)
33 -- First, let's figure out if the player can fly.
34 -- The logic we're using: if he has 225 or 300, then he can fly in Outland. If he's got Cold Weather Flying and those levels, he can fly in Northrend.
35 if true then
36 local ridingLevel = (select(4,GetAchievementInfo(892)) and 300) or (select(4,GetAchievementInfo(890)) and 225) or (select(4,GetAchievementInfo(889)) and 150) or (select(4,GetAchievementInfo(891)) and 75) or 0 -- this is thanks to Maldivia, who is a fucking genius
37 local has_cwf = not not GetSpellInfo(GetSpellInfo(54197))
39 local speed
40 local cull
41 if ridingLevel == 225 then
42 speed = 17.5
43 cull = false
44 elseif ridingLevel == 300 then
45 speed = 27
46 cull = true
47 end
49 if ridingLevel >= 225 then
50 QH_Graph_Flyplaneset(3, speed, cull) -- Outland
51 end
53 if ridingLevel >= 225 and has_cwf then
54 QH_Graph_Flyplaneset(4, speed, cull) -- Northrend
55 end
57 rlevel = ridingLevel
58 cwf = has_cwf
59 else
60 QuestHelper:TextOut("Horrible QH hack, mount flight disabled, please inform Zorba if this is in a release version")
61 end
63 local flightids = DB_ListItems("flightmasters")
64 local flightdb = {}
66 local has = {}
67 local has_count = 0
69 local fidcount = 0
70 for k, v in pairs(flightids) do
71 flightdb[v] = DB_GetItem("flightmasters", v, true, true)
72 if QuestHelper_KnownFlightRoutes[flightdb[v].name] then
73 has[k] = true
74 has_count = has_count + 1
75 end
77 fidcount = fidcount + 1
78 load_earlyload:SetPercentage(fidcount / QuestHelper:TableSize(flightids))
79 end
81 local adjacency = {}
83 local important = {}
85 QH_Timeslice_Yield()
87 local has_seen = 0
88 for k, v in pairs(has) do
89 local tdb = DB_GetItem("flightpaths", k, true, true)
90 if tdb then
91 for dest, dat in pairs(tdb) do
92 if has[dest] then
93 for _, route in ipairs(dat) do
94 local passes = true
95 if route.path then for _, intermed in ipairs(route.path) do
96 if not has[intermed] then passes = false break end
97 end end
99 if passes then
100 --QuestHelper:TextOut(string.format("Found link between %s and %s, cost %f", flightdb[k].name, flightdb[dest].name, route.distance))
101 if not adjacency[k] then adjacency[k] = {} end
102 if not adjacency[dest] then adjacency[dest] = {} end
103 QuestHelper: Assert(not (adjacency[k][dest] and adjacency[k][dest].time))
104 adjacency[k][dest] = {time = route.distance, dist = route.distance, original = true}
106 -- no such thing as strongly asymmetric routes
107 -- note that we're only hitting up adjacency here, because we don't have "time info"
108 if not adjacency[dest][k] then
109 adjacency[dest][k] = {dist = route.distance * 1.1, original = true} -- It's original because, in theory, we may end up basing other links on this one. It's still not time-authoritative, though.
112 important[k] = true
113 important[dest] = true
114 break
119 DB_ReleaseItem(tdb)
122 has_seen = has_seen + 1
123 load_preroll:SetPercentage(has_seen / has_count)
126 QH_Timeslice_Yield()
128 local imp_flat = {}
129 local flightmasters = {}
130 for k, v in pairs(important) do
131 table.insert(imp_flat, k)
132 if flightdb[k].mid then
133 local fmx = DB_GetItem("monster", flightdb[k].mid, true, true)
134 if fmx and fmx.loc then
135 flightmasters[k] = QuestHelper:CreateTable("flightmaster cachey")
136 for tk, v in pairs(fmx.loc[1]) do
137 if not tk:match("__.*") then
138 flightmasters[k][tk] = v
139 QuestHelper:Assert(type(tk) ~= "table")
140 QuestHelper:Assert(type(v) ~= "table")
143 else
144 --QuestHelper:TextOut(string.format("Missing flightmaster location for node %d/%s", k, tostring(flightdb[k].name)))
146 if fmx then DB_ReleaseItem(fmx) end
147 else
148 --QuestHelper:TextOut(string.format("Missing flightmaster for node %d/%s", k, tostring(flightdb[k].name)))
151 table.sort(imp_flat)
153 for _, v in ipairs(imp_flat) do
154 adjacency[v] = adjacency[v] or {}
157 for idx, pivot in ipairs(imp_flat) do
158 QH_Timeslice_Yield()
159 for _, i in ipairs(imp_flat) do
160 for _, j in ipairs(imp_flat) do
161 if adjacency[i][pivot] and adjacency[pivot][j] then
162 local cst = adjacency[i][pivot].dist + adjacency[pivot][j].dist
163 if not adjacency[i][j] or adjacency[i][j].dist > cst then
164 if not adjacency[i][j] then adjacency[i][j] = {} end
165 adjacency[i][j].dist = cst
166 adjacency[i][j].original = nil
172 load_floyd:SetPercentage(idx / #imp_flat)
175 QH_Timeslice_Yield()
178 local clustaken = {}
180 for src, t in pairs(adjacency) do
181 if not clustaken[src] then
182 local tcst = {}
183 local tcct = 0
184 local ctd = {}
185 table.insert(ctd, src)
187 while #ctd > 0 do
188 local ite = table.remove(ctd)
189 QuestHelper: Assert(not clustaken[ite] or tcst[ite])
191 if not tcst[ite] then
192 clustaken[ite] = true
193 tcst[ite] = true
194 for _, dst in pairs(imp_flat) do
195 if adjacency[ite][dst] and not tcst[dst] then
196 table.insert(ctd, dst)
200 tcct = tcct + 1
204 --QuestHelper: TextOut(string.format("Starting with %d, cluster of %d", src, tcct))
209 QH_Graph_Plane_Destroylinks("flightpath")
211 -- reset!
212 QH_Flight_Distances = {}
213 QH_Flight_Destinations = {}
215 for src, t in pairs(adjacency) do
216 QH_Timeslice_Yield()
218 for dest, dat in pairs(t) do
220 local fms = flightmasters[src]
221 local fmd = flightmasters[dest]
222 if fms and fmd then
223 local fmsc = QuestHelper_ParentLookup[fms.p]
224 local fmdc = QuestHelper_ParentLookup[fmd.p]
225 QuestHelper: Assert(fmsc == fmdc)
229 if not QH_Flight_Destinations[dest] and flightmasters[dest] then
230 local fmd = flightmasters[dest]
231 QH_Flight_Destinations[flightdb[dest].name] = {x = fmd.x, y = fmd.y, c = QuestHelper_ParentLookup[fmd.p], p = fmd.p}
235 local sname = flightdb[src].name
236 local dname = flightdb[dest].name
238 if not QH_Flight_Distances[sname] then QH_Flight_Distances[sname] = {} end
239 QuestHelper: Assert(not QH_Flight_Distances[sname][dname])
240 QH_Flight_Distances[sname][dname] = {adjacency[src][dest].dist, not adjacency[src][dest].original}
243 if dat.original and not (src > dest and adjacency[dest][src] and adjacency[dest][src].original) then
244 local fms = flightmasters[src]
245 local fmd = flightmasters[dest]
246 if fms and fmd then
247 local snode = {x = fms.x, y = fms.y, c = QuestHelper_ParentLookup[fms.p], p = fms.p, map_desc = {QHFormat("WAYPOINT_REASON", QHFormat("FLIGHT_POINT", flightdb[src].name))}, condense_class = "flightpath"}
248 local dnode = {x = fmd.x, y = fmd.y, c = QuestHelper_ParentLookup[fmd.p], p = fmd.p, map_desc = {QHFormat("WAYPOINT_REASON", QHFormat("FLIGHT_POINT", flightdb[dest].name))}, condense_class = "flightpath"}
250 local ret = adjacency[dest][src] and adjacency[dest][src].original and adjacency[dest][src].dist
251 QH_Graph_Plane_Makelink("flightpath", snode, dnode, dat.dist, ret)
257 for _, v in pairs(flightdb) do
258 DB_ReleaseItem(v)
260 for _, v in pairs(flightmasters) do
261 QuestHelper:ReleaseTable(v)
264 load_postroll:SetPercentage(1)
266 if not QuestHelper.loading_flightpath then
267 QuestHelper.flightpathing = nil
270 QH_Graph_Plane_Refresh()