Merge branch 'master' of git://cams.pavlovian.net/questhelper
[QuestHelper.git] / graph_flightpath.lua
blob5b465b118c0131ce0d3c060cc6f1e9ab6e93ae68
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 function QH_redo_flightpath()
9 QuestHelper: Assert(DB_Ready())
11 local globby
12 if QuestHelper.loading_flightpath then
13 globby = QuestHelper.loading_flightpath
14 else
15 QuestHelper.flightpathing = QuestHelper.CreateLoadingCounter()
16 globby = QuestHelper.flightpathing
17 end
19 local load_earlyload = globby:MakeSubcategory(1)
20 local load_preroll = globby:MakeSubcategory(1)
21 local load_floyd = globby:MakeSubcategory(1)
22 local load_postroll = globby:MakeSubcategory(0.1)
24 load_preroll:SetPercentage(0)
26 -- First, let's figure out if the player can fly.
27 -- 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.
29 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
30 local has_cwf = not not GetSpellInfo(GetSpellInfo(54197))
32 local speed
33 if ridingLevel == 225 then
34 speed = 11
35 elseif ridingLevel == 300 then
36 speed = 27
37 end
39 if ridingLevel >= 225 then
40 QH_Graph_Flyplaneset(3, speed) -- Outland
41 end
43 if ridingLevel >= 225 and has_cwf then
44 QH_Graph_Flyplaneset(4, speed) -- Northrend
45 end
46 end
48 local flightids = DB_ListItems("flightmasters")
49 local flightdb = {}
51 local has = {}
52 local has_count = 0
54 local fidcount = 0
55 for k, v in pairs(flightids) do
56 flightdb[v] = DB_GetItem("flightmasters", v, true, true)
57 if QuestHelper_KnownFlightRoutes[flightdb[v].name] then
58 has[k] = true
59 has_count = has_count + 1
60 end
62 fidcount = fidcount + 1
63 load_earlyload:SetPercentage(fidcount / QuestHelper:TableSize(flightids))
64 end
66 local adjacency = {}
68 local important = {}
70 QH_Timeslice_Yield()
72 local has_seen = 0
73 for k, v in pairs(has) do
74 local tdb = DB_GetItem("flightpaths", k, true, true)
75 if tdb then
76 for dest, dat in pairs(tdb) do
77 if has[dest] then
78 for _, route in ipairs(dat) do
79 local passes = true
80 if route.path then for _, intermed in ipairs(route.path) do
81 if not has[intermed] then passes = false break end
82 end end
84 if passes then
85 --QuestHelper:TextOut(string.format("Found link between %s and %s, cost %f", flightdb[k].name, flightdb[dest].name, route.distance))
86 if not adjacency[k] then adjacency[k] = {} end
87 if not adjacency[dest] then adjacency[dest] = {} end
88 QuestHelper: Assert(not (adjacency[k][dest] and adjacency[k][dest].time))
89 adjacency[k][dest] = {time = route.distance, dist = route.distance, original = true}
91 -- no such thing as strongly asymmetric routes
92 -- note that we're only hitting up adjacency here, because we don't have "time info"
93 if not adjacency[dest][k] then
94 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.
95 end
97 important[k] = true
98 important[dest] = true
99 break
104 DB_ReleaseItem(tdb)
107 has_seen = has_seen + 1
108 load_preroll:SetPercentage(has_seen / has_count)
111 QH_Timeslice_Yield()
113 local imp_flat = {}
114 local flightmasters = {}
115 for k, v in pairs(important) do
116 table.insert(imp_flat, k)
117 if flightdb[k].mid then
118 local fmx = DB_GetItem("monster", flightdb[k].mid, true, true)
119 if fmx.loc then
120 flightmasters[k] = QuestHelper:CreateTable("flightmaster cachey")
121 for tk, v in pairs(fmx.loc[1]) do
122 if not tk:match("__.*") then
123 flightmasters[k][tk] = v
124 QuestHelper:Assert(type(tk) ~= "table")
125 QuestHelper:Assert(type(v) ~= "table")
128 else
129 --QuestHelper:TextOut(string.format("Missing flightmaster location for node %d/%s", k, tostring(flightdb[k].name)))
131 DB_ReleaseItem(fmx)
132 else
133 --QuestHelper:TextOut(string.format("Missing flightmaster for node %d/%s", k, tostring(flightdb[k].name)))
136 table.sort(imp_flat)
138 for _, v in ipairs(imp_flat) do
139 adjacency[v] = adjacency[v] or {}
142 for idx, pivot in ipairs(imp_flat) do
143 QH_Timeslice_Yield()
144 for _, i in ipairs(imp_flat) do
145 for _, j in ipairs(imp_flat) do
146 if adjacency[i][pivot] and adjacency[pivot][j] then
147 local cst = adjacency[i][pivot].dist + adjacency[pivot][j].dist
148 if not adjacency[i][j] or adjacency[i][j].dist > cst then
149 if not adjacency[i][j] then adjacency[i][j] = {} end
150 adjacency[i][j].dist = cst
151 adjacency[i][j].original = nil
157 load_floyd:SetPercentage(idx / #imp_flat)
160 QH_Timeslice_Yield()
163 local clustaken = {}
165 for src, t in pairs(adjacency) do
166 if not clustaken[src] then
167 local tcst = {}
168 local tcct = 0
169 local ctd = {}
170 table.insert(ctd, src)
172 while #ctd > 0 do
173 local ite = table.remove(ctd)
174 QuestHelper: Assert(not clustaken[ite] or tcst[ite])
176 if not tcst[ite] then
177 clustaken[ite] = true
178 tcst[ite] = true
179 for _, dst in pairs(imp_flat) do
180 if adjacency[ite][dst] and not tcst[dst] then
181 table.insert(ctd, dst)
185 tcct = tcct + 1
189 --QuestHelper: TextOut(string.format("Starting with %d, cluster of %d", src, tcct))
194 QH_Graph_Plane_Destroylinks("flightpath")
196 -- reset!
197 QH_Flight_Distances = {}
198 QH_Flight_Destinations = {}
200 for src, t in pairs(adjacency) do
201 QH_Timeslice_Yield()
203 for dest, dat in pairs(t) do
205 local fms = flightmasters[src]
206 local fmd = flightmasters[dest]
207 if fms and fmd then
208 local fmsc = QuestHelper_ParentLookup[fms.p]
209 local fmdc = QuestHelper_ParentLookup[fmd.p]
210 QuestHelper: Assert(fmsc == fmdc)
214 if not QH_Flight_Destinations[dest] and flightmasters[dest] then
215 local fmd = flightmasters[dest]
216 QH_Flight_Destinations[flightdb[dest].name] = {x = fmd.x, y = fmd.y, c = QuestHelper_ParentLookup[fmd.p], p = fmd.p}
220 local sname = flightdb[src].name
221 local dname = flightdb[dest].name
223 if not QH_Flight_Distances[sname] then QH_Flight_Distances[sname] = {} end
224 QuestHelper: Assert(not QH_Flight_Distances[sname][dname])
225 QH_Flight_Distances[sname][dname] = {adjacency[src][dest].dist, not adjacency[src][dest].original}
228 if dat.original and not (src > dest and adjacency[dest][src] and adjacency[dest][src].original) then
229 local fms = flightmasters[src]
230 local fmd = flightmasters[dest]
231 if fms and fmd then
232 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[dest].name))}, condense_class = "flightpath"}
233 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[src].name))}, condense_class = "flightpath"}
235 local ret = adjacency[dest][src] and adjacency[dest][src].original and adjacency[dest][src].dist
236 QH_Graph_Plane_Makelink("flightpath", snode, dnode, dat.dist, ret)
242 for _, v in pairs(flightdb) do
243 DB_ReleaseItem(v)
245 for _, v in pairs(flightmasters) do
246 QuestHelper:ReleaseTable(v)
249 load_postroll:SetPercentage(1)
251 if not QuestHelper.loading_flightpath then
252 QuestHelper.flightpathing = nil