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
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.107, 0.726}, -- Eastern Plaguelands <--> Western Plaguelands
142 {34, 44, 0.625, 0.03}, -- 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 if not p1
or not p2
then QuestHelper
:Error("Boom!") end
205 local graph
= self
.world_graph
207 graph
:PrepareSearch()
211 for i
, n
in ipairs(el
) do
217 for i
, n
in ipairs(p1
[1]) do
218 graph
:AddRouteStartNode(n
, l
[i
], el
)
221 local e
= graph
:DoRouteSearch(el
)
227 if p1
[1] == p2
[1] then
228 local x
, y
= p1
[3]-p2
[3], p1
[4]-p2
[4]
229 local d2
= math
.sqrt(x
*x
+y
*y
)
239 -- Let's annotate the hell out of this
240 -- 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.)
241 -- 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 :(
242 function QuestHelper
:ComputeTravelTime(p1
, p2
)
243 if not p1
or not p2
then QuestHelper
:Error("Boom!") end
244 local graph
= self
.world_graph
246 graph
:PrepareSearch()
248 local l
= p2
[2] -- Distance to zone boundaries in p2
249 local el
= p2
[1] -- Zone object for the zone that p2 is in
250 for i
, n
in ipairs(el
) do -- i is the zone index, n is the zone data
251 n
.e
, n
.w
= l
[i
], 1 -- n.e is distance from p2 to the current zone boundary, n.w is 1 (weight?)
253 n
.s
= 3 -- this is "state", I think it means "visited". TODO: untangle n.s and make it suck less than it currently does
256 l
= p1
[2] -- Distance to zone boundaries, again
257 for i
, n
in ipairs(p1
[1]) do
258 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?
261 local e
= graph
:DoSearch(el
)
265 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
267 if p1
[1] == p2
[1] then -- if they're in the same zone, we allow the user to walk from one point to another
268 local x
, y
= p1
[3]-p2
[3], p1
[4]-p2
[4]
269 d
= math
.min(d
, math
.sqrt(x
*x
+y
*y
))
275 function QuestHelper
:CreateGraphNode(c
, x
, y
, n
)
276 local node
= self
.world_graph
:CreateNode()
284 if not QuestHelper_ZoneLookup
[c
[1]]
then -- exception for Wrath changeover
285 QuestHelper
:Assert(not QuestHelper
:IsWrath(), "Zone couldn't be found, and should have been")
288 local cont
, zone
= unpack(QuestHelper_ZoneLookup
[c
[1]]
)
290 node
.x
, node
.y
= self
.Astrolabe
:TranslateWorldMapPosition(cont
, zone
, c
[2], c
[3], cont
, 0)
291 node
.x
= node
.x
* self
.continent_scales_x
[node
.c
]
292 node
.y
= node
.y
* self
.continent_scales_y
[node
.c
]
293 node
.name
= c
[5] or QuestHelper_NameLookup
[c
[1]]
300 function QuestHelper
:CreateAndAddZoneNode(z
, c
, x
, y
)
301 local node
= self
:CreateGraphNode(c
, x
, y
)
302 if not node
then return end -- exception for Wrath changeover
303 -- Not going to merge nodes.
304 --[[local closest, travel_time = nil, 0
306 for i, n in ipairs(z) do
307 local t = math.sqrt((n.x-node.x)*(n.x-node.x)+(n.y-node.y)*(n.y-node.y))
308 if not closest or t < travel_time then
309 closest, travel_time = n, t
313 if closest and travel_time < 10 then
314 closest.x = (closest.x * closest.w + node.x)/(closest.w+1)
315 closest.y = (closest.y * closest.w + node.y)/(closest.w+1)
316 closest.w = closest.w + 1
317 self.world_graph:DestroyNode(node)
320 table.insert(z
, node
)
325 function QuestHelper
:CreateAndAddStaticNodePair(data
)
328 if data
[5] and self
.named_nodes
[data
[5]]
then
329 node1
= self
.named_nodes
[data
[5]]
331 node1
= self
:CreateAndAddZoneNode(self
.zone_nodes
[data
[1][1]]
, data
[1])
332 if not node1
then return end -- exception for Wrath changeover
333 if data
[5] then self
.named_nodes
[data
[5]]
= node1
end
336 if data
[6] and self
.named_nodes
[data
[6]]
then
337 node2
= self
.named_nodes
[data
[6]]
339 node2
= self
:CreateAndAddZoneNode(self
.zone_nodes
[data
[2][1]]
, data
[2])
340 if not node2
then return end -- exception for Wrath changeover
341 if data
[6] then self
.named_nodes
[data
[6]]
= node2
end
344 node1
.name
= node1
.name
or "route to "..QuestHelper_NameLookup
[data
[2][1]]
345 node2
.name
= node2
.name
or "route to "..QuestHelper_NameLookup
[data
[1][1]]
347 node1
:Link(node2
, data
[3])
349 if not data
[4] then -- If data[4] is true, then this is a one-way trip.
350 node2
:Link(node1
, data
[3])
357 function QuestHelper
:GetNodeByName(name
, fallback_data
)
358 local node
= self
.named_nodes
[name
]
359 if not node
and fallback_data
then
360 node
= self
:CreateAndAddZoneNode(self
.zone_nodes
[fallback_data
[1]]
, fallback_data
)
361 self
.named_nodes
[name
] = node
366 local function nodeLeavesContinent(node
, c
)
368 for n
, d
in pairs(node
.n
) do
377 local function isGoodPath(start_node
, end_node
, i
, j
)
378 -- Checks to make sure a path doesn't leave the continent only to reenter it.
381 if end_node
.c
== i
then
384 end_node
= end_node
.p
385 if end_node
.c
== j
then
389 return end_node
== start_node
394 local function shouldLink(a
, b
)
395 -- TODO: Need to have objectives not create links to unreachable nodes.
401 for id in pairs(a.id_from) do
402 if not b.id_to[id] then
403 for id in pairs(b.id_to) do
404 if not a.id_from[id] then
415 local function getNPCNode(npc
)
416 local npc_objective
= QuestHelper
:GetObjective("monster", npc
)
417 if npc_objective
:Known() then
418 npc_objective
:PrepareRouting()
419 local p
= npc_objective
:Position()
423 node
= QuestHelper
:CreateAndAddZoneNode(p
[1], p
[1].c
, p
[3], p
[4])
426 npc_objective
:DoneRouting()
432 function QuestHelper
:CreateAndAddTransitionNode(z1
, z2
, pos
)
433 if not z1
or not z2
then
434 QuestHelper
:Assert(not QuestHelper
:IsWrath(), "Zone couldn't be found, and should have been")
438 local node
= self
:CreateGraphNode(pos
)
440 local closest
, travel_time
= nil, 0
442 for i
, n
in ipairs(z1
) do
443 local t
= math
.sqrt((n
.x
-node
.x
)*(n
.x
-node
.x
)+(n
.y
-node
.y
)*(n
.y
-node
.y
))
444 if not closest
or t
< travel_time
then
445 closest
, travel_time
= n
, t
450 for i
, n
in ipairs(z2
) do
451 local t
= math
.sqrt((n
.x
-node
.x
)*(n
.x
-node
.x
)+(n
.y
-node
.y
)*(n
.y
-node
.y
))
452 if not closest
or t
< travel_time
then
453 closest
, travel_time
= n
, t
458 if closest
and travel_time
< 10 then
459 --QuestHelper:TextOut("Node already exists at "..closest.x..", "..closest.y..", name="..(closest.name or "nil"))
460 closest
.x
= (closest
.x
* closest
.w
+ node
.x
)/(closest
.w
+1)
461 closest
.y
= (closest
.y
* closest
.w
+ node
.y
)/(closest
.w
+1)
462 closest
.w
= closest
.w
+ 1
463 local z1_has
, z2_has
= false, false
465 -- Just because the node already exists, doesn't mean its already in both lists!
467 for i
, n
in ipairs(z1
) do
475 for i
, n
in ipairs(z2
) do
485 if not z1_has
then table.insert(z1
, closest
) end
486 if not z2_has
then table.insert(z2
, closest
) end
488 self
.world_graph
:DestroyNode(node
)
492 table.insert(z1
, node
)
493 if z1
~= z2
then table.insert(z2
, node
) end
499 function QuestHelper
:ReleaseObjectivePathingInfo(o
)
501 for z
, pl
in pairs(o
.p
) do
502 self
:ReleaseTable(o
.d
[z
])
504 for i
, p
in ipairs(pl
) do
505 self
:ReleaseTable(p
[2])
509 self
:ReleaseTable(pl
)
512 self
:ReleaseTable(o
.d
)
513 self
:ReleaseTable(o
.p
)
514 self
:ReleaseTable(o
.nm
)
515 self
:ReleaseTable(o
.nm2
)
516 self
:ReleaseTable(o
.nl
)
518 local cache
= o
.distance_cache
519 for k
, v
in pairs(cache
) do
523 self
:ReleaseTable(cache
)
525 o
.d
, o
.p
, o
.nm
, o
.nm2
, o
.nl
= nil, nil, nil, nil, nil
526 o
.distance_cache
= nil
527 o
.pos
, o
.sop
= nil, nil -- ResetPathing will preserve these values if needed.
532 function QuestHelper
:SetupTeleportInfo(info
, can_create
)
533 self
:TeleportInfoClear(info
)
535 if QuestHelper_Home
then
536 local node
= self
:GetNodeByName("HOME_PORTAL", can_create
and QuestHelper_Home
)
538 local cooldown
= self
:ItemCooldown(6948)
540 self
:SetTeleportInfoTarget(info
, node
, GetTime()-60*60+cooldown
, 60*60, 10)
543 self
.defered_graph_reset
= true
547 -- TODO: Compact this. . . and find a better way to tell if the player has a spell.
549 if GetSpellTexture("Teleport: Darnassus") then
550 local node
= self
:GetNodeByName("DARNASSUS_PORTAL", can_create
and DARNASSUS_PORTAL
)
551 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
554 if GetSpellTexture("Teleport: Exodar") then
555 local node
= self
:GetNodeByName("EXODAR_PORTAL", can_create
and EXODAR_PORTAL
)
556 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
559 if GetSpellTexture("Teleport: Ironforge") then
560 local node
= self
:GetNodeByName("IRONFORGE_PORTAL", can_create
and IRONFORGE_PORTAL
)
561 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
564 if GetSpellTexture("Teleport: Moonglade") then
565 local node
= self
:GetNodeByName("MOONGLADE_PORTAL", can_create
and MOONGLADE_PORTAL
)
566 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10) else self
.defered_graph_reset
= true end
569 if GetSpellTexture("Teleport: Orgrimmar") then
570 local node
= self
:GetNodeByName("ORGRIMMAR_PORTAL", can_create
and ORGRIMMAR_PORTAL
)
571 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
574 if GetSpellTexture("Teleport: Shattrath") then
575 local node
= self
:GetNodeByName("SHATTRATH_CITY_PORTAL", can_create
and SHATTRATH_CITY_PORTAL
)
576 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
579 if GetSpellTexture("Teleport: Silvermoon") then
580 local node
= self
:GetNodeByName("SILVERMOON_CITY_PORTAL", can_create
and SILVERMOON_CITY_PORTAL
)
581 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
584 if GetSpellTexture("Teleport: Stormwind") then
585 local node
= self
:GetNodeByName("STORMWIND_CITY_PORTAL", can_create
and STORMWIND_CITY_PORTAL
)
586 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
589 if GetSpellTexture("Teleport: Thunder Bluff") then
590 local node
= self
:GetNodeByName("THUNDER_BLUFF_PORTAL", can_create
and THUNDER_BLUFF_PORTAL
)
591 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
594 if GetSpellTexture("Teleport: Undercity") then
595 local node
= self
:GetNodeByName("UNDERCITY_PORTAL", can_create
and UNDERCITY_PORTAL
)
596 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
599 self
:SetTeleportInfoReagent(info
, 17031, self
:CountItem(17031))
602 function QuestHelper
:ResetPathing()
603 for key
in pairs(self
.named_nodes
) do
604 self
.named_nodes
[key
] = nil
607 -- Objectives may include cached information that depends on the world graph.
610 while i
<= #self
.prepared_objectives
do
611 local o
= self
.prepared_objectives
[i
]
613 if o
.setup_count
== 0 then
614 table.remove(self
.prepared_objectives
, i
)
615 self
:ReleaseObjectivePathingInfo(o
)
617 -- Routing should reset the positions of objectives in the route after the reset is complete.
619 self
:ReleaseObjectivePathingInfo(o
)
624 local to_readd
= self
.prepared_objectives
625 self
.prepared_objectives
= self
.old_prepared_objectives
or {}
626 self
.old_prepared_objectives
= to_readd
628 local zone_nodes
= self
.zone_nodes
629 if not zone_nodes
then
631 self
.zone_nodes
= zone_nodes
634 local flight_master_nodes
= self
.flight_master_nodes
635 if not flight_master_nodes
then
636 flight_master_nodes
= {}
637 self
.flight_master_nodes
= flight_master_nodes
639 for key
in pairs(flight_master_nodes
) do
640 flight_master_nodes
[key
] = nil
644 self
.world_graph
:Reset()
647 local continent_scales_x
, continent_scales_y
= self
.continent_scales_x
, self
.continent_scales_y
648 if not continent_scales_x
then
649 continent_scales_x
= {}
650 continent_scales_y
= {}
651 self
.continent_scales_x
= continent_scales_x
652 self
.continent_scales_y
= continent_scales_y
655 for c
in pairs(self
.Astrolabe
:GetMapVirtualContinents()) do
656 if not continent_scales_x
[c
] then
657 local _
, x
, y
= self
.Astrolabe
:ComputeDistance(c
, 0, 0.25, 0.25, c
, 0, 0.75, 0.75)
659 continent_scales_x
[c
] = x
*walkspeed_multiplier
*2
660 continent_scales_y
[c
] = y
*walkspeed_multiplier
*2
664 for i
, name
in pairs(QuestHelper_NameLookup
) do
665 local z
= zone_nodes
[i
]
667 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.
669 z
.i
, z
.c
, z
.z
= i
, unpack(QuestHelper_ZoneLookup
[i
])
671 for key
in pairs(z
) do
674 z
.i
, z
.c
, z
.z
= i
, unpack(QuestHelper_ZoneLookup
[i
])
678 self
:SetupTeleportInfo(self
.teleport_info
, true)
681 --[[for node, info in pairs(self.teleport_info.node) do
682 self:TextOut("You can teleport to "..(node.name or "nil").. " in "..self:TimeString(info[1]+info[2]-GetTime()))
685 if self
.faction
== 1 then
686 for i
, data
in ipairs(static_alliance_routes
) do
687 self
:CreateAndAddStaticNodePair(data
)
689 elseif self
.faction
== 2 then
690 for i
, data
in ipairs(static_horde_routes
) do
691 self
:CreateAndAddStaticNodePair(data
)
695 for i
, data
in ipairs(static_shared_routes
) do
696 self
:CreateAndAddStaticNodePair(data
)
699 if self
.player_level
>= 58 then
700 dark_portal_route
[3] = 5
702 -- If you can't take the route yet, we'll still add it and just pretend it will take a really long time.
703 dark_portal_route
[3] = 86400
706 self
:CreateAndAddStaticNodePair(dark_portal_route
)
708 local st
= self
:CreateTable("ResetPathing local st")
710 for i
, data
in pairs(static_zone_transitions
) do
711 st
[1], st
[2], st
[3] = data
[1], data
[3], data
[4]
713 local transnode
= self
:CreateAndAddTransitionNode(zone_nodes
[data
[1]]
,
716 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
719 self
:ReleaseTable(st
)
721 -- Create and link the flight route nodes.
722 local flight_times
= self
.flight_times
723 if not flight_times
then
724 self
:buildFlightTimes()
725 flight_times
= self
.flight_times
728 for start
, list
in pairs(flight_times
) do
729 for dest
, duration
in pairs(list
) do
730 local a_npc
, b_npc
= self
:getFlightInstructor(start
), self
:getFlightInstructor(dest
)
732 if a_npc
and b_npc
then
733 local a
, b
= flight_master_nodes
[start
], flight_master_nodes
[dest
]
736 a
= getNPCNode(a_npc
)
738 flight_master_nodes
[start
] = a
739 a
.name
= (select(3, string.find(start
, "^(.*),")) or start
).." flight point"
744 b
= getNPCNode(b_npc
)
746 flight_master_nodes
[dest
] = b
747 b
.name
= (select(3, string.find(dest
, "^(.*),")) or dest
).." flight point"
752 a
:Link(b
, duration
+5)
759 -- id_from, id_to, and id_local will be used in determining whether there is a point to linking nodes together.
760 for i
, n
in ipairs(self
.world_graph
.nodes
) do
761 n
.id_from
= self
:CreateTable("ResetPathing n.id_from")
762 n
.id_to
= self
:CreateTable("ResetPathing n.id_to")
763 n
.id_local
= self
:CreateTable("ResetPathing n.id_local")
766 -- Setup the local ids a node exists in.
767 for i
, list
in pairs(zone_nodes
) do
768 for _
, n
in ipairs(list
) do
775 -- Figure out where each node can come from or go to.
776 for i
, list
in pairs(zone_nodes
) do
777 for _
, node
in ipairs(list
) do
778 for n
in pairs(node
.n
) do
779 for id
in pairs(n
.id_local
) do node
.id_to
[id
] = true end
780 for id
in pairs(node
.id_local
) do n
.id_from
[id
] = true end
785 -- We'll treat 0 as a special id for where ever it is the player happens to be.
786 for node
in pairs(self
.teleport_info
.node
) do
787 node
.id_from
[0] = true
790 -- Will go through each zone and link all the nodes we have so far with every other node.
791 for _
, list
in pairs(zone_nodes
) do
794 if shouldLink(list
[i
], list
[j
]) then
795 list
[i
]:Link(list
[j
], cont_dist(list
[i
], list
[j
]))
803 -- We don't need to know where the nodes can go or come from now.
804 for i
, n
in ipairs(self
.world_graph
.nodes
) do
805 self
:ReleaseTable(n
.id_from
)
806 self
:ReleaseTable(n
.id_to
)
807 self
:ReleaseTable(n
.id_local
)
808 n
.id_from
, n
.id_to
, n
.id_local
= nil, nil, nil
811 -- TODO: This is a work around until I fix shouldLink
812 for start
, list
in pairs(flight_times
) do
813 for dest
, duration
in pairs(list
) do
814 local a
, b
= flight_master_nodes
[start
], flight_master_nodes
[dest
]
816 a
:Link(b
, duration
+5)
822 -- self.world_graph:SanityCheck()
824 -- Remove objectives again, since we created some for the flight masters.
826 local o
= table.remove(self
.prepared_objectives
)
827 if not o
then break end
829 self
:ReleaseObjectivePathingInfo(o
)
831 if o
.setup_count
> 0 then
832 -- There's a chance an objective could end up in the list twice, but we'll deal with that by not actually
833 -- adding locations for it if it's already setup.
834 table.insert(to_readd
, o
)
839 local obj
= table.remove(to_readd
)
840 if not obj
then break end
842 if not obj
.setup
then -- In case the objective was added multiple times to the to_readd list.
843 obj
.d
= QuestHelper
:CreateTable("ResetPathing obj.d")
844 obj
.p
= QuestHelper
:CreateTable("ResetPathing obj.p")
845 obj
.nm
= QuestHelper
:CreateTable("ResetPathing obj.nm")
846 obj
.nm2
= QuestHelper
:CreateTable("ResetPathing obj.nm2")
847 obj
.nl
= QuestHelper
:CreateTable("ResetPathing obj.nl")
848 obj
.distance_cache
= QuestHelper
:CreateTable("ResetPathing obj.distance_cache")
849 obj
:AppendPositions(obj
, 1, nil)
855 self
.pos
[1] = self
.zone_nodes
[self
.i
]
856 for i
, n
in ipairs(self
.pos
[1]) do
857 local a
, b
= n
.x
-self
.pos
[3], n
.y
-self
.pos
[4]
858 self
.pos
[2][i
] = math
.sqrt(a
*a
+b
*b
)
862 -- And if all went according to plan, we now have a graph we can follow to get from anywhere to anywhere.
864 if self
.graph_walker
then
865 self
.graph_walker
:GraphChanged()
870 function QuestHelper
:Disallowed(index
)
871 return QuestHelper_RestrictedZones
[index
] ~= QuestHelper_RestrictedZones
[self
.i
]