1 QuestHelper_File
["pathfinding.lua"] = "Development Version"
3 local IRONFORGE_PORTAL
= {25,0.255,0.084, "Ironforge portal site"}
4 local STORMWIND_CITY_PORTAL
= QuestHelper_ConvertCoordsFromWrath(QuestHelper_ConvertCoordsToWrath({36,0.387,0.802, "Stormwind City portal site"}, true)) -- The annoying "wrath" construction here first forces it into Wrath format, then puts it into Native format.
5 local DARNASSUS_PORTAL
= {21,0.397,0.824, "Darnassus portal site"}
6 local EXODAR_PORTAL
= {12,0.476,0.598, "Exodar portal site"}
8 local SHATTRATH_CITY_PORTAL
= {60,0.530,0.492, "Shattrath City portal site"}
9 local DALARAN_PORTAL
= {67,0.500,0.394, "Dalaran portal site"}
10 local MOONGLADE_PORTAL
= {20,0.563,0.320, "Moonglade portal site"}
12 local SILVERMOON_CITY_PORTAL
= {52,0.583,0.192, "Silvermoon City portal site"}
13 local UNDERCITY_PORTAL
= {45,0.846,0.163, "Undercity portal site"}
14 local ORGRIMMAR_PORTAL
= {1,0.386,0.859, "Orgrimmar portal site"}
15 local THUNDER_BLUFF_PORTAL
= {23,0.222,0.168, "Thunder Bluff portal site"}
17 local static_horde_routes
=
19 {{7, 0.505, 0.124}, {38, 0.313, 0.303}, 210}, -- Durotar <--> Grom'gol Base Camp
20 {{38, 0.316, 0.289}, {43, 0.621, 0.591}, 210}, -- Grom'gol Base Camp <--> Tirisfal Glades
21 {{43, 0.605, 0.587}, {7, 0.509, 0.141}, 210}, -- Tirisfal Glades <--> Durotar
22 {{45, 0.549, 0.11}, {52, 0.495, 0.148}, 5}, -- Undercity <--> Silvermoon City
24 {{7, 0.413, 0.178}, {65, 0.414, 0.536}, 5}, -- Durotar <--> Warsong Hold
25 {{43, 0.591, 0.590}, {70, 0.777, 0.283}, 5}, -- Tirisfal Glades <--> Vengeance Landing
27 {{60, 0.592, 0.483}, SILVERMOON_CITY_PORTAL
, 5, true, nil, "SILVERMOON_CITY_PORTAL"}, -- Shattrath City --> Silvermoon City
28 {{60, 0.528, 0.531}, THUNDER_BLUFF_PORTAL
, 5, true, nil, "THUNDER_BLUFF_PORTAL"}, -- Shattrath City --> Thunder Bluff
29 {{60, 0.522, 0.529}, ORGRIMMAR_PORTAL
, 5, true, nil, "ORGRIMMAR_PORTAL"}, -- Shattrath City --> Orgrimmar
30 {{60, 0.517, 0.525}, UNDERCITY_PORTAL
, 5, true, nil, "UNDERCITY_PORTAL"}, -- Shattrath City --> Undercity
32 {{67, 0.583, 0.216}, SILVERMOON_CITY_PORTAL
, 5, true, nil, "SILVERMOON_CITY_PORTAL"}, -- Dalaran --> Silvermoon City
33 {{67, 0.573, 0.219}, THUNDER_BLUFF_PORTAL
, 5, true, nil, "THUNDER_BLUFF_PORTAL"}, -- Dalaran --> Thunder Bluff
34 {{67, 0.553, 0.255}, ORGRIMMAR_PORTAL
, 5, true, nil, "ORGRIMMAR_PORTAL"}, -- Dalaran --> Orgrimmar
35 {{67, 0.556, 0.238}, UNDERCITY_PORTAL
, 5, true, nil, "UNDERCITY_PORTAL"}, -- Dalaran --> Undercity
36 {{67, 0.563, 0.226}, SHATTRATH_CITY_PORTAL
, 5, true, nil, "SHATTRATH_CITY_PORTAL"}, -- Dalaran --> Shatt
40 local static_alliance_routes
=
42 {{36, 0.639, 0.083}, {25, 0.764, 0.512}, 120}, -- Deeprun Tram
43 {{10, 0.718, 0.565}, {51, 0.047, 0.636}, 210}, -- Theramore Isle <--> Menethil Harmor
45 {QuestHelper_ConvertCoordsFromWrath({36, 0.183, 0.255}), {65, 0.597, 0.694}, 210}, -- Stormwind City <--> Valiance Keep (note: new Stormwind location)
46 {{51, 0.047, 0.571}, {70, 0.612, 0.626}, 210}, -- Menethil <--> Daggercap Bay
48 {{60, 0.558, 0.366}, STORMWIND_CITY_PORTAL
, 5, true, nil, "STORMWIND_CITY_PORTAL"}, -- Shattrath City --> Stormwind City
49 {{60, 0.563, 0.37}, IRONFORGE_PORTAL
, 5, true, nil, "IRONFORGE_PORTAL"}, -- Shattrath City --> Ironforge
50 {{60, 0.552, 0.364}, DARNASSUS_PORTAL
, 5, true, nil, "DARNASSUS_PORTAL"}, -- Shattrath City --> Darnassus
51 {{60, 0.596, 0.467}, EXODAR_PORTAL
, 5, true, nil, "EXODAR_PORTAL"}, -- Shattrath City --> Exodar
53 {{67, 0.401, 0.628}, STORMWIND_CITY_PORTAL
, 5, true, nil, "STORMWIND_CITY_PORTAL"}, -- Dalaran --> Stormwind City
54 {{67, 0.395, 0.640}, IRONFORGE_PORTAL
, 5, true, nil, "IRONFORGE_PORTAL"}, -- Dalaran --> Ironforge
55 {{67, 0.389, 0.651}, DARNASSUS_PORTAL
, 5, true, nil, "DARNASSUS_PORTAL"}, -- Dalaran --> Darnassus
56 {{67, 0.382, 0.664}, EXODAR_PORTAL
, 5, true, nil, "EXODAR_PORTAL"}, -- Dalaran --> Exodar
57 {{67, 0.371, 0.667}, SHATTRATH_CITY_PORTAL
, 5, true, nil, "SHATTRATH_CITY_PORTAL"}, -- Dalaran --> Shatt
60 if QuestHelper
:IsWrath() then -- siiiiigh
61 table.insert(static_alliance_routes
, {{36, 0.228, 0.560}, {16, 0.323, 0.441}, 210}) -- Stormwind City <--> Auberdine (note: new Stormwind location)
63 table.insert(static_alliance_routes
, {{51, 0.044, 0.569}, {16, 0.323, 0.441}, 210}) -- Menethil Harbor <--> Auberdine
66 local static_shared_routes
=
68 {{11, 0.638, 0.387}, {38, 0.257, 0.73}, 210}, -- Ratchet <--> Booty Bay
69 {{40, 0.318, 0.503}, {32, 0.347, 0.84}, 130}, -- Burning Steppes <--> Searing Gorge
71 -- More Alliance routes than anything, but without them theres no valid path to these areas for Horde characters.
72 {{24, 0.559, 0.896}, {21, 0.305, 0.414}, 5}, -- Rut'Theran Village <--> Darnassus
73 {{16, 0.332, 0.398}, {24, 0.548, 0.971}, 210}, -- Auberdine <--> Rut'Theran Village
74 {{16, 0.306, 0.409}, {3, 0.2, 0.546}, 210}, -- Auberdine <--> Azuremyst Isle
76 -- Route to new zone. Not valid, exists only to keep routing from exploding if you don't have the flight routes there.
77 {{41, 0.5, 0.5}, {64, 0.5, 0.5}, 7200}, -- Eversong Woods <--> Sunwell
79 {{70, 0.235, 0.578}, {68, 0.496, 0.784}, 210}, -- Kamagua <--> Moa'ki
80 {{65, 0.789, 0.536}, {68, 0.480, 0.787}, 210}, -- Unu'pe <--> Moa'ki
81 {{67, 0.559, 0.467}, {66, 0.158, 0.428}, 5, true}, -- Dalaran --> Violet Stand
82 {{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)
84 {{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.
87 -- Darkportal is handled specially, depending on whether or not you're level 58+ or not.
88 local dark_portal_route
= {{33, 0.587, 0.599}, {56, 0.898, 0.502}, 5}
90 local static_zone_transitions
=
92 {2, 11, 0.687, 0.872}, -- Ashenvale <--> The Barrens
93 {2, 6, 0.423, 0.711}, -- Ashenvale <--> Stonetalon Mountains
94 {2, 15, 0.954, 0.484}, -- Ashenvale <--> Azshara
95 {2, 16, 0.289, 0.144}, -- Ashenvale <--> Darkshore
96 {2, 13, 0.557, 0.29}, -- Ashenvale <--> Felwood
97 {21, 24, 0.894, 0.358}, -- Darnassus <--> Teldrassil
98 {22, 11, 0.697, 0.604}, -- Mulgore <--> The Barrens
99 {22, 23, 0.376, 0.33}, -- Mulgore <--> Thunder Bluff
100 {22, 23, 0.403, 0.193}, -- Mulgore <--> Thunder Bluff
101 {3, 12, 0.247, 0.494}, -- Azuremyst Isle <--> The Exodar
102 {3, 12, 0.369, 0.469}, -- Azuremyst Isle <--> The Exodar
103 {3, 12, 0.310, 0.487}, -- Azuremyst Isle <--> The Exodar
104 {3, 12, 0.335, 0.494}, -- Azuremyst Isle <--> The Exodar
105 {3, 9, 0.42, 0.013}, -- Azuremyst Isle <--> Bloodmyst Isle
106 {4, 6, 0.539, 0.032}, -- Desolace <--> Stonetalon Mountains
107 {4, 17, 0.428, 0.976}, -- Desolace <--> Feralas
108 {5, 18, 0.865, 0.115}, -- Silithus <--> Un'Goro Crater
109 {7, 11, 0.341, 0.424}, -- Durotar <--> The Barrens
110 {7, 1, 0.455, 0.121}, -- Durotar <--> Orgrimmar
111 {8, 18, 0.269, 0.516}, -- Tanaris <--> Un'Goro Crater
112 {8, 14, 0.512, 0.21}, -- Tanaris <--> Thousand Needles
113 {10, 11, 0.287, 0.472}, -- Dustwallow Marsh <--> The Barrens
114 {10, 11, 0.563, 0.077}, -- Dustwallow Marsh <--> The Barrens
115 {11, 14, 0.442, 0.915}, -- The Barrens <--> Thousand Needles
116 {13, 19, 0.685, 0.06}, -- Felwood <--> Winterspring
117 {13, 20, 0.669, -0.063}, -- Felwood <--> Moonglade
118 {1, 11, 0.118, 0.69}, -- Orgrimmar <--> The Barrens
119 {17, 14, 0.899, 0.46}, -- Feralas <--> Thousand Needles
120 {6, 11, 0.836, 0.973}, -- Stonetalon Mountains <--> The Barrens
121 {26, 48, 0.521, 0.7}, -- Alterac Mountains <--> Hillsbrad Foothills
122 {26, 35, 0.173, 0.482}, -- Alterac Mountains <--> Silverpine Forest
123 {26, 50, 0.807, 0.347}, -- Alterac Mountains <--> Western Plaguelands
124 {39, 51, 0.454, 0.89}, -- Arathi Highlands <--> Wetlands
125 {39, 48, 0.2, 0.293}, -- Arathi Highlands <--> Hillsbrad Foothills
126 {27, 29, 0.49, 0.071}, -- Badlands <--> Loch Modan
127 -- {27, 32, -0.005, 0.636}, -- Badlands <--> Searing Gorge -- This is the "alliance-only" locked path, I'm disabling it for now entirely
128 {33, 46, 0.519, 0.051}, -- Blasted Lands <--> Swamp of Sorrows
129 {40, 30, 0.79, 0.842}, -- Burning Steppes <--> Redridge Mountains
130 {47, 31, 0.324, 0.363}, -- Deadwind Pass <--> Duskwood
131 {47, 46, 0.605, 0.41}, -- Deadwind Pass <--> Swamp of Sorrows
132 {28, 25, 0.534, 0.349}, -- Dun Morogh <--> Ironforge
133 {28, 29, 0.863, 0.514}, -- Dun Morogh <--> Loch Modan
134 {28, 29, 0.844, 0.31}, -- Dun Morogh <--> Loch Modan
135 {31, 37, 0.801, 0.158}, -- Duskwood <--> Elwynn Forest
136 {31, 37, 0.15, 0.214}, -- Duskwood <--> Elwynn Forest
137 {31, 38, 0.447, 0.884}, -- Duskwood <--> Stranglethorn Vale
138 {31, 38, 0.209, 0.863}, -- Duskwood <--> Stranglethorn Vale
139 {31, 30, 0.941, 0.103}, -- Duskwood <--> Redridge Mountains
140 {31, 49, 0.079, 0.638}, -- Duskwood <--> Westfall
141 {34, 50, 0.077, 0.661}, -- Eastern Plaguelands <--> Western Plaguelands
142 {34, 44, 0.575, 0.000}, -- Eastern Plaguelands <--> Ghostlands
143 {37, 36, 0.321, 0.493}, -- Elwynn Forest <--> Stormwind City -- Don't need to convert because it's in Elwynn coordinates, not Stormwind coordinates
144 {37, 49, 0.202, 0.804}, -- Elwynn Forest <--> Westfall
145 {37, 30, 0.944, 0.724}, -- Elwynn Forest <--> Redridge Mountains
146 {41, 52, 0.567, 0.494}, -- Eversong Woods <--> Silvermoon City
147 {41, 44, 0.486, 0.916}, -- Eversong Woods <--> Ghostlands
148 {35, 43, 0.678, 0.049}, -- Silverpine Forest <--> Tirisfal Glades
149 {42, 50, 0.217, 0.264}, -- The Hinterlands <--> Western Plaguelands
150 {43, 45, 0.619, 0.651}, -- Tirisfal Glades <--> Undercity
151 {43, 50, 0.851, 0.703}, -- Tirisfal Glades <--> Western Plaguelands
152 {38, 49, 0.292, 0.024}, -- Stranglethorn Vale <--> Westfall
153 {48, 35, 0.137, 0.458}, -- Hillsbrad Foothills <--> Silverpine Forest
154 {48, 42, 0.899, 0.253}, -- Hillsbrad Foothills <--> The Hinterlands
155 {29, 51, 0.252, 0}, -- Loch Modan <--> Wetlands
157 -- These are just guesses, since I haven't actually been to these areas.
158 {58, 60, 0.783, 0.545}, -- Nagrand <--> Shattrath City
159 {60, 55, 0.782, 0.492}, -- Shattrath City <--> Terokkar Forest
160 {54, 59, 0.842, 0.284}, -- Blade's Edge Mountains <--> Netherstorm
161 {54, 57, 0.522, 0.996}, -- Blade's Edge Mountains <--> Zangarmarsh
162 {54, 57, 0.312, 0.94}, -- Blade's Edge Mountains <--> Zangarmarsh
163 {56, 55, 0.353, 0.901}, -- Hellfire Peninsula <--> Terokkar Forest
164 {56, 57, 0.093, 0.519}, -- Hellfire Peninsula <--> Zangarmarsh
165 {58, 55, 0.8, 0.817}, -- Nagrand <--> Terokkar Forest
166 {58, 57, 0.343, 0.159}, -- Nagrand <--> Zangarmarsh
167 {58, 57, 0.754, 0.331}, -- Nagrand <--> Zangarmarsh
168 {53, 55, 0.208, 0.271}, -- Shadowmoon Valley <--> Terokkar Forest
169 {55, 57, 0.341, 0.098}, -- Terokkar Forest <--> Zangarmarsh
171 -- Most of these are also guesses :)
172 {65, 72, 0.547, 0.059}, -- Borean Tundra <--> Sholazar Basin
173 {65, 68, 0.967, 0.359}, -- Borean Tundra <--> Dragonblight
174 {74, 72, 0.208, 0.191}, -- Wintergrasp <--> Sholazar
175 {68, 74, 0.250, 0.410}, -- Dragonblight <--> Wintergrasp
176 {68, 71, 0.359, 0.155}, -- Dragonblight <--> Icecrown
177 {68, 66, 0.612, 0.142}, -- Dragonblight <--> Crystalsong
178 {68, 75, 0.900, 0.200}, -- Dragonblight <--> Zul'Drak
179 {68, 69, 0.924, 0.304}, -- Dragonblight <--> Grizzly Hills
180 {68, 69, 0.931, 0.634}, -- Dragonblight <--> Grizzly Hills
181 {70, 69, 0.540, 0.042}, -- Howling Fjord <--> Grizzly Hills
182 {70, 69, 0.233, 0.074}, -- Howling Fjord <--> Grizzly Hills
183 {70, 69, 0.753, 0.060}, -- Howling Fjord <--> Grizzly Hills
184 {69, 75, 0.432, 0.253}, -- Grizzly Hills <--> Zul'Drak
185 {69, 75, 0.583, 0.136}, -- Grizzly Hills <--> Zul'Drak
186 {66, 75, 0.967, 0.599}, -- Crystalsong <--> Zul'Drak
187 {66, 71, 0.156, 0.085}, -- Crystalsong <--> Icecrown
188 {66, 73, 0.706, 0.315}, -- Crystalsong <--> Storm Peaks
189 {66, 73, 0.839, 0.340}, -- Crystalsong <--> Storm Peaks
190 {71, 73, 0.920, 0.767}, -- Icecrown <--> Storm Peaks
193 local walkspeed_multiplier
= 1/7 -- Every yard walked takes this many seconds.
195 QuestHelper
.prepared_objectives
= {}
196 QuestHelper
.named_nodes
= {}
198 local function cont_dist(a
, b
)
199 local x
, y
= a
.x
-b
.x
, a
.y
-b
.y
200 return math
.sqrt(x
*x
+y
*y
)
203 function QuestHelper
:ComputeRoute(p1
, p2
)
204 for i
in ipairs(p1
[1]) do QuestHelper
: Assert(p1
[2][i
], "p1 nil flightpath error resurgence!") end
205 for i
in ipairs(p2
[1]) do QuestHelper
: Assert(p2
[2][i
], "p2 nil flightpath error resurgence!") end
207 if not p1
or not p2
then QuestHelper
:Error("Boom!") end
208 local graph
= self
.world_graph
210 graph
:PrepareSearch()
214 for i
, n
in ipairs(el
) do
220 for n
in pairs(graph
.open
) do QuestHelper
: Assert(nil, "not empty in preparesearch within computeroute") end
221 for i
, n
in ipairs(p1
[1]) do
222 graph
:AddRouteStartNode(n
, l
[i
], el
)
225 local e
= graph
:DoRouteSearch(el
)
231 if p1
[1] == p2
[1] then
232 local x
, y
= p1
[3]-p2
[3], p1
[4]-p2
[4]
233 local d2
= math
.sqrt(x
*x
+y
*y
)
243 -- Let's annotate the hell out of this
244 -- 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.)
245 -- 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 :(
246 function QuestHelper
:ComputeTravelTime(p1
, p2
)
247 if not p1
or not p2
then QuestHelper
:Error("Boom!") end
248 local graph
= self
.world_graph
250 graph
:PrepareSearch()
252 local l
= p2
[2] -- Distance to zone boundaries in p2
253 local el
= p2
[1] -- Zone object for the zone that p2 is in
254 for i
, n
in ipairs(el
) do -- i is the zone index, n is the zone data
255 n
.e
, n
.w
= l
[i
], 1 -- n.e is distance from p2 to the current zone boundary, n.w is 1 (weight?)
257 n
.s
= 3 -- this is "state", I think it means "visited". TODO: untangle n.s and make it suck less than it currently does
260 l
= p1
[2] -- Distance to zone boundaries, again
261 for i
, n
in ipairs(p1
[1]) do
262 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?
265 local e
= graph
:DoSearch(el
)
269 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
271 if p1
[1] == p2
[1] then -- if they're in the same zone, we allow the user to walk from one point to another
272 local x
, y
= p1
[3]-p2
[3], p1
[4]-p2
[4]
273 d
= math
.min(d
, math
.sqrt(x
*x
+y
*y
))
279 function QuestHelper
:CreateGraphNode(c
, x
, y
, n
)
280 local node
= self
.world_graph
:CreateNode()
288 if not QuestHelper_ZoneLookup
[c
[1]]
then -- exception for Wrath changeover
289 QuestHelper
:Assert(not QuestHelper
:IsWrath(), "Zone couldn't be found, and should have been")
292 local cont
, zone
= unpack(QuestHelper_ZoneLookup
[c
[1]]
)
294 node
.x
, node
.y
= self
.Astrolabe
:TranslateWorldMapPosition(cont
, zone
, c
[2], c
[3], cont
, 0)
295 node
.x
= node
.x
* self
.continent_scales_x
[node
.c
]
296 node
.y
= node
.y
* self
.continent_scales_y
[node
.c
]
297 node
.name
= c
[5] or QuestHelper_NameLookup
[c
[1]]
304 function QuestHelper
:CreateAndAddZoneNode(z
, c
, x
, y
)
305 local node
= self
:CreateGraphNode(c
, x
, y
)
306 if not node
then return end -- exception for Wrath changeover
307 -- Not going to merge nodes.
308 --[[local closest, travel_time = nil, 0
310 for i, n in ipairs(z) do
311 local t = math.sqrt((n.x-node.x)*(n.x-node.x)+(n.y-node.y)*(n.y-node.y))
312 if not closest or t < travel_time then
313 closest, travel_time = n, t
317 if closest and travel_time < 10 then
318 closest.x = (closest.x * closest.w + node.x)/(closest.w+1)
319 closest.y = (closest.y * closest.w + node.y)/(closest.w+1)
320 closest.w = closest.w + 1
321 self.world_graph:DestroyNode(node)
324 table.insert(z
, node
)
329 function QuestHelper
:CreateAndAddStaticNodePair(data
)
332 if data
[5] and self
.named_nodes
[data
[5]]
then
333 node1
= self
.named_nodes
[data
[5]]
335 node1
= self
:CreateAndAddZoneNode(self
.zone_nodes
[data
[1][1]]
, data
[1])
336 if not node1
then return end -- exception for Wrath changeover
337 if data
[5] then self
.named_nodes
[data
[5]]
= node1
end
340 if data
[6] and self
.named_nodes
[data
[6]]
then
341 node2
= self
.named_nodes
[data
[6]]
343 node2
= self
:CreateAndAddZoneNode(self
.zone_nodes
[data
[2][1]]
, data
[2])
344 if not node2
then return end -- exception for Wrath changeover
345 if data
[6] then self
.named_nodes
[data
[6]]
= node2
end
348 node1
.name
= node1
.name
or "route to "..QuestHelper_NameLookup
[data
[2][1]]
349 node2
.name
= node2
.name
or "route to "..QuestHelper_NameLookup
[data
[1][1]]
351 node1
:Link(node2
, data
[3])
353 if not data
[4] then -- If data[4] is true, then this is a one-way trip.
354 node2
:Link(node1
, data
[3])
361 function QuestHelper
:GetNodeByName(name
, fallback_data
)
362 local node
= self
.named_nodes
[name
]
363 if not node
and fallback_data
then
364 node
= self
:CreateAndAddZoneNode(self
.zone_nodes
[fallback_data
[1]]
, fallback_data
)
365 self
.named_nodes
[name
] = node
370 local function nodeLeavesContinent(node
, c
)
372 for n
, d
in pairs(node
.n
) do
381 local function isGoodPath(start_node
, end_node
, i
, j
)
382 -- Checks to make sure a path doesn't leave the continent only to reenter it.
385 if end_node
.c
== i
then
388 end_node
= end_node
.p
389 if end_node
.c
== j
then
393 return end_node
== start_node
398 local function shouldLink(a
, b
)
399 -- TODO: Need to have objectives not create links to unreachable nodes.
405 for id in pairs(a.id_from) do
406 if not b.id_to[id] then
407 for id in pairs(b.id_to) do
408 if not a.id_from[id] then
419 local function getNPCNode(npc
)
420 local npc_objective
= QuestHelper
:GetObjective("monster", npc
)
421 if npc_objective
:Known() then
422 npc_objective
:PrepareRouting()
423 local p
= npc_objective
:Position()
427 node
= QuestHelper
:CreateAndAddZoneNode(p
[1], p
[1].c
, p
[3], p
[4])
430 npc_objective
:DoneRouting()
436 function QuestHelper
:CreateAndAddTransitionNode(z1
, z2
, pos
)
437 if not z1
or not z2
then
438 QuestHelper
:Assert(not QuestHelper
:IsWrath(), "Zone couldn't be found, and should have been")
442 local node
= self
:CreateGraphNode(pos
)
444 local closest
, travel_time
= nil, 0
446 for i
, n
in ipairs(z1
) do
447 local t
= math
.sqrt((n
.x
-node
.x
)*(n
.x
-node
.x
)+(n
.y
-node
.y
)*(n
.y
-node
.y
))
448 if not closest
or t
< travel_time
then
449 closest
, travel_time
= n
, t
454 for i
, n
in ipairs(z2
) do
455 local t
= math
.sqrt((n
.x
-node
.x
)*(n
.x
-node
.x
)+(n
.y
-node
.y
)*(n
.y
-node
.y
))
456 if not closest
or t
< travel_time
then
457 closest
, travel_time
= n
, t
462 if closest
and travel_time
< 10 then
463 --QuestHelper:TextOut("Node already exists at "..closest.x..", "..closest.y..", name="..(closest.name or "nil"))
464 closest
.x
= (closest
.x
* closest
.w
+ node
.x
)/(closest
.w
+1)
465 closest
.y
= (closest
.y
* closest
.w
+ node
.y
)/(closest
.w
+1)
466 closest
.w
= closest
.w
+ 1
467 local z1_has
, z2_has
= false, false
469 -- Just because the node already exists, doesn't mean its already in both lists!
471 for i
, n
in ipairs(z1
) do
479 for i
, n
in ipairs(z2
) do
489 if not z1_has
then table.insert(z1
, closest
) end
490 if not z2_has
then table.insert(z2
, closest
) end
492 self
.world_graph
:DestroyNode(node
)
496 table.insert(z1
, node
)
497 if z1
~= z2
then table.insert(z2
, node
) end
503 function QuestHelper
:ReleaseObjectivePathingInfo(o
)
505 for z
, pl
in pairs(o
.p
) do
506 self
:ReleaseTable(o
.d
[z
])
508 for i
, p
in ipairs(pl
) do
509 self
:ReleaseTable(p
[2])
513 self
:ReleaseTable(pl
)
516 self
:ReleaseTable(o
.d
)
517 self
:ReleaseTable(o
.p
)
518 self
:ReleaseTable(o
.nm
)
519 self
:ReleaseTable(o
.nm2
)
520 self
:ReleaseTable(o
.nl
)
522 local cache
= o
.distance_cache
523 for k
, v
in pairs(cache
) do
527 self
:ReleaseTable(cache
)
529 o
.d
, o
.p
, o
.nm
, o
.nm2
, o
.nl
= nil, nil, nil, nil, nil
530 o
.distance_cache
= nil
531 o
.pos
, o
.sop
= nil, nil -- ResetPathing will preserve these values if needed.
536 function QuestHelper
:SetupTeleportInfo(info
, can_create
)
537 self
:TeleportInfoClear(info
)
539 if QuestHelper_Home
then
540 local node
= self
:GetNodeByName("HOME_PORTAL", can_create
and QuestHelper_Home
)
542 local cooldown
= self
:ItemCooldown(6948)
544 self
:SetTeleportInfoTarget(info
, node
, GetTime()-60*60+cooldown
, 60*60, 10)
547 self
.defered_graph_reset
= true
551 -- TODO: Compact this. . . and find a better way to tell if the player has a spell.
553 if GetSpellTexture("Teleport: Darnassus") then
554 local node
= self
:GetNodeByName("DARNASSUS_PORTAL", can_create
and DARNASSUS_PORTAL
)
555 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
558 if GetSpellTexture("Teleport: Exodar") then
559 local node
= self
:GetNodeByName("EXODAR_PORTAL", can_create
and EXODAR_PORTAL
)
560 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
563 if GetSpellTexture("Teleport: Ironforge") then
564 local node
= self
:GetNodeByName("IRONFORGE_PORTAL", can_create
and IRONFORGE_PORTAL
)
565 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
568 if GetSpellTexture("Teleport: Moonglade") then
569 local node
= self
:GetNodeByName("MOONGLADE_PORTAL", can_create
and MOONGLADE_PORTAL
)
570 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10) else self
.defered_graph_reset
= true end
573 if GetSpellTexture("Teleport: Orgrimmar") then
574 local node
= self
:GetNodeByName("ORGRIMMAR_PORTAL", can_create
and ORGRIMMAR_PORTAL
)
575 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
578 if GetSpellTexture("Teleport: Shattrath") then
579 local node
= self
:GetNodeByName("SHATTRATH_CITY_PORTAL", can_create
and SHATTRATH_CITY_PORTAL
)
580 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
583 if GetSpellTexture("Teleport: Silvermoon") then
584 local node
= self
:GetNodeByName("SILVERMOON_CITY_PORTAL", can_create
and SILVERMOON_CITY_PORTAL
)
585 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
588 if GetSpellTexture("Teleport: Stormwind") then
589 local node
= self
:GetNodeByName("STORMWIND_CITY_PORTAL", can_create
and STORMWIND_CITY_PORTAL
)
590 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
593 if GetSpellTexture("Teleport: Thunder Bluff") then
594 local node
= self
:GetNodeByName("THUNDER_BLUFF_PORTAL", can_create
and THUNDER_BLUFF_PORTAL
)
595 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
598 if GetSpellTexture("Teleport: Undercity") then
599 local node
= self
:GetNodeByName("UNDERCITY_PORTAL", can_create
and UNDERCITY_PORTAL
)
600 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
603 self
:SetTeleportInfoReagent(info
, 17031, self
:CountItem(17031))
606 function QuestHelper
:ResetPathing()
607 for key
in pairs(self
.named_nodes
) do
608 self
.named_nodes
[key
] = nil
611 -- Objectives may include cached information that depends on the world graph.
614 while i
<= #self
.prepared_objectives
do
615 local o
= self
.prepared_objectives
[i
]
617 if o
.setup_count
== 0 then
618 table.remove(self
.prepared_objectives
, i
)
619 self
:ReleaseObjectivePathingInfo(o
)
621 -- Routing should reset the positions of objectives in the route after the reset is complete.
623 self
:ReleaseObjectivePathingInfo(o
)
628 local to_readd
= self
.prepared_objectives
629 self
.prepared_objectives
= self
.old_prepared_objectives
or {}
630 self
.old_prepared_objectives
= to_readd
632 local zone_nodes
= self
.zone_nodes
633 if not zone_nodes
then
635 self
.zone_nodes
= zone_nodes
638 local flight_master_nodes
= self
.flight_master_nodes
639 if not flight_master_nodes
then
640 flight_master_nodes
= {}
641 self
.flight_master_nodes
= flight_master_nodes
643 for key
in pairs(flight_master_nodes
) do
644 flight_master_nodes
[key
] = nil
648 self
.world_graph
:Reset()
651 local continent_scales_x
, continent_scales_y
= self
.continent_scales_x
, self
.continent_scales_y
652 if not continent_scales_x
then
653 continent_scales_x
= {}
654 continent_scales_y
= {}
655 self
.continent_scales_x
= continent_scales_x
656 self
.continent_scales_y
= continent_scales_y
659 for c
in pairs(self
.Astrolabe
:GetMapVirtualContinents()) do
660 if not continent_scales_x
[c
] then
661 local _
, x
, y
= self
.Astrolabe
:ComputeDistance(c
, 0, 0.25, 0.25, c
, 0, 0.75, 0.75)
663 continent_scales_x
[c
] = x
*walkspeed_multiplier
*2
664 continent_scales_y
[c
] = y
*walkspeed_multiplier
*2
668 for i
, name
in pairs(QuestHelper_NameLookup
) do
669 local z
= zone_nodes
[i
]
671 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.
673 z
.i
, z
.c
, z
.z
= i
, unpack(QuestHelper_ZoneLookup
[i
])
675 for key
in pairs(z
) do
678 z
.i
, z
.c
, z
.z
= i
, unpack(QuestHelper_ZoneLookup
[i
])
682 self
:SetupTeleportInfo(self
.teleport_info
, true)
685 --[[for node, info in pairs(self.teleport_info.node) do
686 self:TextOut("You can teleport to "..(node.name or "nil").. " in "..self:TimeString(info[1]+info[2]-GetTime()))
689 if self
.faction
== 1 then
690 for i
, data
in ipairs(static_alliance_routes
) do
691 self
:CreateAndAddStaticNodePair(data
)
693 elseif self
.faction
== 2 then
694 for i
, data
in ipairs(static_horde_routes
) do
695 self
:CreateAndAddStaticNodePair(data
)
699 for i
, data
in ipairs(static_shared_routes
) do
700 self
:CreateAndAddStaticNodePair(data
)
703 if self
.player_level
>= 58 then
704 dark_portal_route
[3] = 5
706 -- If you can't take the route yet, we'll still add it and just pretend it will take a really long time.
707 dark_portal_route
[3] = 86400
710 self
:CreateAndAddStaticNodePair(dark_portal_route
)
712 local st
= self
:CreateTable("ResetPathing local st")
714 for i
, data
in pairs(static_zone_transitions
) do
715 st
[1], st
[2], st
[3] = data
[1], data
[3], data
[4]
717 local transnode
= self
:CreateAndAddTransitionNode(zone_nodes
[data
[1]]
,
720 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
723 self
:ReleaseTable(st
)
725 -- Create and link the flight route nodes.
726 local flight_times
= self
.flight_times
727 if not flight_times
then
728 self
:buildFlightTimes()
729 flight_times
= self
.flight_times
732 for start
, list
in pairs(flight_times
) do
733 for dest
, duration
in pairs(list
) do
734 local a_npc
, b_npc
= self
:getFlightInstructor(start
), self
:getFlightInstructor(dest
)
736 if a_npc
and b_npc
then
737 local a
, b
= flight_master_nodes
[start
], flight_master_nodes
[dest
]
740 a
= getNPCNode(a_npc
)
742 flight_master_nodes
[start
] = a
743 a
.name
= (select(3, string.find(start
, "^(.*),")) or start
).." flight point"
748 b
= getNPCNode(b_npc
)
750 flight_master_nodes
[dest
] = b
751 b
.name
= (select(3, string.find(dest
, "^(.*),")) or dest
).." flight point"
756 a
:Link(b
, duration
+5)
763 -- id_from, id_to, and id_local will be used in determining whether there is a point to linking nodes together.
764 for i
, n
in ipairs(self
.world_graph
.nodes
) do
765 n
.id_from
= self
:CreateTable("ResetPathing n.id_from")
766 n
.id_to
= self
:CreateTable("ResetPathing n.id_to")
767 n
.id_local
= self
:CreateTable("ResetPathing n.id_local")
770 -- Setup the local ids a node exists in.
771 for i
, list
in pairs(zone_nodes
) do
772 for _
, n
in ipairs(list
) do
779 -- Figure out where each node can come from or go to.
780 for i
, list
in pairs(zone_nodes
) do
781 for _
, node
in ipairs(list
) do
782 for n
in pairs(node
.n
) do
783 for id
in pairs(n
.id_local
) do node
.id_to
[id
] = true end
784 for id
in pairs(node
.id_local
) do n
.id_from
[id
] = true end
789 -- We'll treat 0 as a special id for where ever it is the player happens to be.
790 for node
in pairs(self
.teleport_info
.node
) do
791 node
.id_from
[0] = true
794 -- Will go through each zone and link all the nodes we have so far with every other node.
795 for _
, list
in pairs(zone_nodes
) do
798 if shouldLink(list
[i
], list
[j
]) then
799 list
[i
]:Link(list
[j
], cont_dist(list
[i
], list
[j
]))
807 -- We don't need to know where the nodes can go or come from now.
808 for i
, n
in ipairs(self
.world_graph
.nodes
) do
809 self
:ReleaseTable(n
.id_from
)
810 self
:ReleaseTable(n
.id_to
)
811 self
:ReleaseTable(n
.id_local
)
812 n
.id_from
, n
.id_to
, n
.id_local
= nil, nil, nil
815 -- TODO: This is a work around until I fix shouldLink
816 for start
, list
in pairs(flight_times
) do
817 for dest
, duration
in pairs(list
) do
818 local a
, b
= flight_master_nodes
[start
], flight_master_nodes
[dest
]
820 a
:Link(b
, duration
+5)
826 -- self.world_graph:SanityCheck()
828 -- Remove objectives again, since we created some for the flight masters.
830 local o
= table.remove(self
.prepared_objectives
)
831 if not o
then break end
833 self
:ReleaseObjectivePathingInfo(o
)
835 if o
.setup_count
> 0 then
836 -- There's a chance an objective could end up in the list twice, but we'll deal with that by not actually
837 -- adding locations for it if it's already setup.
838 table.insert(to_readd
, o
)
843 local obj
= table.remove(to_readd
)
844 if not obj
then break end
846 if not obj
.setup
then -- In case the objective was added multiple times to the to_readd list.
847 obj
.d
= QuestHelper
:CreateTable("ResetPathing obj.d")
848 obj
.p
= QuestHelper
:CreateTable("ResetPathing obj.p")
849 obj
.nm
= QuestHelper
:CreateTable("ResetPathing obj.nm")
850 obj
.nm2
= QuestHelper
:CreateTable("ResetPathing obj.nm2")
851 obj
.nl
= QuestHelper
:CreateTable("ResetPathing obj.nl")
852 obj
.distance_cache
= QuestHelper
:CreateTable("ResetPathing obj.distance_cache")
853 obj
:AppendPositions(obj
, 1, nil)
859 self
.pos
[1] = self
.zone_nodes
[self
.i
]
860 for i
, n
in ipairs(self
.pos
[1]) do
861 local a
, b
= n
.x
-self
.pos
[3], n
.y
-self
.pos
[4]
862 self
.pos
[2][i
] = math
.sqrt(a
*a
+b
*b
)
866 -- And if all went according to plan, we now have a graph we can follow to get from anywhere to anywhere.
868 if self
.graph_walker
then
869 self
.graph_walker
:GraphChanged()
874 function QuestHelper
:Disallowed(index
)
875 return QuestHelper_RestrictedZones
[index
] ~= QuestHelper_RestrictedZones
[self
.i
]