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 MOONGLADE_PORTAL
= {20,0.563,0.320, "Moonglade portal site"}
11 local SILVERMOON_CITY_PORTAL
= {52,0.583,0.192, "Silvermoon City portal site"}
12 local UNDERCITY_PORTAL
= {45,0.846,0.163, "Undercity portal site"}
13 local ORGRIMMAR_PORTAL
= {1,0.386,0.859, "Orgrimmar portal site"}
14 local THUNDER_BLUFF_PORTAL
= {23,0.222,0.168, "Thunder Bluff portal site"}
16 local static_horde_routes
=
18 {{7, 0.505, 0.124}, {38, 0.313, 0.303}, 210}, -- Durotar <--> Grom'gol Base Camp
19 {{38, 0.316, 0.289}, {43, 0.621, 0.591}, 210}, -- Grom'gol Base Camp <--> Tirisfal Glades
20 {{43, 0.605, 0.587}, {7, 0.509, 0.141}, 210}, -- Tirisfal Glades <--> Durotar
21 {{45, 0.549, 0.11}, {52, 0.495, 0.148}, 5}, -- Undercity <--> Silvermoon City
23 {{7, 0.413, 0.178}, {65, 0.414, 0.536}, 5}, -- Durotar <--> Warsong Hold
24 {{43, 0.591, 0.590}, {70, 0.777, 0.283}, 5}, -- Tirisfal Glades <--> Vengeance Landing
26 {{60, 0.592, 0.483}, SILVERMOON_CITY_PORTAL
, 5, true, nil, "SILVERMOON_CITY_PORTAL"}, -- Shattrath City --> Silvermoon City
27 {{60, 0.528, 0.531}, THUNDER_BLUFF_PORTAL
, 5, true, nil, "THUNDER_BLUFF_PORTAL"}, -- Shattrath City --> Thunder Bluff
28 {{60, 0.522, 0.529}, ORGRIMMAR_PORTAL
, 5, true, nil, "ORGRIMMAR_PORTAL"}, -- Shattrath City --> Orgrimmar
29 {{60, 0.517, 0.525}, UNDERCITY_PORTAL
, 5, true, nil, "UNDERCITY_PORTAL"} -- Shattrath City --> Undercity
33 local static_alliance_routes
=
35 {{36, 0.639, 0.083}, {25, 0.764, 0.512}, 120}, -- Deeprun Tram
36 {{10, 0.718, 0.565}, {51, 0.047, 0.636}, 210}, -- Theramore Isle <--> Menethil Harmor
38 {QuestHelper_ConvertCoordsFromWrath({36, 0.183, 0.255}), {65, 0.597, 0.694}, 210}, -- Stormwind City <--> Valiance Keep (note: new Stormwind location)
39 {{51, 0.047, 0.571}, {70, 0.612, 0.626}, 210}, -- Menethil <--> Daggercap Bay
41 {{60, 0.558, 0.366}, STORMWIND_CITY_PORTAL
, 5, true, nil, "STORMWIND_CITY_PORTAL"}, -- Shattrath City --> Stormwind City
42 {{60, 0.563, 0.37}, IRONFORGE_PORTAL
, 5, true, nil, "IRONFORGE_PORTAL"}, -- Shattrath City --> Ironforge
43 {{60, 0.552, 0.364}, DARNASSUS_PORTAL
, 5, true, nil, "DARNASSUS_PORTAL"}, -- Shattrath City --> Darnassus
44 {{60, 0.596, 0.467}, EXODAR_PORTAL
, 5, true, nil, "EXODAR_PORTAL"} -- Shattrath City --> Exodar
47 if QuestHelper
:IsWrath() then -- siiiiigh
48 table.insert(static_alliance_routes
, {{36, 0.228, 0.560}, {16, 0.323, 0.441}, 210}) -- Stormwind City <--> Auberdine (note: new Stormwind location)
50 table.insert(static_alliance_routes
, {{51, 0.044, 0.569}, {16, 0.323, 0.441}, 210}) -- Menethil Harbor <--> Auberdine
53 local static_shared_routes
=
55 {{11, 0.638, 0.387}, {38, 0.257, 0.73}, 210}, -- Ratchet <--> Booty Bay
56 {{40, 0.318, 0.503}, {32, 0.347, 0.84}, 130}, -- Burning Steppes <--> Searing Gorge
58 -- More Alliance routes than anything, but without them theres no valid path to these areas for Horde characters.
59 {{24, 0.559, 0.896}, {21, 0.305, 0.414}, 5}, -- Rut'Theran Village <--> Darnassus
60 {{16, 0.332, 0.398}, {24, 0.548, 0.971}, 210}, -- Auberdine <--> Rut'Theran Village
61 {{16, 0.306, 0.409}, {3, 0.2, 0.546}, 210}, -- Auberdine <--> Azuremyst Isle
63 -- Route to new zone. Not valid, exists only to keep routing from exploding if you don't have the flight routes there.
64 {{41, 0.5, 0.5}, {64, 0.5, 0.5}, 7200}, -- Eversong Woods <--> Sunwell
66 {{70, 0.235, 0.578}, {68, 0.496, 0.784}, 210}, -- Kamagua <--> Moa'ki
67 {{65, 0.789, 0.536}, {68, 0.480, 0.787}, 210}, -- Unu'pe <--> Moa'ki
70 -- Darkportal is handled specially, depending on whether or not you're level 58+ or not.
71 local dark_portal_route
= {{33, 0.587, 0.599}, {56, 0.898, 0.502}, 5}
73 local static_zone_transitions
=
75 {2, 11, 0.687, 0.872}, -- Ashenvale <--> The Barrens
76 {2, 6, 0.423, 0.711}, -- Ashenvale <--> Stonetalon Mountains
77 {2, 15, 0.954, 0.484}, -- Ashenvale <--> Azshara
78 {2, 16, 0.289, 0.144}, -- Ashenvale <--> Darkshore
79 {2, 13, 0.557, 0.29}, -- Ashenvale <--> Felwood
80 {21, 24, 0.894, 0.358}, -- Darnassus <--> Teldrassil
81 {22, 11, 0.697, 0.604}, -- Mulgore <--> The Barrens
82 {22, 23, 0.376, 0.33}, -- Mulgore <--> Thunder Bluff
83 {22, 23, 0.403, 0.193}, -- Mulgore <--> Thunder Bluff
84 {3, 12, 0.247, 0.494}, -- Azuremyst Isle <--> The Exodar
85 {3, 12, 0.369, 0.469}, -- Azuremyst Isle <--> The Exodar
86 {3, 9, 0.42, 0.013}, -- Azuremyst Isle <--> Bloodmyst Isle
87 {4, 6, 0.539, 0.032}, -- Desolace <--> Stonetalon Mountains
88 {4, 17, 0.428, 0.976}, -- Desolace <--> Feralas
89 {5, 18, 0.865, 0.115}, -- Silithus <--> Un'Goro Crater
90 {7, 11, 0.341, 0.424}, -- Durotar <--> The Barrens
91 {7, 1, 0.455, 0.121}, -- Durotar <--> Orgrimmar
92 {8, 18, 0.269, 0.516}, -- Tanaris <--> Un'Goro Crater
93 {8, 14, 0.512, 0.21}, -- Tanaris <--> Thousand Needles
94 {10, 11, 0.287, 0.472}, -- Dustwallow Marsh <--> The Barrens
95 {10, 11, 0.563, 0.077}, -- Dustwallow Marsh <--> The Barrens
96 {11, 14, 0.442, 0.915}, -- The Barrens <--> Thousand Needles
97 {13, 19, 0.685, 0.06}, -- Felwood <--> Winterspring
98 {13, 20, 0.669, -0.063}, -- Felwood <--> Moonglade
99 {1, 11, 0.118, 0.69}, -- Orgrimmar <--> The Barrens
100 {17, 14, 0.899, 0.46}, -- Feralas <--> Thousand Needles
101 {6, 11, 0.836, 0.973}, -- Stonetalon Mountains <--> The Barrens
102 {26, 48, 0.521, 0.7}, -- Alterac Mountains <--> Hillsbrad Foothills
103 {26, 35, 0.173, 0.482}, -- Alterac Mountains <--> Silverpine Forest
104 {26, 50, 0.807, 0.347}, -- Alterac Mountains <--> Western Plaguelands
105 {39, 51, 0.454, 0.89}, -- Arathi Highlands <--> Wetlands
106 {39, 48, 0.2, 0.293}, -- Arathi Highlands <--> Hillsbrad Foothills
107 {27, 29, 0.49, 0.071}, -- Badlands <--> Loch Modan
108 {27, 32, -0.005, 0.636}, -- Badlands <--> Searing Gorge
109 {33, 46, 0.519, 0.051}, -- Blasted Lands <--> Swamp of Sorrows
110 {40, 30, 0.79, 0.842}, -- Burning Steppes <--> Redridge Mountains
111 {47, 31, 0.324, 0.363}, -- Deadwind Pass <--> Duskwood
112 {47, 46, 0.605, 0.41}, -- Deadwind Pass <--> Swamp of Sorrows
113 {28, 25, 0.534, 0.349}, -- Dun Morogh <--> Ironforge
114 {28, 29, 0.863, 0.514}, -- Dun Morogh <--> Loch Modan
115 {28, 29, 0.844, 0.31}, -- Dun Morogh <--> Loch Modan
116 {31, 37, 0.801, 0.158}, -- Duskwood <--> Elwynn Forest
117 {31, 37, 0.15, 0.214}, -- Duskwood <--> Elwynn Forest
118 {31, 38, 0.447, 0.884}, -- Duskwood <--> Stranglethorn Vale
119 {31, 38, 0.209, 0.863}, -- Duskwood <--> Stranglethorn Vale
120 {31, 30, 0.941, 0.103}, -- Duskwood <--> Redridge Mountains
121 {31, 49, 0.079, 0.638}, -- Duskwood <--> Westfall
122 {34, 50, 0.107, 0.726}, -- Eastern Plaguelands <--> Western Plaguelands
123 {34, 44, 0.625, 0.03}, -- Eastern Plaguelands <--> Ghostlands
124 {37, 36, 0.321, 0.493}, -- Elwynn Forest <--> Stormwind City -- Don't need to convert because it's in Elwynn coordinates, not Stormwind coordinates
125 {37, 49, 0.202, 0.804}, -- Elwynn Forest <--> Westfall
126 {37, 30, 0.944, 0.724}, -- Elwynn Forest <--> Redridge Mountains
127 {41, 52, 0.567, 0.494}, -- Eversong Woods <--> Silvermoon City
128 {41, 44, 0.486, 0.916}, -- Eversong Woods <--> Ghostlands
129 {35, 43, 0.678, 0.049}, -- Silverpine Forest <--> Tirisfal Glades
130 {42, 50, 0.217, 0.264}, -- The Hinterlands <--> Western Plaguelands
131 {43, 45, 0.619, 0.651}, -- Tirisfal Glades <--> Undercity
132 {43, 50, 0.851, 0.703}, -- Tirisfal Glades <--> Western Plaguelands
133 {38, 49, 0.292, 0.024}, -- Stranglethorn Vale <--> Westfall
134 {48, 35, 0.137, 0.458}, -- Hillsbrad Foothills <--> Silverpine Forest
135 {48, 42, 0.899, 0.253}, -- Hillsbrad Foothills <--> The Hinterlands
136 {29, 51, 0.252, 0}, -- Loch Modan <--> Wetlands
138 -- These are just guesses, since I haven't actually been to these areas.
139 {58, 60, 0.783, 0.545}, -- Nagrand <--> Shattrath City
140 {60, 55, 0.782, 0.492}, -- Shattrath City <--> Terokkar Forest
141 {54, 59, 0.842, 0.284}, -- Blade's Edge Mountains <--> Netherstorm
142 {54, 57, 0.522, 0.996}, -- Blade's Edge Mountains <--> Zangarmarsh
143 {54, 57, 0.312, 0.94}, -- Blade's Edge Mountains <--> Zangarmarsh
144 {56, 55, 0.353, 0.901}, -- Hellfire Peninsula <--> Terokkar Forest
145 {56, 57, 0.093, 0.519}, -- Hellfire Peninsula <--> Zangarmarsh
146 {58, 55, 0.8, 0.817}, -- Nagrand <--> Terokkar Forest
147 {58, 57, 0.343, 0.159}, -- Nagrand <--> Zangarmarsh
148 {58, 57, 0.754, 0.331}, -- Nagrand <--> Zangarmarsh
149 {53, 55, 0.208, 0.271}, -- Shadowmoon Valley <--> Terokkar Forest
150 {55, 57, 0.341, 0.098}, -- Terokkar Forest <--> Zangarmarsh
152 -- Most of these are also guesses :)
153 {65, 72, 0.547, 0.059}, -- Borean Tundra <--> Sholazar Basin
154 {65, 68, 0.967, 0.359}, -- Borean Tundra <--> Dragonblight
155 {74, 72, 0.208, 0.191}, -- Wintergrasp <--> Sholazar
156 {68, 74, 0.250, 0.410}, -- Dragonblight <--> Wintergrasp
157 {68, 71, 0.359, 0.155}, -- Dragonblight <--> Icecrown
158 {68, 66, 0.612, 0.142}, -- Dragonblight <--> Crystalsong
159 {68, 75, 0.900, 0.200}, -- Dragonblight <--> Zul'Drak
160 {68, 69, 0.924, 0.304}, -- Dragonblight <--> Grizzly Hills
161 {68, 69, 0.931, 0.634}, -- Dragonblight <--> Grizzly Hills
162 {70, 69, 0.540, 0.042}, -- Howling Fjord <--> Grizzly Hills
163 {70, 69, 0.233, 0.074}, -- Howling Fjord <--> Grizzly Hills
164 {70, 69, 0.753, 0.060}, -- Howling Fjord <--> Grizzly Hills
165 {69, 75, 0.432, 0.253}, -- Grizzly Hills <--> Zul'Drak
166 {69, 75, 0.583, 0.136}, -- Grizzly Hills <--> Zul'Drak
167 {66, 75, 0.967, 0.599}, -- Crystalsong <--> Zul'Drak
168 {66, 71, 0.156, 0.085}, -- Crystalsong <--> Icecrown
169 {66, 73, 0.706, 0.315}, -- Crystalsong <--> Storm Peaks
170 {66, 73, 0.839, 0.340}, -- Crystalsong <--> Storm Peaks
171 {71, 73, 0.920, 0.767}, -- Icecrown <--> Storm Peaks
174 local walkspeed_multiplier
= 1/7 -- Every yard walked takes this many seconds.
176 QuestHelper
.prepared_objectives
= {}
177 QuestHelper
.named_nodes
= {}
179 local function cont_dist(a
, b
)
180 local x
, y
= a
.x
-b
.x
, a
.y
-b
.y
181 return math
.sqrt(x
*x
+y
*y
)
184 function QuestHelper
:ComputeRoute(p1
, p2
)
185 if not p1
or not p2
then QuestHelper
:Error("Boom!") end
186 local graph
= self
.world_graph
188 graph
:PrepareSearch()
192 for i
, n
in ipairs(el
) do
198 for i
, n
in ipairs(p1
[1]) do
199 graph
:AddRouteStartNode(n
, l
[i
], el
)
202 local e
= graph
:DoRouteSearch(el
)
208 if p1
[1] == p2
[1] then
209 local x
, y
= p1
[3]-p2
[3], p1
[4]-p2
[4]
210 local d2
= math
.sqrt(x
*x
+y
*y
)
220 -- Let's annotate the hell out of this
221 -- 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.)
222 -- 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 :(
223 function QuestHelper
:ComputeTravelTime(p1
, p2
)
224 if not p1
or not p2
then QuestHelper
:Error("Boom!") end
225 local graph
= self
.world_graph
227 graph
:PrepareSearch()
229 local l
= p2
[2] -- Distance to zone boundaries in p2
230 local el
= p2
[1] -- Zone object for the zone that p2 is in
231 for i
, n
in ipairs(el
) do -- i is the zone index, n is the zone data
232 n
.e
, n
.w
= l
[i
], 1 -- n.e is distance from p2 to the current zone boundary, n.w is 1 (weight?)
234 n
.s
= 3 -- this is "state", I think it means "visited". TODO: untangle n.s and make it suck less than it currently does
237 l
= p1
[2] -- Distance to zone boundaries, again
238 for i
, n
in ipairs(p1
[1]) do
239 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?
242 local e
= graph
:DoSearch(el
)
246 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
248 if p1
[1] == p2
[1] then -- if they're in the same zone, we allow the user to walk from one point to another
249 local x
, y
= p1
[3]-p2
[3], p1
[4]-p2
[4]
250 d
= math
.min(d
, math
.sqrt(x
*x
+y
*y
))
256 function QuestHelper
:CreateGraphNode(c
, x
, y
, n
)
257 local node
= self
.world_graph
:CreateNode()
265 if not QuestHelper_ZoneLookup
[c
[1]]
then -- exception for Wrath changeover
266 QuestHelper
:Assert(not QuestHelper
:IsWrath(), "Zone couldn't be found, and should have been")
269 local cont
, zone
= unpack(QuestHelper_ZoneLookup
[c
[1]]
)
271 node
.x
, node
.y
= self
.Astrolabe
:TranslateWorldMapPosition(cont
, zone
, c
[2], c
[3], cont
, 0)
272 node
.x
= node
.x
* self
.continent_scales_x
[node
.c
]
273 node
.y
= node
.y
* self
.continent_scales_y
[node
.c
]
274 node
.name
= c
[5] or QuestHelper_NameLookup
[c
[1]]
281 function QuestHelper
:CreateAndAddZoneNode(z
, c
, x
, y
)
282 local node
= self
:CreateGraphNode(c
, x
, y
)
283 if not node
then return end -- exception for Wrath changeover
284 -- Not going to merge nodes.
285 --[[local closest, travel_time = nil, 0
287 for i, n in ipairs(z) do
288 local t = math.sqrt((n.x-node.x)*(n.x-node.x)+(n.y-node.y)*(n.y-node.y))
289 if not closest or t < travel_time then
290 closest, travel_time = n, t
294 if closest and travel_time < 10 then
295 closest.x = (closest.x * closest.w + node.x)/(closest.w+1)
296 closest.y = (closest.y * closest.w + node.y)/(closest.w+1)
297 closest.w = closest.w + 1
298 self.world_graph:DestroyNode(node)
301 table.insert(z
, node
)
306 function QuestHelper
:CreateAndAddStaticNodePair(data
)
309 if data
[5] and self
.named_nodes
[data
[5]]
then
310 node1
= self
.named_nodes
[data
[5]]
312 node1
= self
:CreateAndAddZoneNode(self
.zone_nodes
[data
[1][1]]
, data
[1])
313 if not node1
then return end -- exception for Wrath changeover
314 if data
[5] then self
.named_nodes
[data
[5]]
= node1
end
317 if data
[6] and self
.named_nodes
[data
[6]]
then
318 node2
= self
.named_nodes
[data
[6]]
320 node2
= self
:CreateAndAddZoneNode(self
.zone_nodes
[data
[2][1]]
, data
[2])
321 if not node2
then return end -- exception for Wrath changeover
322 if data
[6] then self
.named_nodes
[data
[6]]
= node2
end
325 node1
.name
= node1
.name
or "route to "..QuestHelper_NameLookup
[data
[2][1]]
326 node2
.name
= node2
.name
or "route to "..QuestHelper_NameLookup
[data
[1][1]]
328 node1
:Link(node2
, data
[3])
330 if not data
[4] then -- If data[4] is true, then this is a one-way trip.
331 node2
:Link(node1
, data
[3])
334 self
:yieldIfNeeded(0.1)
338 function QuestHelper
:GetNodeByName(name
, fallback_data
)
339 local node
= self
.named_nodes
[name
]
340 if not node
and fallback_data
then
341 node
= self
:CreateAndAddZoneNode(self
.zone_nodes
[fallback_data
[1]]
, fallback_data
)
342 self
.named_nodes
[name
] = node
347 local function nodeLeavesContinent(node
, c
)
349 for n
, d
in pairs(node
.n
) do
358 local function isGoodPath(start_node
, end_node
, i
, j
)
359 -- Checks to make sure a path doesn't leave the continent only to reenter it.
362 if end_node
.c
== i
then
365 end_node
= end_node
.p
366 if end_node
.c
== j
then
370 return end_node
== start_node
375 local function shouldLink(a
, b
)
376 -- TODO: Need to have objectives not create links to unreachable nodes.
382 for id in pairs(a.id_from) do
383 if not b.id_to[id] then
384 for id in pairs(b.id_to) do
385 if not a.id_from[id] then
396 local function getNPCNode(npc
)
397 local npc_objective
= QuestHelper
:GetObjective("monster", npc
)
398 if npc_objective
:Known() then
399 npc_objective
:PrepareRouting()
400 local p
= npc_objective
:Position()
404 node
= QuestHelper
:CreateAndAddZoneNode(p
[1], p
[1].c
, p
[3], p
[4])
407 npc_objective
:DoneRouting()
413 function QuestHelper
:CreateAndAddTransitionNode(z1
, z2
, pos
)
414 if not z1
or not z2
then
415 QuestHelper
:Assert(not QuestHelper
:IsWrath(), "Zone couldn't be found, and should have been")
419 local node
= self
:CreateGraphNode(pos
)
421 local closest
, travel_time
= nil, 0
423 for i
, n
in ipairs(z1
) do
424 local t
= math
.sqrt((n
.x
-node
.x
)*(n
.x
-node
.x
)+(n
.y
-node
.y
)*(n
.y
-node
.y
))
425 if not closest
or t
< travel_time
then
426 closest
, travel_time
= n
, t
431 for i
, n
in ipairs(z2
) do
432 local t
= math
.sqrt((n
.x
-node
.x
)*(n
.x
-node
.x
)+(n
.y
-node
.y
)*(n
.y
-node
.y
))
433 if not closest
or t
< travel_time
then
434 closest
, travel_time
= n
, t
439 if closest
and travel_time
< 10 then
440 --QuestHelper:TextOut("Node already exists at "..closest.x..", "..closest.y..", name="..(closest.name or "nil"))
441 closest
.x
= (closest
.x
* closest
.w
+ node
.x
)/(closest
.w
+1)
442 closest
.y
= (closest
.y
* closest
.w
+ node
.y
)/(closest
.w
+1)
443 closest
.w
= closest
.w
+ 1
444 local z1_has
, z2_has
= false, false
446 -- Just because the node already exists, doesn't mean its already in both lists!
448 for i
, n
in ipairs(z1
) do
456 for i
, n
in ipairs(z2
) do
466 if not z1_has
then table.insert(z1
, closest
) end
467 if not z2_has
then table.insert(z2
, closest
) end
469 self
.world_graph
:DestroyNode(node
)
470 self
:yieldIfNeeded(0.2)
473 table.insert(z1
, node
)
474 if z1
~= z2
then table.insert(z2
, node
) end
475 self
:yieldIfNeeded(0.1)
480 function QuestHelper
:ReleaseObjectivePathingInfo(o
)
482 for z
, pl
in pairs(o
.p
) do
483 self
:ReleaseTable(o
.d
[z
])
485 for i
, p
in ipairs(pl
) do
486 self
:ReleaseTable(p
[2])
490 self
:ReleaseTable(pl
)
493 self
:ReleaseTable(o
.d
)
494 self
:ReleaseTable(o
.p
)
495 self
:ReleaseTable(o
.nm
)
496 self
:ReleaseTable(o
.nm2
)
497 self
:ReleaseTable(o
.nl
)
499 local cache
= o
.distance_cache
500 for k
, v
in pairs(cache
) do
504 self
:ReleaseTable(cache
)
506 o
.d
, o
.p
, o
.nm
, o
.nm2
, o
.nl
= nil, nil, nil, nil, nil
507 o
.distance_cache
= nil
508 o
.pos
, o
.sop
= nil, nil -- ResetPathing will preserve these values if needed.
513 function QuestHelper
:SetupTeleportInfo(info
, can_create
)
514 self
:TeleportInfoClear(info
)
516 if QuestHelper_Home
then
517 local node
= self
:GetNodeByName("HOME_PORTAL", can_create
and QuestHelper_Home
)
519 local cooldown
= self
:ItemCooldown(6948)
521 self
:SetTeleportInfoTarget(info
, node
, GetTime()-60*60+cooldown
, 60*60, 10)
524 self
.defered_graph_reset
= true
528 -- TODO: Compact this. . . and find a better way to tell if the player has a spell.
530 if GetSpellTexture("Teleport: Darnassus") then
531 local node
= self
:GetNodeByName("DARNASSUS_PORTAL", can_create
and DARNASSUS_PORTAL
)
532 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
535 if GetSpellTexture("Teleport: Exodar") then
536 local node
= self
:GetNodeByName("EXODAR_PORTAL", can_create
and EXODAR_PORTAL
)
537 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
540 if GetSpellTexture("Teleport: Ironforge") then
541 local node
= self
:GetNodeByName("IRONFORGE_PORTAL", can_create
and IRONFORGE_PORTAL
)
542 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
545 if GetSpellTexture("Teleport: Moonglade") then
546 local node
= self
:GetNodeByName("MOONGLADE_PORTAL", can_create
and MOONGLADE_PORTAL
)
547 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10) else self
.defered_graph_reset
= true end
550 if GetSpellTexture("Teleport: Orgrimmar") then
551 local node
= self
:GetNodeByName("ORGRIMMAR_PORTAL", can_create
and ORGRIMMAR_PORTAL
)
552 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
555 if GetSpellTexture("Teleport: Shattrath") then
556 local node
= self
:GetNodeByName("SHATTRATH_CITY_PORTAL", can_create
and SHATTRATH_CITY_PORTAL
)
557 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
560 if GetSpellTexture("Teleport: Silvermoon") then
561 local node
= self
:GetNodeByName("SILVERMOON_CITY_PORTAL", can_create
and SILVERMOON_CITY_PORTAL
)
562 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
565 if GetSpellTexture("Teleport: Stormwind") then
566 local node
= self
:GetNodeByName("STORMWIND_CITY_PORTAL", can_create
and STORMWIND_CITY_PORTAL
)
567 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
570 if GetSpellTexture("Teleport: Thunder Bluff") then
571 local node
= self
:GetNodeByName("THUNDER_BLUFF_PORTAL", can_create
and THUNDER_BLUFF_PORTAL
)
572 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
575 if GetSpellTexture("Teleport: Undercity") then
576 local node
= self
:GetNodeByName("UNDERCITY_PORTAL", can_create
and UNDERCITY_PORTAL
)
577 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
580 self
:SetTeleportInfoReagent(info
, 17031, self
:CountItem(17031))
583 function QuestHelper
:ResetPathing()
584 for key
in pairs(self
.named_nodes
) do
585 self
.named_nodes
[key
] = nil
588 -- Objectives may include cached information that depends on the world graph.
591 while i
<= #self
.prepared_objectives
do
592 local o
= self
.prepared_objectives
[i
]
594 if o
.setup_count
== 0 then
595 table.remove(self
.prepared_objectives
, i
)
596 self
:ReleaseObjectivePathingInfo(o
)
598 -- Routing should reset the positions of objectives in the route after the reset is complete.
600 self
:ReleaseObjectivePathingInfo(o
)
605 local to_readd
= self
.prepared_objectives
606 self
.prepared_objectives
= self
.old_prepared_objectives
or {}
607 self
.old_prepared_objectives
= to_readd
609 local zone_nodes
= self
.zone_nodes
610 if not zone_nodes
then
612 self
.zone_nodes
= zone_nodes
615 local flight_master_nodes
= self
.flight_master_nodes
616 if not flight_master_nodes
then
617 flight_master_nodes
= {}
618 self
.flight_master_nodes
= flight_master_nodes
620 for key
in pairs(flight_master_nodes
) do
621 flight_master_nodes
[key
] = nil
625 self
.world_graph
:Reset()
626 self
:yieldIfNeeded(0.1)
628 local continent_scales_x
, continent_scales_y
= self
.continent_scales_x
, self
.continent_scales_y
629 if not continent_scales_x
then
630 continent_scales_x
= {}
631 continent_scales_y
= {}
632 self
.continent_scales_x
= continent_scales_x
633 self
.continent_scales_y
= continent_scales_y
636 for c
=1,select("#", GetMapContinents()) do
637 if not continent_scales_x
[c
] then
638 local _
, x
, y
= self
.Astrolabe
:ComputeDistance(c
, 0, 0.25, 0.25, c
, 0, 0.75, 0.75)
640 continent_scales_x
[c
] = x
*walkspeed_multiplier
*2
641 continent_scales_y
[c
] = y
*walkspeed_multiplier
*2
645 for i
, name
in pairs(QuestHelper_NameLookup
) do
646 local z
= zone_nodes
[i
]
648 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.
650 z
.i
, z
.c
, z
.z
= i
, unpack(QuestHelper_ZoneLookup
[i
])
652 for key
in pairs(z
) do
655 z
.i
, z
.c
, z
.z
= i
, unpack(QuestHelper_ZoneLookup
[i
])
659 self
:SetupTeleportInfo(self
.teleport_info
, true)
660 self
:yieldIfNeeded(0.1)
662 --[[for node, info in pairs(self.teleport_info.node) do
663 self:TextOut("You can teleport to "..(node.name or "nil").. " in "..self:TimeString(info[1]+info[2]-GetTime()))
666 if self
.faction
== 1 then
667 for i
, data
in ipairs(static_alliance_routes
) do
668 self
:CreateAndAddStaticNodePair(data
)
670 elseif self
.faction
== 2 then
671 for i
, data
in ipairs(static_horde_routes
) do
672 self
:CreateAndAddStaticNodePair(data
)
676 for i
, data
in ipairs(static_shared_routes
) do
677 self
:CreateAndAddStaticNodePair(data
)
680 if self
.player_level
>= 58 then
681 dark_portal_route
[3] = 5
683 -- If you can't take the route yet, we'll still add it and just pretend it will take a really long time.
684 dark_portal_route
[3] = 86400
687 self
:CreateAndAddStaticNodePair(dark_portal_route
)
689 local st
= self
:CreateTable("ResetPathing local st")
691 for i
, data
in pairs(static_zone_transitions
) do
692 st
[1], st
[2], st
[3] = data
[1], data
[3], data
[4]
694 local transnode
= self
:CreateAndAddTransitionNode(zone_nodes
[data
[1]]
,
697 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
700 self
:ReleaseTable(st
)
702 -- Create and link the flight route nodes.
703 local flight_times
= self
.flight_times
704 if not flight_times
then
705 self
:buildFlightTimes()
706 flight_times
= self
.flight_times
709 for start
, list
in pairs(flight_times
) do
710 for dest
, duration
in pairs(list
) do
711 local a_npc
, b_npc
= self
:getFlightInstructor(start
), self
:getFlightInstructor(dest
)
713 if a_npc
and b_npc
then
714 local a
, b
= flight_master_nodes
[start
], flight_master_nodes
[dest
]
717 a
= getNPCNode(a_npc
)
719 flight_master_nodes
[start
] = a
720 a
.name
= (select(3, string.find(start
, "^(.*),")) or start
).." flight point"
725 b
= getNPCNode(b_npc
)
727 flight_master_nodes
[dest
] = b
728 b
.name
= (select(3, string.find(dest
, "^(.*),")) or dest
).." flight point"
733 a
:Link(b
, duration
+5)
737 self
:yieldIfNeeded(0.1)
740 -- id_from, id_to, and id_local will be used in determining whether there is a point to linking nodes together.
741 for i
, n
in ipairs(self
.world_graph
.nodes
) do
742 n
.id_from
= self
:CreateTable("ResetPathing n.id_from")
743 n
.id_to
= self
:CreateTable("ResetPathing n.id_to")
744 n
.id_local
= self
:CreateTable("ResetPathing n.id_local")
747 -- Setup the local ids a node exists in.
748 for i
, list
in pairs(zone_nodes
) do
749 for _
, n
in ipairs(list
) do
756 -- Figure out where each node can come from or go to.
757 for i
, list
in pairs(zone_nodes
) do
758 for _
, node
in ipairs(list
) do
759 for n
in pairs(node
.n
) do
760 for id
in pairs(n
.id_local
) do node
.id_to
[id
] = true end
761 for id
in pairs(node
.id_local
) do n
.id_from
[id
] = true end
766 -- We'll treat 0 as a special id for where ever it is the player happens to be.
767 for node
in pairs(self
.teleport_info
.node
) do
768 node
.id_from
[0] = true
771 -- Will go through each zone and link all the nodes we have so far with every other node.
772 for _
, list
in pairs(zone_nodes
) do
775 if shouldLink(list
[i
], list
[j
]) then
776 list
[i
]:Link(list
[j
], cont_dist(list
[i
], list
[j
]))
782 self
:yieldIfNeeded(0.1)
784 -- We don't need to know where the nodes can go or come from now.
785 for i
, n
in ipairs(self
.world_graph
.nodes
) do
786 self
:ReleaseTable(n
.id_from
)
787 self
:ReleaseTable(n
.id_to
)
788 self
:ReleaseTable(n
.id_local
)
789 n
.id_from
, n
.id_to
, n
.id_local
= nil, nil, nil
792 -- TODO: This is a work around until I fix shouldLink
793 for start
, list
in pairs(flight_times
) do
794 for dest
, duration
in pairs(list
) do
795 local a
, b
= flight_master_nodes
[start
], flight_master_nodes
[dest
]
797 a
:Link(b
, duration
+5)
802 self
:yieldIfNeeded(0.1)
803 -- self.world_graph:SanityCheck()
805 -- Remove objectives again, since we created some for the flight masters.
807 local o
= table.remove(self
.prepared_objectives
)
808 if not o
then break end
810 self
:ReleaseObjectivePathingInfo(o
)
812 if o
.setup_count
> 0 then
813 -- There's a chance an objective could end up in the list twice, but we'll deal with that by not actually
814 -- adding locations for it if it's already setup.
815 table.insert(to_readd
, o
)
820 local obj
= table.remove(to_readd
)
821 if not obj
then break end
823 if not obj
.setup
then -- In case the objective was added multiple times to the to_readd list.
824 obj
.d
= QuestHelper
:CreateTable("ResetPathing obj.d")
825 obj
.p
= QuestHelper
:CreateTable("ResetPathing obj.p")
826 obj
.nm
= QuestHelper
:CreateTable("ResetPathing obj.nm")
827 obj
.nm2
= QuestHelper
:CreateTable("ResetPathing obj.nm2")
828 obj
.nl
= QuestHelper
:CreateTable("ResetPathing obj.nl")
829 obj
.distance_cache
= QuestHelper
:CreateTable("ResetPathing obj.distance_cache")
830 obj
:AppendPositions(obj
, 1, nil)
836 self
.pos
[1] = self
.zone_nodes
[self
.i
]
837 for i
, n
in ipairs(self
.pos
[1]) do
838 local a
, b
= n
.x
-self
.pos
[3], n
.y
-self
.pos
[4]
839 self
.pos
[2][i
] = math
.sqrt(a
*a
+b
*b
)
843 -- And if all went according to plan, we now have a graph we can follow to get from anywhere to anywhere.
845 if self
.graph_walker
then
846 self
.graph_walker
:GraphChanged()