1 QuestHelper_File
["pathfinding.lua"] = "Development Version"
2 QuestHelper_Loadtime
["pathfinding.lua"] = GetTime()
4 local IRONFORGE_PORTAL
= {25,0.255,0.084, "Ironforge portal site"}
5 local STORMWIND_CITY_PORTAL
= QuestHelper_ConvertCoordsToWrath({36,0.387,0.802, "Stormwind City portal site"}, true) -- Old pre-Wrath coordinates. I could fix it, but . . . meh.
6 local DARNASSUS_PORTAL
= {21,0.397,0.824, "Darnassus portal site"}
7 local EXODAR_PORTAL
= {12,0.476,0.598, "Exodar portal site"}
9 local SHATTRATH_CITY_PORTAL
= {60,0.530,0.492, "Shattrath City portal site"}
10 local DALARAN_PORTAL
= {67,0.500,0.394, "Dalaran portal site"}
11 local MOONGLADE_PORTAL
= {20,0.563,0.320, "Moonglade portal site"}
13 local SILVERMOON_CITY_PORTAL
= {52,0.583,0.192, "Silvermoon City portal site"}
14 local UNDERCITY_PORTAL
= {45,0.846,0.163, "Undercity portal site"}
15 local ORGRIMMAR_PORTAL
= {1,0.386,0.859, "Orgrimmar portal site"}
16 local THUNDER_BLUFF_PORTAL
= {23,0.222,0.168, "Thunder Bluff portal site"}
18 local static_horde_routes
=
20 {{7, 0.505, 0.124}, {38, 0.313, 0.303}, 210}, -- Durotar <--> Grom'gol Base Camp
21 {{38, 0.316, 0.289}, {43, 0.621, 0.591}, 210}, -- Grom'gol Base Camp <--> Tirisfal Glades
22 {{43, 0.605, 0.587}, {7, 0.509, 0.141}, 210}, -- Tirisfal Glades <--> Durotar
23 {{45, 0.549, 0.11}, {52, 0.495, 0.148}, 60}, -- Undercity <--> Silvermoon City
25 {{7, 0.413, 0.178}, {65, 0.414, 0.536}, 210}, -- Durotar <--> Warsong Hold
26 {{43, 0.591, 0.590}, {70, 0.777, 0.283}, 210}, -- Tirisfal Glades <--> Vengeance Landing
28 {{60, 0.592, 0.483}, SILVERMOON_CITY_PORTAL
, 60, true, nil, "SILVERMOON_CITY_PORTAL"}, -- Shattrath City --> Silvermoon City
29 {{60, 0.528, 0.531}, THUNDER_BLUFF_PORTAL
, 60, true, nil, "THUNDER_BLUFF_PORTAL"}, -- Shattrath City --> Thunder Bluff
30 {{60, 0.522, 0.529}, ORGRIMMAR_PORTAL
, 60, true, nil, "ORGRIMMAR_PORTAL"}, -- Shattrath City --> Orgrimmar
31 {{60, 0.517, 0.525}, UNDERCITY_PORTAL
, 60, true, nil, "UNDERCITY_PORTAL"}, -- Shattrath City --> Undercity
33 {{67, 0.583, 0.216}, SILVERMOON_CITY_PORTAL
, 60, true, nil, "SILVERMOON_CITY_PORTAL"}, -- Dalaran --> Silvermoon City
34 {{67, 0.573, 0.219}, THUNDER_BLUFF_PORTAL
, 60, true, nil, "THUNDER_BLUFF_PORTAL"}, -- Dalaran --> Thunder Bluff
35 {{67, 0.553, 0.255}, ORGRIMMAR_PORTAL
, 60, true, nil, "ORGRIMMAR_PORTAL"}, -- Dalaran --> Orgrimmar
36 {{67, 0.556, 0.238}, UNDERCITY_PORTAL
, 60, true, nil, "UNDERCITY_PORTAL"}, -- Dalaran --> Undercity
37 {{67, 0.563, 0.226}, SHATTRATH_CITY_PORTAL
, 60, true, nil, "SHATTRATH_CITY_PORTAL"}, -- Dalaran --> Shatt
41 local static_alliance_routes
=
43 {{36, 0.639, 0.083}, {25, 0.764, 0.512}, 180}, -- Deeprun Tram
44 {{10, 0.718, 0.565}, {51, 0.047, 0.636}, 210}, -- Theramore Isle <--> Menethil Harmor
45 {{36, 0.228, 0.560}, {16, 0.323, 0.441}, 210}, -- Stormwind City <--> Auberdine
47 {{36, 0.183, 0.255}, {65, 0.597, 0.694}, 210}, -- Stormwind City <--> Valiance Keep
48 {{51, 0.047, 0.571}, {70, 0.612, 0.626}, 210}, -- Menethil <--> Daggercap Bay
50 {{60, 0.558, 0.366}, STORMWIND_CITY_PORTAL
, 60, true, nil, "STORMWIND_CITY_PORTAL"}, -- Shattrath City --> Stormwind City
51 {{60, 0.563, 0.37}, IRONFORGE_PORTAL
, 60, true, nil, "IRONFORGE_PORTAL"}, -- Shattrath City --> Ironforge
52 {{60, 0.552, 0.364}, DARNASSUS_PORTAL
, 60, true, nil, "DARNASSUS_PORTAL"}, -- Shattrath City --> Darnassus
53 {{60, 0.596, 0.467}, EXODAR_PORTAL
, 60, true, nil, "EXODAR_PORTAL"}, -- Shattrath City --> Exodar
55 {{67, 0.401, 0.628}, STORMWIND_CITY_PORTAL
, 60, true, nil, "STORMWIND_CITY_PORTAL"}, -- Dalaran --> Stormwind City
56 {{67, 0.395, 0.640}, IRONFORGE_PORTAL
, 60, true, nil, "IRONFORGE_PORTAL"}, -- Dalaran --> Ironforge
57 {{67, 0.389, 0.651}, DARNASSUS_PORTAL
, 60, true, nil, "DARNASSUS_PORTAL"}, -- Dalaran --> Darnassus
58 {{67, 0.382, 0.664}, EXODAR_PORTAL
, 60, true, nil, "EXODAR_PORTAL"}, -- Dalaran --> Exodar
59 {{67, 0.371, 0.667}, SHATTRATH_CITY_PORTAL
, 60, true, nil, "SHATTRATH_CITY_PORTAL"}, -- Dalaran --> Shatt
62 local static_shared_routes
=
64 {{11, 0.638, 0.387}, {38, 0.257, 0.73}, 210}, -- Ratchet <--> Booty Bay
65 {{40, 0.318, 0.503}, {32, 0.347, 0.84}, 130}, -- Burning Steppes <--> Searing Gorge
67 -- More Alliance routes than anything, but without them theres no valid path to these areas for Horde characters.
68 {{24, 0.559, 0.896}, {21, 0.305, 0.414}, 5}, -- Rut'Theran Village <--> Darnassus
69 {{16, 0.332, 0.398}, {24, 0.548, 0.971}, 210}, -- Auberdine <--> Rut'Theran Village
70 {{16, 0.306, 0.409}, {3, 0.2, 0.546}, 210}, -- Auberdine <--> Azuremyst Isle
72 -- Route to new zone. Not valid, exists only to keep routing from exploding if you don't have the flight routes there.
73 {{41, 0.5, 0.5}, {64, 0.5, 0.5}, 7200}, -- Eversong Woods <--> Sunwell
75 {{70, 0.235, 0.578}, {68, 0.496, 0.784}, 210}, -- Kamagua <--> Moa'ki
76 {{65, 0.789, 0.536}, {68, 0.480, 0.787}, 210}, -- Unu'pe <--> Moa'ki
77 {{67, 0.559, 0.467}, {66, 0.158, 0.428}, 5, true}, -- Dalaran --> Violet Stand
78 {{66, 0.157, 0.425}, {67, 0.559, 0.468}, 5, true}, -- Violent Stand --> Dalaran (slightly different coordinates, may be important once solid walls are in)
80 {{34, 0.817, 0.461}, {78, 0.492, 0.312}, 86400}, -- EPL Ebon Hold <--> Scarlet Enclave Ebon Hold. Exists solely to fix some pathing crashes. 24-hour boat ride :D
82 {{67, 0.256, 0.515}, {8, 0.659, 0.498}, 5, true}, -- Dalaran --> CoT
84 -- Wrath instance entrances
85 {{80, 0.693, 0.730}, {70, 0.573, 0.467}, 5}, -- UK
86 {{86, 0.362, 0.880}, {65, 0.275, 0.260}, 5}, -- Nexus
87 {{92, 0.094, 0.933}, {68, 0.260, 0.508}, 5}, -- AN
88 {{94, 0.900, 0.791}, {68, 0.285, 0.517}, 5}, -- AK
89 {{96, 0.294, 0.810}, {75, 0.286, 0.869}, 5}, -- Draktharon
90 {{100, 0.469, 0.780}, {67, 0.679, 0.694}, 5}, -- VH
91 {{102, 0.590, 0.309}, {75, 0.764, 0.214}, 5}, -- Gundrak NW
92 {{102, 0.344, 0.312}, {75, 0.810, 0.286}, 5}, -- Gundrak SE
93 {{104, 0.344, 0.362}, {73, 0.397, 0.269}, 5}, -- HoS
94 {{106, 0.020, 0.538}, {73, 0.453, 0.216}, 5}, -- HoL
95 {{110, 0.613, 0.476}, {65, 0.275, 0.266}, 5}, -- Oculus
96 {{120, 0.875, 0.712}, {8, 0.614, 0.626}, 5}, -- CoT
97 {{124, 0.445, 0.161}, {70, 0.573, 0.467}, 5}, -- UP
98 {{126, 0.500, 0.500}, {74, 0.500, 0.115}, 5}, -- VoA, zone-in link is incorrect
99 {{128, 0.500, 0.500}, {68, 0.873, 0.510}, 5}, -- Naxx, zone-in link is incorrect (but might be close)
100 {{140, 0.635, 0.501}, {68, 0.600, 0.566}, 5}, -- Sarth
101 {{142, 0.500, 0.500}, {65, 0.275, 0.267}, 5}, -- Malygos, zone-in link is incorrect (not that it matters with malygos)
102 {{144, 0.500, 0.500}, {73, 0.416, 0.179}, 5}, -- Ulduar, zone-in link is incorrect
104 -- Wrath in-zone links, all currently incorrect
106 {{80, 0.500, 0.500}, {82, 0.500, 0.500}, 5},
107 {{80, 0.500, 0.500}, {84, 0.500, 0.500}, 5},
110 {{88, 0.500, 0.500}, {90, 0.500, 0.500}, 5},
111 {{88, 0.500, 0.500}, {92, 0.500, 0.500}, 5},
114 {{96, 0.500, 0.500}, {98, 0.500, 0.500}, 5},
117 {{106, 0.500, 0.500}, {108, 0.500, 0.500}, 5},
120 {{110, 0.500, 0.500}, {112, 0.500, 0.500}, 5},
121 {{110, 0.500, 0.500}, {114, 0.500, 0.500}, 5},
122 {{110, 0.500, 0.500}, {116, 0.500, 0.500}, 5},
125 {{120, 0.500, 0.500}, {118, 0.500, 0.500}, 5},
128 {{122, 0.500, 0.500}, {124, 0.500, 0.500}, 5},
131 {{128, 0.500, 0.500}, {130, 0.500, 0.500}, 5},
132 {{128, 0.500, 0.500}, {132, 0.500, 0.500}, 5},
133 {{128, 0.500, 0.500}, {134, 0.500, 0.500}, 5},
134 {{128, 0.500, 0.500}, {136, 0.500, 0.500}, 5},
135 {{128, 0.500, 0.500}, {138, 0.500, 0.500}, 5},
138 {{144, 0.500, 0.500}, {146, 0.500, 0.500}, 5},
139 {{144, 0.500, 0.500}, {148, 0.500, 0.500}, 5},
140 {{144, 0.500, 0.500}, {150, 0.500, 0.500}, 5},
141 {{144, 0.500, 0.500}, {152, 0.500, 0.500}, 5},
144 -- Darkportal is handled specially, depending on whether or not you're level 58+ or not.
145 local dark_portal_route
= {{33, 0.587, 0.599}, {56, 0.898, 0.502}, 60}
147 local static_zone_transitions
=
149 {2, 11, 0.687, 0.872}, -- Ashenvale <--> The Barrens
150 {2, 6, 0.423, 0.711}, -- Ashenvale <--> Stonetalon Mountains
151 {2, 15, 0.954, 0.484}, -- Ashenvale <--> Azshara
152 {2, 16, 0.289, 0.144}, -- Ashenvale <--> Darkshore
153 {2, 13, 0.557, 0.29}, -- Ashenvale <--> Felwood
154 {21, 24, 0.894, 0.358}, -- Darnassus <--> Teldrassil
155 {22, 11, 0.697, 0.604}, -- Mulgore <--> The Barrens
156 {22, 23, 0.376, 0.33}, -- Mulgore <--> Thunder Bluff
157 {22, 23, 0.403, 0.193}, -- Mulgore <--> Thunder Bluff
158 {3, 12, 0.247, 0.494}, -- Azuremyst Isle <--> The Exodar
159 {3, 12, 0.369, 0.469}, -- Azuremyst Isle <--> The Exodar
160 {3, 12, 0.310, 0.487}, -- Azuremyst Isle <--> The Exodar
161 {3, 12, 0.335, 0.494}, -- Azuremyst Isle <--> The Exodar
162 {3, 9, 0.42, 0.013}, -- Azuremyst Isle <--> Bloodmyst Isle
163 {4, 6, 0.539, 0.032}, -- Desolace <--> Stonetalon Mountains
164 {4, 17, 0.428, 0.976}, -- Desolace <--> Feralas
165 {5, 18, 0.865, 0.115}, -- Silithus <--> Un'Goro Crater
166 {7, 11, 0.341, 0.424}, -- Durotar <--> The Barrens
167 {7, 1, 0.455, 0.121}, -- Durotar <--> Orgrimmar
168 {8, 18, 0.269, 0.516}, -- Tanaris <--> Un'Goro Crater
169 {8, 14, 0.512, 0.21}, -- Tanaris <--> Thousand Needles
170 {10, 11, 0.287, 0.472}, -- Dustwallow Marsh <--> The Barrens
171 {10, 11, 0.563, 0.077}, -- Dustwallow Marsh <--> The Barrens
172 {11, 14, 0.442, 0.915}, -- The Barrens <--> Thousand Needles
173 {13, 19, 0.685, 0.06}, -- Felwood <--> Winterspring
174 {13, 20, 0.669, -0.063}, -- Felwood <--> Moonglade
175 {1, 11, 0.118, 0.69}, -- Orgrimmar <--> The Barrens
176 {17, 14, 0.899, 0.46}, -- Feralas <--> Thousand Needles
177 {6, 11, 0.836, 0.973}, -- Stonetalon Mountains <--> The Barrens
178 {26, 48, 0.521, 0.7}, -- Alterac Mountains <--> Hillsbrad Foothills
179 {26, 35, 0.173, 0.482}, -- Alterac Mountains <--> Silverpine Forest
180 {26, 50, 0.807, 0.347}, -- Alterac Mountains <--> Western Plaguelands
181 {39, 51, 0.454, 0.89}, -- Arathi Highlands <--> Wetlands
182 {39, 48, 0.2, 0.293}, -- Arathi Highlands <--> Hillsbrad Foothills
183 {27, 29, 0.49, 0.071}, -- Badlands <--> Loch Modan
184 -- {27, 32, -0.005, 0.636}, -- Badlands <--> Searing Gorge -- This is the "alliance-only" locked path, I'm disabling it for now entirely
185 {33, 46, 0.519, 0.051}, -- Blasted Lands <--> Swamp of Sorrows
186 {40, 30, 0.79, 0.842}, -- Burning Steppes <--> Redridge Mountains
187 {47, 31, 0.324, 0.363}, -- Deadwind Pass <--> Duskwood
188 {47, 46, 0.605, 0.41}, -- Deadwind Pass <--> Swamp of Sorrows
189 {28, 25, 0.534, 0.349}, -- Dun Morogh <--> Ironforge
190 {28, 29, 0.863, 0.514}, -- Dun Morogh <--> Loch Modan
191 {28, 29, 0.844, 0.31}, -- Dun Morogh <--> Loch Modan
192 {31, 37, 0.801, 0.158}, -- Duskwood <--> Elwynn Forest
193 {31, 37, 0.15, 0.214}, -- Duskwood <--> Elwynn Forest
194 {31, 38, 0.447, 0.884}, -- Duskwood <--> Stranglethorn Vale
195 {31, 38, 0.209, 0.863}, -- Duskwood <--> Stranglethorn Vale
196 {31, 30, 0.941, 0.103}, -- Duskwood <--> Redridge Mountains
197 {31, 49, 0.079, 0.638}, -- Duskwood <--> Westfall
198 {34, 50, 0.077, 0.661}, -- Eastern Plaguelands <--> Western Plaguelands
199 {34, 44, 0.575, 0.000}, -- Eastern Plaguelands <--> Ghostlands
200 {37, 36, 0.321, 0.493}, -- Elwynn Forest <--> Stormwind City -- Don't need to convert because it's in Elwynn coordinates, not Stormwind coordinates
201 {37, 49, 0.202, 0.804}, -- Elwynn Forest <--> Westfall
202 {37, 30, 0.944, 0.724}, -- Elwynn Forest <--> Redridge Mountains
203 {41, 52, 0.567, 0.494}, -- Eversong Woods <--> Silvermoon City
204 {41, 44, 0.486, 0.916}, -- Eversong Woods <--> Ghostlands
205 {35, 43, 0.678, 0.049}, -- Silverpine Forest <--> Tirisfal Glades
206 {42, 50, 0.217, 0.264}, -- The Hinterlands <--> Western Plaguelands
207 {43, 45, 0.619, 0.651}, -- Tirisfal Glades <--> Undercity
208 {43, 50, 0.851, 0.703}, -- Tirisfal Glades <--> Western Plaguelands
209 {38, 49, 0.292, 0.024}, -- Stranglethorn Vale <--> Westfall
210 {48, 35, 0.137, 0.458}, -- Hillsbrad Foothills <--> Silverpine Forest
211 {48, 42, 0.899, 0.253}, -- Hillsbrad Foothills <--> The Hinterlands
212 {29, 51, 0.252, 0}, -- Loch Modan <--> Wetlands
214 -- These are just guesses, since I haven't actually been to these areas.
215 {58, 60, 0.783, 0.545}, -- Nagrand <--> Shattrath City
216 {60, 55, 0.782, 0.492}, -- Shattrath City <--> Terokkar Forest
217 {54, 59, 0.842, 0.284}, -- Blade's Edge Mountains <--> Netherstorm
218 {54, 57, 0.522, 0.996}, -- Blade's Edge Mountains <--> Zangarmarsh
219 {54, 57, 0.312, 0.94}, -- Blade's Edge Mountains <--> Zangarmarsh
220 {56, 55, 0.353, 0.901}, -- Hellfire Peninsula <--> Terokkar Forest
221 {56, 57, 0.093, 0.519}, -- Hellfire Peninsula <--> Zangarmarsh
222 {58, 55, 0.8, 0.817}, -- Nagrand <--> Terokkar Forest
223 {58, 57, 0.343, 0.159}, -- Nagrand <--> Zangarmarsh
224 {58, 57, 0.754, 0.331}, -- Nagrand <--> Zangarmarsh
225 {53, 55, 0.208, 0.271}, -- Shadowmoon Valley <--> Terokkar Forest
226 {55, 57, 0.341, 0.098}, -- Terokkar Forest <--> Zangarmarsh
228 -- Most of these are also guesses :)
229 {65, 72, 0.547, 0.059}, -- Borean Tundra <--> Sholazar Basin
230 {65, 68, 0.967, 0.359}, -- Borean Tundra <--> Dragonblight
231 {74, 72, 0.208, 0.191}, -- Wintergrasp <--> Sholazar
232 {68, 74, 0.250, 0.410}, -- Dragonblight <--> Wintergrasp
233 {68, 71, 0.359, 0.155}, -- Dragonblight <--> Icecrown
234 {68, 66, 0.612, 0.142}, -- Dragonblight <--> Crystalsong
235 {68, 75, 0.900, 0.200}, -- Dragonblight <--> Zul'Drak
236 {68, 69, 0.924, 0.304}, -- Dragonblight <--> Grizzly Hills
237 {68, 69, 0.931, 0.634}, -- Dragonblight <--> Grizzly Hills
238 {70, 69, 0.540, 0.042}, -- Howling Fjord <--> Grizzly Hills
239 {70, 69, 0.233, 0.074}, -- Howling Fjord <--> Grizzly Hills
240 {70, 69, 0.753, 0.060}, -- Howling Fjord <--> Grizzly Hills
241 {69, 75, 0.432, 0.253}, -- Grizzly Hills <--> Zul'Drak
242 {69, 75, 0.583, 0.136}, -- Grizzly Hills <--> Zul'Drak
243 {66, 75, 0.967, 0.599}, -- Crystalsong <--> Zul'Drak
244 {66, 71, 0.156, 0.085}, -- Crystalsong <--> Icecrown
245 {66, 73, 0.706, 0.315}, -- Crystalsong <--> Storm Peaks
246 {66, 73, 0.839, 0.340}, -- Crystalsong <--> Storm Peaks
247 {71, 73, 0.920, 0.767}, -- Icecrown <--> Storm Peaks
250 local walkspeed_multiplier
= 1/7 -- Every yard walked takes this many seconds.
252 QuestHelper
.prepared_objectives
= {}
253 QuestHelper
.named_nodes
= {}
255 local function cont_dist(a
, b
)
256 local x
, y
= a
.x
-b
.x
, a
.y
-b
.y
257 return math
.sqrt(x
*x
+y
*y
)
260 function QuestHelper
:ComputeRoute(p1
, p2
)
261 for i
in ipairs(p1
[1]) do QuestHelper
: Assert(p1
[2][i
], "p1 nil flightpath error resurgence!") end
262 for i
in ipairs(p2
[1]) do QuestHelper
: Assert(p2
[2][i
], "p2 nil flightpath error resurgence!") end
264 if not p1
or not p2
then QuestHelper
:Error("Boom!") end
265 local graph
= self
.world_graph
267 graph
:PrepareSearch()
271 for i
, n
in ipairs(el
) do
277 for n
in pairs(graph
.open
) do QuestHelper
: Assert(nil, "not empty in preparesearch within computeroute") end
278 for i
, n
in ipairs(p1
[1]) do
279 graph
:AddRouteStartNode(n
, l
[i
], el
)
282 local e
= graph
:DoRouteSearch(el
)
288 if p1
[1] == p2
[1] then
289 local x
, y
= p1
[3]-p2
[3], p1
[4]-p2
[4]
290 local d2
= math
.sqrt(x
*x
+y
*y
)
300 -- Let's annotate the hell out of this
301 -- ComputeTravelTime finds the shortest path between points p1 and p2. It will cheerfully use route boundaries, ships, etc. It returns the distance of that path, gleefully throwing away the path itself. Thanks, ComputeTravelTime! Thomputetraveltime. (That joke does not work as well in this case.)
302 -- ZORBANOTE: Given that Graph works properly, this does too! Almost - it doesn't actually keep track of the last leg when optimizing, though it does create a valid path with a valid cost. Yaaaaaaaay :(
303 function QuestHelper
:ComputeTravelTime(p1
, p2
)
304 if not p1
or not p2
then QuestHelper
:Error("Boom!") end
305 local graph
= self
.world_graph
307 graph
:PrepareSearch()
309 local l
= p2
[2] -- Distance to zone boundaries in p2
310 local el
= p2
[1] -- Zone object for the zone that p2 is in
311 for i
, n
in ipairs(el
) do -- i is the zone index, n is the zone data
312 n
.e
, n
.w
= l
[i
], 1 -- n.e is distance from p2 to the current zone boundary, n.w is 1 (weight?)
314 n
.s
= 3 -- this is "state", I think it means "visited". TODO: untangle n.s and make it suck less than it currently does
317 l
= p1
[2] -- Distance to zone boundaries, again
318 for i
, n
in ipairs(p1
[1]) do
319 graph
:AddStartNode(n
, l
[i
], el
) -- We're adding start nodes - a prebuilt cost of the distance-to-that-zone. Each startnode also contains its own endlist, for reasons unknown yet byzantine. "n" is the zone link itself, l[i] is the cost that we still have stored. Why does this need to be in both n.e and AddStartNode?
322 local e
= graph
:DoSearch(el
)
326 local d
= e
.g
+e
.e
-- e.e is presumably the same n.e we introduced earlier. e.g - graph cost? wait a second - does this mean that the graph system is not taking e.e into account? ha ha no it isn't, oh boy oh boy
328 if p1
[1] == p2
[1] then -- if they're in the same zone, we allow the user to walk from one point to another
329 local x
, y
= p1
[3]-p2
[3], p1
[4]-p2
[4]
330 d
= math
.min(d
, math
.sqrt(x
*x
+y
*y
))
336 function QuestHelper
:CreateGraphNode(c
, x
, y
, n
)
337 local node
= self
.world_graph
:CreateNode()
345 QuestHelper
: Assert(QuestHelper_ZoneLookup
[c
[1]]
, "Zone couldn't be found, and should have been")
346 local cont
, zone
= unpack(QuestHelper_ZoneLookup
[c
[1]]
)
348 node
.x
, node
.y
= self
.Astrolabe
:TranslateWorldMapPosition(cont
, zone
, c
[2], c
[3], cont
, 0)
349 node
.x
= node
.x
* self
.continent_scales_x
[node
.c
]
350 node
.y
= node
.y
* self
.continent_scales_y
[node
.c
]
351 node
.name
= c
[5] or QuestHelper_NameLookup
[c
[1]]
358 function QuestHelper
:CreateAndAddZoneNode(z
, c
, x
, y
)
359 local node
= self
:CreateGraphNode(c
, x
, y
)
360 if not node
then return end -- exception for Wrath changeover
361 -- Not going to merge nodes.
362 --[[local closest, travel_time = nil, 0
364 for i, n in ipairs(z) do
365 local t = math.sqrt((n.x-node.x)*(n.x-node.x)+(n.y-node.y)*(n.y-node.y))
366 if not closest or t < travel_time then
367 closest, travel_time = n, t
371 if closest and travel_time < 10 then
372 closest.x = (closest.x * closest.w + node.x)/(closest.w+1)
373 closest.y = (closest.y * closest.w + node.y)/(closest.w+1)
374 closest.w = closest.w + 1
375 self.world_graph:DestroyNode(node)
378 table.insert(z
, node
)
383 function QuestHelper
:CreateAndAddStaticNodePair(data
)
386 if data
[5] and self
.named_nodes
[data
[5]]
then
387 node1
= self
.named_nodes
[data
[5]]
389 node1
= self
:CreateAndAddZoneNode(self
.zone_nodes
[data
[1][1]]
, data
[1])
390 if not node1
then return end -- exception for Wrath changeover
391 if data
[5] then self
.named_nodes
[data
[5]]
= node1
end
394 if data
[6] and self
.named_nodes
[data
[6]]
then
395 node2
= self
.named_nodes
[data
[6]]
397 node2
= self
:CreateAndAddZoneNode(self
.zone_nodes
[data
[2][1]]
, data
[2])
398 if not node2
then return end -- exception for Wrath changeover
399 if data
[6] then self
.named_nodes
[data
[6]]
= node2
end
402 node1
.name
= node1
.name
or "route to "..QuestHelper_NameLookup
[data
[2][1]]
403 node2
.name
= node2
.name
or "route to "..QuestHelper_NameLookup
[data
[1][1]]
405 node1
:Link(node2
, data
[3])
407 if not data
[4] then -- If data[4] is true, then this is a one-way trip.
408 node2
:Link(node1
, data
[3])
415 function QuestHelper
:GetNodeByName(name
, fallback_data
)
416 local node
= self
.named_nodes
[name
]
417 if not node
and fallback_data
then
418 node
= self
:CreateAndAddZoneNode(self
.zone_nodes
[fallback_data
[1]]
, fallback_data
)
419 self
.named_nodes
[name
] = node
424 local function nodeLeavesContinent(node
, c
)
426 for n
, d
in pairs(node
.n
) do
435 local function isGoodPath(start_node
, end_node
, i
, j
)
436 -- Checks to make sure a path doesn't leave the continent only to reenter it.
439 if end_node
.c
== i
then
442 end_node
= end_node
.p
443 if end_node
.c
== j
then
447 return end_node
== start_node
452 local function shouldLink(a
, b
)
453 -- TODO: Need to have objectives not create links to unreachable nodes.
459 for id in pairs(a.id_from) do
460 if not b.id_to[id] then
461 for id in pairs(b.id_to) do
462 if not a.id_from[id] then
473 local function getNPCNode(npc
)
474 local npc_objective
= QuestHelper
:GetObjective("monster", npc
)
475 if npc_objective
:Known() then
476 npc_objective
:PrepareRouting()
477 local p
= npc_objective
:Position()
481 node
= QuestHelper
:CreateAndAddZoneNode(p
[1], p
[1].c
, p
[3], p
[4])
484 npc_objective
:DoneRouting()
490 function QuestHelper
:CreateAndAddTransitionNode(z1
, z2
, pos
)
491 QuestHelper
: Assert(z1
and z2
, "Zone couldn't be found, and should have been")
493 local node
= self
:CreateGraphNode(pos
)
495 local closest
, travel_time
= nil, 0
497 for i
, n
in ipairs(z1
) do
498 local t
= math
.sqrt((n
.x
-node
.x
)*(n
.x
-node
.x
)+(n
.y
-node
.y
)*(n
.y
-node
.y
))
499 if not closest
or t
< travel_time
then
500 closest
, travel_time
= n
, t
505 for i
, n
in ipairs(z2
) do
506 local t
= math
.sqrt((n
.x
-node
.x
)*(n
.x
-node
.x
)+(n
.y
-node
.y
)*(n
.y
-node
.y
))
507 if not closest
or t
< travel_time
then
508 closest
, travel_time
= n
, t
513 if closest
and travel_time
< 10 then
514 --QuestHelper:TextOut("Node already exists at "..closest.x..", "..closest.y..", name="..(closest.name or "nil"))
515 closest
.x
= (closest
.x
* closest
.w
+ node
.x
)/(closest
.w
+1)
516 closest
.y
= (closest
.y
* closest
.w
+ node
.y
)/(closest
.w
+1)
517 closest
.w
= closest
.w
+ 1
518 local z1_has
, z2_has
= false, false
520 -- Just because the node already exists, doesn't mean its already in both lists!
522 for i
, n
in ipairs(z1
) do
530 for i
, n
in ipairs(z2
) do
540 if not z1_has
then table.insert(z1
, closest
) end
541 if not z2_has
then table.insert(z2
, closest
) end
543 self
.world_graph
:DestroyNode(node
)
547 table.insert(z1
, node
)
548 if z1
~= z2
then table.insert(z2
, node
) end
554 function QuestHelper
:ReleaseObjectivePathingInfo(o
)
556 for z
, pl
in pairs(o
.p
) do
557 self
:ReleaseTable(o
.d
[z
])
559 for i
, p
in ipairs(pl
) do
560 self
:ReleaseTable(p
[2])
564 self
:ReleaseTable(pl
)
567 self
:ReleaseTable(o
.d
)
568 self
:ReleaseTable(o
.p
)
569 self
:ReleaseTable(o
.nm
)
570 self
:ReleaseTable(o
.nm2
)
571 self
:ReleaseTable(o
.nl
)
573 local cache
= o
.distance_cache
574 for k
, v
in pairs(cache
) do
578 self
:ReleaseTable(cache
)
580 o
.d
, o
.p
, o
.nm
, o
.nm2
, o
.nl
= nil, nil, nil, nil, nil
581 o
.distance_cache
= nil
582 o
.pos
, o
.sop
= nil, nil -- ResetPathing will preserve these values if needed.
587 function QuestHelper
:SetupTeleportInfo(info
, can_create
)
588 self
:TeleportInfoClear(info
)
590 if QuestHelper_Home
then
591 local node
= self
:GetNodeByName("HOME_PORTAL", can_create
and QuestHelper_Home
)
593 local cooldown
= self
:ItemCooldown(6948)
595 self
:SetTeleportInfoTarget(info
, node
, GetTime()-60*60+cooldown
, 60*60, 10)
598 self
.defered_graph_reset
= true
602 -- TODO: Compact this. . . and find a better way to tell if the player has a spell.
604 if GetSpellTexture("Teleport: Darnassus") then
605 local node
= self
:GetNodeByName("DARNASSUS_PORTAL", can_create
and DARNASSUS_PORTAL
)
606 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
609 if GetSpellTexture("Teleport: Exodar") then
610 local node
= self
:GetNodeByName("EXODAR_PORTAL", can_create
and EXODAR_PORTAL
)
611 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
614 if GetSpellTexture("Teleport: Ironforge") then
615 local node
= self
:GetNodeByName("IRONFORGE_PORTAL", can_create
and IRONFORGE_PORTAL
)
616 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
619 if GetSpellTexture("Teleport: Moonglade") then
620 local node
= self
:GetNodeByName("MOONGLADE_PORTAL", can_create
and MOONGLADE_PORTAL
)
621 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10) else self
.defered_graph_reset
= true end
624 if GetSpellTexture("Teleport: Orgrimmar") then
625 local node
= self
:GetNodeByName("ORGRIMMAR_PORTAL", can_create
and ORGRIMMAR_PORTAL
)
626 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
629 if GetSpellTexture("Teleport: Shattrath") then
630 local node
= self
:GetNodeByName("SHATTRATH_CITY_PORTAL", can_create
and SHATTRATH_CITY_PORTAL
)
631 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
634 if GetSpellTexture("Teleport: Silvermoon") then
635 local node
= self
:GetNodeByName("SILVERMOON_CITY_PORTAL", can_create
and SILVERMOON_CITY_PORTAL
)
636 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
639 if GetSpellTexture("Teleport: Stormwind") then
640 local node
= self
:GetNodeByName("STORMWIND_CITY_PORTAL", can_create
and STORMWIND_CITY_PORTAL
)
641 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
644 if GetSpellTexture("Teleport: Thunder Bluff") then
645 local node
= self
:GetNodeByName("THUNDER_BLUFF_PORTAL", can_create
and THUNDER_BLUFF_PORTAL
)
646 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
649 if GetSpellTexture("Teleport: Undercity") then
650 local node
= self
:GetNodeByName("UNDERCITY_PORTAL", can_create
and UNDERCITY_PORTAL
)
651 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
654 self
:SetTeleportInfoReagent(info
, 17031, self
:CountItem(17031))
657 function QuestHelper
:ResetPathing()
658 for key
in pairs(self
.named_nodes
) do
659 self
.named_nodes
[key
] = nil
662 -- Objectives may include cached information that depends on the world graph.
665 while i
<= #self
.prepared_objectives
do
666 local o
= self
.prepared_objectives
[i
]
668 if o
.setup_count
== 0 then
669 table.remove(self
.prepared_objectives
, i
)
670 self
:ReleaseObjectivePathingInfo(o
)
672 -- Routing should reset the positions of objectives in the route after the reset is complete.
674 self
:ReleaseObjectivePathingInfo(o
)
679 local to_readd
= self
.prepared_objectives
680 self
.prepared_objectives
= self
.old_prepared_objectives
or {}
681 self
.old_prepared_objectives
= to_readd
683 local zone_nodes
= self
.zone_nodes
684 if not zone_nodes
then
686 self
.zone_nodes
= zone_nodes
689 local flight_master_nodes
= self
.flight_master_nodes
690 if not flight_master_nodes
then
691 flight_master_nodes
= {}
692 self
.flight_master_nodes
= flight_master_nodes
694 for key
in pairs(flight_master_nodes
) do
695 flight_master_nodes
[key
] = nil
699 self
.world_graph
:Reset()
702 local continent_scales_x
, continent_scales_y
= self
.continent_scales_x
, self
.continent_scales_y
703 if not continent_scales_x
then
704 continent_scales_x
= {}
705 continent_scales_y
= {}
706 self
.continent_scales_x
= continent_scales_x
707 self
.continent_scales_y
= continent_scales_y
710 for c
in pairs(self
.Astrolabe
:GetMapVirtualContinents()) do
711 if not continent_scales_x
[c
] then
712 local _
, x
, y
= self
.Astrolabe
:ComputeDistance(c
, 0, 0.25, 0.25, c
, 0, 0.75, 0.75)
714 continent_scales_x
[c
] = x
*walkspeed_multiplier
*2
715 continent_scales_y
[c
] = y
*walkspeed_multiplier
*2
719 for i
, name
in pairs(QuestHelper_NameLookup
) do
720 local z
= zone_nodes
[i
]
722 z
= QuestHelper
:CreateTable("zone") -- This was originally z = {}. I'm pretty sure that the CreateTable/ReleaseTable system is (largely) immune to leaks, and I'm also pretty sure that zones are only created once, at the beginning of the system. Keep this in mind if leaks start occuring.
724 z
.i
, z
.c
, z
.z
= i
, unpack(QuestHelper_ZoneLookup
[i
])
726 for key
in pairs(z
) do
729 z
.i
, z
.c
, z
.z
= i
, unpack(QuestHelper_ZoneLookup
[i
])
733 self
:SetupTeleportInfo(self
.teleport_info
, true)
736 --[[for node, info in pairs(self.teleport_info.node) do
737 self:TextOut("You can teleport to "..(node.name or "nil").. " in "..self:TimeString(info[1]+info[2]-GetTime()))
740 if self
.faction
== 1 then
741 for i
, data
in ipairs(static_alliance_routes
) do
742 self
:CreateAndAddStaticNodePair(data
)
744 elseif self
.faction
== 2 then
745 for i
, data
in ipairs(static_horde_routes
) do
746 self
:CreateAndAddStaticNodePair(data
)
750 for i
, data
in ipairs(static_shared_routes
) do
751 self
:CreateAndAddStaticNodePair(data
)
754 if self
.player_level
>= 58 then
755 dark_portal_route
[3] = 5
757 -- If you can't take the route yet, we'll still add it and just pretend it will take a really long time.
758 dark_portal_route
[3] = 86400
761 self
:CreateAndAddStaticNodePair(dark_portal_route
)
763 local st
= self
:CreateTable("ResetPathing local st")
765 for i
, data
in pairs(static_zone_transitions
) do
766 st
[1], st
[2], st
[3] = data
[1], data
[3], data
[4]
768 local transnode
= self
:CreateAndAddTransitionNode(zone_nodes
[data
[1]]
,
771 if transnode
then transnode
.name
= QHFormat("ZONE_BORDER", QuestHelper_NameLookup
[data
[1]]
, QuestHelper_NameLookup
[data
[2]]
) end -- if the transition node wasn't creatable, we obviously can't name it
774 self
:ReleaseTable(st
)
776 -- Create and link the flight route nodes.
777 local flight_times
= self
.flight_times
778 if not flight_times
then
779 self
:buildFlightTimes()
780 flight_times
= self
.flight_times
783 for start
, list
in pairs(flight_times
) do
784 for dest
, duration
in pairs(list
) do
785 local a_npc
, b_npc
= self
:getFlightInstructor(start
), self
:getFlightInstructor(dest
)
787 if a_npc
and b_npc
then
788 local a
, b
= flight_master_nodes
[start
], flight_master_nodes
[dest
]
791 a
= getNPCNode(a_npc
)
793 flight_master_nodes
[start
] = a
794 a
.name
= (select(3, string.find(start
, "^(.*),")) or start
).." flight point"
799 b
= getNPCNode(b_npc
)
801 flight_master_nodes
[dest
] = b
802 b
.name
= (select(3, string.find(dest
, "^(.*),")) or dest
).." flight point"
807 a
:Link(b
, duration
+5)
814 -- id_from, id_to, and id_local will be used in determining whether there is a point to linking nodes together.
815 for i
, n
in ipairs(self
.world_graph
.nodes
) do
816 n
.id_from
= self
:CreateTable("ResetPathing n.id_from")
817 n
.id_to
= self
:CreateTable("ResetPathing n.id_to")
818 n
.id_local
= self
:CreateTable("ResetPathing n.id_local")
821 -- Setup the local ids a node exists in.
822 for i
, list
in pairs(zone_nodes
) do
823 for _
, n
in ipairs(list
) do
830 -- Figure out where each node can come from or go to.
831 for i
, list
in pairs(zone_nodes
) do
832 for _
, node
in ipairs(list
) do
833 for n
in pairs(node
.n
) do
834 for id
in pairs(n
.id_local
) do node
.id_to
[id
] = true end
835 for id
in pairs(node
.id_local
) do n
.id_from
[id
] = true end
840 -- We'll treat 0 as a special id for where ever it is the player happens to be.
841 for node
in pairs(self
.teleport_info
.node
) do
842 node
.id_from
[0] = true
845 -- Will go through each zone and link all the nodes we have so far with every other node.
846 for _
, list
in pairs(zone_nodes
) do
849 if shouldLink(list
[i
], list
[j
]) then
850 list
[i
]:Link(list
[j
], cont_dist(list
[i
], list
[j
]))
858 -- We don't need to know where the nodes can go or come from now.
859 for i
, n
in ipairs(self
.world_graph
.nodes
) do
860 self
:ReleaseTable(n
.id_from
)
861 self
:ReleaseTable(n
.id_to
)
862 self
:ReleaseTable(n
.id_local
)
863 n
.id_from
, n
.id_to
, n
.id_local
= nil, nil, nil
866 -- TODO: This is a work around until I fix shouldLink
867 for start
, list
in pairs(flight_times
) do
868 for dest
, duration
in pairs(list
) do
869 local a
, b
= flight_master_nodes
[start
], flight_master_nodes
[dest
]
871 a
:Link(b
, duration
+5)
877 -- self.world_graph:SanityCheck()
879 -- Remove objectives again, since we created some for the flight masters.
881 local o
= table.remove(self
.prepared_objectives
)
882 if not o
then break end
884 self
:ReleaseObjectivePathingInfo(o
)
886 if o
.setup_count
> 0 then
887 -- There's a chance an objective could end up in the list twice, but we'll deal with that by not actually
888 -- adding locations for it if it's already setup.
889 table.insert(to_readd
, o
)
894 local obj
= table.remove(to_readd
)
895 if not obj
then break end
897 if not obj
.setup
then -- In case the objective was added multiple times to the to_readd list.
898 obj
.d
= QuestHelper
:CreateTable("ResetPathing obj.d")
899 obj
.p
= QuestHelper
:CreateTable("ResetPathing obj.p")
900 obj
.nm
= QuestHelper
:CreateTable("ResetPathing obj.nm")
901 obj
.nm2
= QuestHelper
:CreateTable("ResetPathing obj.nm2")
902 obj
.nl
= QuestHelper
:CreateTable("ResetPathing obj.nl")
903 obj
.distance_cache
= QuestHelper
:CreateTable("ResetPathing obj.distance_cache")
904 obj
:AppendPositions(obj
, 1, nil)
910 self
.pos
[1] = self
.zone_nodes
[self
.i
]
911 for i
, n
in ipairs(self
.pos
[1]) do
912 local a
, b
= n
.x
-self
.pos
[3], n
.y
-self
.pos
[4]
913 self
.pos
[2][i
] = math
.sqrt(a
*a
+b
*b
)
917 -- And if all went according to plan, we now have a graph we can follow to get from anywhere to anywhere.
919 if self
.graph_walker
then
920 self
.graph_walker
:GraphChanged()
925 function QuestHelper
:Disallowed(index
)
926 return QuestHelper_RestrictedZones
[index
] ~= QuestHelper_RestrictedZones
[self
.i
]