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_ConvertCoordsToWrath({36,0.387,0.802, "Stormwind City portal site"}, true) -- Old pre-Wrath coordinates. I could fix it, but . . . meh.
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}, 60}, -- Undercity <--> Silvermoon City
24 {{7, 0.413, 0.178}, {65, 0.414, 0.536}, 210}, -- Durotar <--> Warsong Hold
25 {{43, 0.591, 0.590}, {70, 0.777, 0.283}, 210}, -- Tirisfal Glades <--> Vengeance Landing
27 {{60, 0.592, 0.483}, SILVERMOON_CITY_PORTAL
, 60, true, nil, "SILVERMOON_CITY_PORTAL"}, -- Shattrath City --> Silvermoon City
28 {{60, 0.528, 0.531}, THUNDER_BLUFF_PORTAL
, 60, true, nil, "THUNDER_BLUFF_PORTAL"}, -- Shattrath City --> Thunder Bluff
29 {{60, 0.522, 0.529}, ORGRIMMAR_PORTAL
, 60, true, nil, "ORGRIMMAR_PORTAL"}, -- Shattrath City --> Orgrimmar
30 {{60, 0.517, 0.525}, UNDERCITY_PORTAL
, 60, true, nil, "UNDERCITY_PORTAL"}, -- Shattrath City --> Undercity
32 {{67, 0.583, 0.216}, SILVERMOON_CITY_PORTAL
, 60, true, nil, "SILVERMOON_CITY_PORTAL"}, -- Dalaran --> Silvermoon City
33 {{67, 0.573, 0.219}, THUNDER_BLUFF_PORTAL
, 60, true, nil, "THUNDER_BLUFF_PORTAL"}, -- Dalaran --> Thunder Bluff
34 {{67, 0.553, 0.255}, ORGRIMMAR_PORTAL
, 60, true, nil, "ORGRIMMAR_PORTAL"}, -- Dalaran --> Orgrimmar
35 {{67, 0.556, 0.238}, UNDERCITY_PORTAL
, 60, true, nil, "UNDERCITY_PORTAL"}, -- Dalaran --> Undercity
36 {{67, 0.563, 0.226}, SHATTRATH_CITY_PORTAL
, 60, 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
44 {{36, 0.228, 0.560}, {16, 0.323, 0.441}, 210}, -- Stormwind City <--> Auberdine
46 {{36, 0.183, 0.255}, {65, 0.597, 0.694}, 210}, -- Stormwind City <--> Valiance Keep
47 {{51, 0.047, 0.571}, {70, 0.612, 0.626}, 210}, -- Menethil <--> Daggercap Bay
49 {{60, 0.558, 0.366}, STORMWIND_CITY_PORTAL
, 60, true, nil, "STORMWIND_CITY_PORTAL"}, -- Shattrath City --> Stormwind City
50 {{60, 0.563, 0.37}, IRONFORGE_PORTAL
, 60, true, nil, "IRONFORGE_PORTAL"}, -- Shattrath City --> Ironforge
51 {{60, 0.552, 0.364}, DARNASSUS_PORTAL
, 60, true, nil, "DARNASSUS_PORTAL"}, -- Shattrath City --> Darnassus
52 {{60, 0.596, 0.467}, EXODAR_PORTAL
, 60, true, nil, "EXODAR_PORTAL"}, -- Shattrath City --> Exodar
54 {{67, 0.401, 0.628}, STORMWIND_CITY_PORTAL
, 60, true, nil, "STORMWIND_CITY_PORTAL"}, -- Dalaran --> Stormwind City
55 {{67, 0.395, 0.640}, IRONFORGE_PORTAL
, 60, true, nil, "IRONFORGE_PORTAL"}, -- Dalaran --> Ironforge
56 {{67, 0.389, 0.651}, DARNASSUS_PORTAL
, 60, true, nil, "DARNASSUS_PORTAL"}, -- Dalaran --> Darnassus
57 {{67, 0.382, 0.664}, EXODAR_PORTAL
, 60, true, nil, "EXODAR_PORTAL"}, -- Dalaran --> Exodar
58 {{67, 0.371, 0.667}, SHATTRATH_CITY_PORTAL
, 60, true, nil, "SHATTRATH_CITY_PORTAL"}, -- Dalaran --> Shatt
61 local static_shared_routes
=
63 {{11, 0.638, 0.387}, {38, 0.257, 0.73}, 210}, -- Ratchet <--> Booty Bay
64 {{40, 0.318, 0.503}, {32, 0.347, 0.84}, 130}, -- Burning Steppes <--> Searing Gorge
66 -- More Alliance routes than anything, but without them theres no valid path to these areas for Horde characters.
67 {{24, 0.559, 0.896}, {21, 0.305, 0.414}, 5}, -- Rut'Theran Village <--> Darnassus
68 {{16, 0.332, 0.398}, {24, 0.548, 0.971}, 210}, -- Auberdine <--> Rut'Theran Village
69 {{16, 0.306, 0.409}, {3, 0.2, 0.546}, 210}, -- Auberdine <--> Azuremyst Isle
71 -- Route to new zone. Not valid, exists only to keep routing from exploding if you don't have the flight routes there.
72 {{41, 0.5, 0.5}, {64, 0.5, 0.5}, 7200}, -- Eversong Woods <--> Sunwell
74 {{70, 0.235, 0.578}, {68, 0.496, 0.784}, 210}, -- Kamagua <--> Moa'ki
75 {{65, 0.789, 0.536}, {68, 0.480, 0.787}, 210}, -- Unu'pe <--> Moa'ki
76 {{67, 0.559, 0.467}, {66, 0.158, 0.428}, 5, true}, -- Dalaran --> Violet Stand
77 {{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)
79 {{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 -- Darkportal is handled specially, depending on whether or not you're level 58+ or not.
83 local dark_portal_route
= {{33, 0.587, 0.599}, {56, 0.898, 0.502}, 60}
85 local static_zone_transitions
=
87 {2, 11, 0.687, 0.872}, -- Ashenvale <--> The Barrens
88 {2, 6, 0.423, 0.711}, -- Ashenvale <--> Stonetalon Mountains
89 {2, 15, 0.954, 0.484}, -- Ashenvale <--> Azshara
90 {2, 16, 0.289, 0.144}, -- Ashenvale <--> Darkshore
91 {2, 13, 0.557, 0.29}, -- Ashenvale <--> Felwood
92 {21, 24, 0.894, 0.358}, -- Darnassus <--> Teldrassil
93 {22, 11, 0.697, 0.604}, -- Mulgore <--> The Barrens
94 {22, 23, 0.376, 0.33}, -- Mulgore <--> Thunder Bluff
95 {22, 23, 0.403, 0.193}, -- Mulgore <--> Thunder Bluff
96 {3, 12, 0.247, 0.494}, -- Azuremyst Isle <--> The Exodar
97 {3, 12, 0.369, 0.469}, -- Azuremyst Isle <--> The Exodar
98 {3, 12, 0.310, 0.487}, -- Azuremyst Isle <--> The Exodar
99 {3, 12, 0.335, 0.494}, -- Azuremyst Isle <--> The Exodar
100 {3, 9, 0.42, 0.013}, -- Azuremyst Isle <--> Bloodmyst Isle
101 {4, 6, 0.539, 0.032}, -- Desolace <--> Stonetalon Mountains
102 {4, 17, 0.428, 0.976}, -- Desolace <--> Feralas
103 {5, 18, 0.865, 0.115}, -- Silithus <--> Un'Goro Crater
104 {7, 11, 0.341, 0.424}, -- Durotar <--> The Barrens
105 {7, 1, 0.455, 0.121}, -- Durotar <--> Orgrimmar
106 {8, 18, 0.269, 0.516}, -- Tanaris <--> Un'Goro Crater
107 {8, 14, 0.512, 0.21}, -- Tanaris <--> Thousand Needles
108 {10, 11, 0.287, 0.472}, -- Dustwallow Marsh <--> The Barrens
109 {10, 11, 0.563, 0.077}, -- Dustwallow Marsh <--> The Barrens
110 {11, 14, 0.442, 0.915}, -- The Barrens <--> Thousand Needles
111 {13, 19, 0.685, 0.06}, -- Felwood <--> Winterspring
112 {13, 20, 0.669, -0.063}, -- Felwood <--> Moonglade
113 {1, 11, 0.118, 0.69}, -- Orgrimmar <--> The Barrens
114 {17, 14, 0.899, 0.46}, -- Feralas <--> Thousand Needles
115 {6, 11, 0.836, 0.973}, -- Stonetalon Mountains <--> The Barrens
116 {26, 48, 0.521, 0.7}, -- Alterac Mountains <--> Hillsbrad Foothills
117 {26, 35, 0.173, 0.482}, -- Alterac Mountains <--> Silverpine Forest
118 {26, 50, 0.807, 0.347}, -- Alterac Mountains <--> Western Plaguelands
119 {39, 51, 0.454, 0.89}, -- Arathi Highlands <--> Wetlands
120 {39, 48, 0.2, 0.293}, -- Arathi Highlands <--> Hillsbrad Foothills
121 {27, 29, 0.49, 0.071}, -- Badlands <--> Loch Modan
122 -- {27, 32, -0.005, 0.636}, -- Badlands <--> Searing Gorge -- This is the "alliance-only" locked path, I'm disabling it for now entirely
123 {33, 46, 0.519, 0.051}, -- Blasted Lands <--> Swamp of Sorrows
124 {40, 30, 0.79, 0.842}, -- Burning Steppes <--> Redridge Mountains
125 {47, 31, 0.324, 0.363}, -- Deadwind Pass <--> Duskwood
126 {47, 46, 0.605, 0.41}, -- Deadwind Pass <--> Swamp of Sorrows
127 {28, 25, 0.534, 0.349}, -- Dun Morogh <--> Ironforge
128 {28, 29, 0.863, 0.514}, -- Dun Morogh <--> Loch Modan
129 {28, 29, 0.844, 0.31}, -- Dun Morogh <--> Loch Modan
130 {31, 37, 0.801, 0.158}, -- Duskwood <--> Elwynn Forest
131 {31, 37, 0.15, 0.214}, -- Duskwood <--> Elwynn Forest
132 {31, 38, 0.447, 0.884}, -- Duskwood <--> Stranglethorn Vale
133 {31, 38, 0.209, 0.863}, -- Duskwood <--> Stranglethorn Vale
134 {31, 30, 0.941, 0.103}, -- Duskwood <--> Redridge Mountains
135 {31, 49, 0.079, 0.638}, -- Duskwood <--> Westfall
136 {34, 50, 0.077, 0.661}, -- Eastern Plaguelands <--> Western Plaguelands
137 {34, 44, 0.575, 0.000}, -- Eastern Plaguelands <--> Ghostlands
138 {37, 36, 0.321, 0.493}, -- Elwynn Forest <--> Stormwind City -- Don't need to convert because it's in Elwynn coordinates, not Stormwind coordinates
139 {37, 49, 0.202, 0.804}, -- Elwynn Forest <--> Westfall
140 {37, 30, 0.944, 0.724}, -- Elwynn Forest <--> Redridge Mountains
141 {41, 52, 0.567, 0.494}, -- Eversong Woods <--> Silvermoon City
142 {41, 44, 0.486, 0.916}, -- Eversong Woods <--> Ghostlands
143 {35, 43, 0.678, 0.049}, -- Silverpine Forest <--> Tirisfal Glades
144 {42, 50, 0.217, 0.264}, -- The Hinterlands <--> Western Plaguelands
145 {43, 45, 0.619, 0.651}, -- Tirisfal Glades <--> Undercity
146 {43, 50, 0.851, 0.703}, -- Tirisfal Glades <--> Western Plaguelands
147 {38, 49, 0.292, 0.024}, -- Stranglethorn Vale <--> Westfall
148 {48, 35, 0.137, 0.458}, -- Hillsbrad Foothills <--> Silverpine Forest
149 {48, 42, 0.899, 0.253}, -- Hillsbrad Foothills <--> The Hinterlands
150 {29, 51, 0.252, 0}, -- Loch Modan <--> Wetlands
152 -- These are just guesses, since I haven't actually been to these areas.
153 {58, 60, 0.783, 0.545}, -- Nagrand <--> Shattrath City
154 {60, 55, 0.782, 0.492}, -- Shattrath City <--> Terokkar Forest
155 {54, 59, 0.842, 0.284}, -- Blade's Edge Mountains <--> Netherstorm
156 {54, 57, 0.522, 0.996}, -- Blade's Edge Mountains <--> Zangarmarsh
157 {54, 57, 0.312, 0.94}, -- Blade's Edge Mountains <--> Zangarmarsh
158 {56, 55, 0.353, 0.901}, -- Hellfire Peninsula <--> Terokkar Forest
159 {56, 57, 0.093, 0.519}, -- Hellfire Peninsula <--> Zangarmarsh
160 {58, 55, 0.8, 0.817}, -- Nagrand <--> Terokkar Forest
161 {58, 57, 0.343, 0.159}, -- Nagrand <--> Zangarmarsh
162 {58, 57, 0.754, 0.331}, -- Nagrand <--> Zangarmarsh
163 {53, 55, 0.208, 0.271}, -- Shadowmoon Valley <--> Terokkar Forest
164 {55, 57, 0.341, 0.098}, -- Terokkar Forest <--> Zangarmarsh
166 -- Most of these are also guesses :)
167 {65, 72, 0.547, 0.059}, -- Borean Tundra <--> Sholazar Basin
168 {65, 68, 0.967, 0.359}, -- Borean Tundra <--> Dragonblight
169 {74, 72, 0.208, 0.191}, -- Wintergrasp <--> Sholazar
170 {68, 74, 0.250, 0.410}, -- Dragonblight <--> Wintergrasp
171 {68, 71, 0.359, 0.155}, -- Dragonblight <--> Icecrown
172 {68, 66, 0.612, 0.142}, -- Dragonblight <--> Crystalsong
173 {68, 75, 0.900, 0.200}, -- Dragonblight <--> Zul'Drak
174 {68, 69, 0.924, 0.304}, -- Dragonblight <--> Grizzly Hills
175 {68, 69, 0.931, 0.634}, -- Dragonblight <--> Grizzly Hills
176 {70, 69, 0.540, 0.042}, -- Howling Fjord <--> Grizzly Hills
177 {70, 69, 0.233, 0.074}, -- Howling Fjord <--> Grizzly Hills
178 {70, 69, 0.753, 0.060}, -- Howling Fjord <--> Grizzly Hills
179 {69, 75, 0.432, 0.253}, -- Grizzly Hills <--> Zul'Drak
180 {69, 75, 0.583, 0.136}, -- Grizzly Hills <--> Zul'Drak
181 {66, 75, 0.967, 0.599}, -- Crystalsong <--> Zul'Drak
182 {66, 71, 0.156, 0.085}, -- Crystalsong <--> Icecrown
183 {66, 73, 0.706, 0.315}, -- Crystalsong <--> Storm Peaks
184 {66, 73, 0.839, 0.340}, -- Crystalsong <--> Storm Peaks
185 {71, 73, 0.920, 0.767}, -- Icecrown <--> Storm Peaks
188 local walkspeed_multiplier
= 1/7 -- Every yard walked takes this many seconds.
190 QuestHelper
.prepared_objectives
= {}
191 QuestHelper
.named_nodes
= {}
193 local function cont_dist(a
, b
)
194 local x
, y
= a
.x
-b
.x
, a
.y
-b
.y
195 return math
.sqrt(x
*x
+y
*y
)
198 function QuestHelper
:ComputeRoute(p1
, p2
)
199 for i
in ipairs(p1
[1]) do QuestHelper
: Assert(p1
[2][i
], "p1 nil flightpath error resurgence!") end
200 for i
in ipairs(p2
[1]) do QuestHelper
: Assert(p2
[2][i
], "p2 nil flightpath error resurgence!") end
202 if not p1
or not p2
then QuestHelper
:Error("Boom!") end
203 local graph
= self
.world_graph
205 graph
:PrepareSearch()
209 for i
, n
in ipairs(el
) do
215 for n
in pairs(graph
.open
) do QuestHelper
: Assert(nil, "not empty in preparesearch within computeroute") end
216 for i
, n
in ipairs(p1
[1]) do
217 graph
:AddRouteStartNode(n
, l
[i
], el
)
220 local e
= graph
:DoRouteSearch(el
)
226 if p1
[1] == p2
[1] then
227 local x
, y
= p1
[3]-p2
[3], p1
[4]-p2
[4]
228 local d2
= math
.sqrt(x
*x
+y
*y
)
238 -- Let's annotate the hell out of this
239 -- 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.)
240 -- 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 :(
241 function QuestHelper
:ComputeTravelTime(p1
, p2
)
242 if not p1
or not p2
then QuestHelper
:Error("Boom!") end
243 local graph
= self
.world_graph
245 graph
:PrepareSearch()
247 local l
= p2
[2] -- Distance to zone boundaries in p2
248 local el
= p2
[1] -- Zone object for the zone that p2 is in
249 for i
, n
in ipairs(el
) do -- i is the zone index, n is the zone data
250 n
.e
, n
.w
= l
[i
], 1 -- n.e is distance from p2 to the current zone boundary, n.w is 1 (weight?)
252 n
.s
= 3 -- this is "state", I think it means "visited". TODO: untangle n.s and make it suck less than it currently does
255 l
= p1
[2] -- Distance to zone boundaries, again
256 for i
, n
in ipairs(p1
[1]) do
257 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?
260 local e
= graph
:DoSearch(el
)
264 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
266 if p1
[1] == p2
[1] then -- if they're in the same zone, we allow the user to walk from one point to another
267 local x
, y
= p1
[3]-p2
[3], p1
[4]-p2
[4]
268 d
= math
.min(d
, math
.sqrt(x
*x
+y
*y
))
274 function QuestHelper
:CreateGraphNode(c
, x
, y
, n
)
275 local node
= self
.world_graph
:CreateNode()
283 QuestHelper
: Assert(QuestHelper_ZoneLookup
[c
[1]]
, "Zone couldn't be found, and should have been")
284 local cont
, zone
= unpack(QuestHelper_ZoneLookup
[c
[1]]
)
286 node
.x
, node
.y
= self
.Astrolabe
:TranslateWorldMapPosition(cont
, zone
, c
[2], c
[3], cont
, 0)
287 node
.x
= node
.x
* self
.continent_scales_x
[node
.c
]
288 node
.y
= node
.y
* self
.continent_scales_y
[node
.c
]
289 node
.name
= c
[5] or QuestHelper_NameLookup
[c
[1]]
296 function QuestHelper
:CreateAndAddZoneNode(z
, c
, x
, y
)
297 local node
= self
:CreateGraphNode(c
, x
, y
)
298 if not node
then return end -- exception for Wrath changeover
299 -- Not going to merge nodes.
300 --[[local closest, travel_time = nil, 0
302 for i, n in ipairs(z) do
303 local t = math.sqrt((n.x-node.x)*(n.x-node.x)+(n.y-node.y)*(n.y-node.y))
304 if not closest or t < travel_time then
305 closest, travel_time = n, t
309 if closest and travel_time < 10 then
310 closest.x = (closest.x * closest.w + node.x)/(closest.w+1)
311 closest.y = (closest.y * closest.w + node.y)/(closest.w+1)
312 closest.w = closest.w + 1
313 self.world_graph:DestroyNode(node)
316 table.insert(z
, node
)
321 function QuestHelper
:CreateAndAddStaticNodePair(data
)
324 if data
[5] and self
.named_nodes
[data
[5]]
then
325 node1
= self
.named_nodes
[data
[5]]
327 node1
= self
:CreateAndAddZoneNode(self
.zone_nodes
[data
[1][1]]
, data
[1])
328 if not node1
then return end -- exception for Wrath changeover
329 if data
[5] then self
.named_nodes
[data
[5]]
= node1
end
332 if data
[6] and self
.named_nodes
[data
[6]]
then
333 node2
= self
.named_nodes
[data
[6]]
335 node2
= self
:CreateAndAddZoneNode(self
.zone_nodes
[data
[2][1]]
, data
[2])
336 if not node2
then return end -- exception for Wrath changeover
337 if data
[6] then self
.named_nodes
[data
[6]]
= node2
end
340 node1
.name
= node1
.name
or "route to "..QuestHelper_NameLookup
[data
[2][1]]
341 node2
.name
= node2
.name
or "route to "..QuestHelper_NameLookup
[data
[1][1]]
343 node1
:Link(node2
, data
[3])
345 if not data
[4] then -- If data[4] is true, then this is a one-way trip.
346 node2
:Link(node1
, data
[3])
353 function QuestHelper
:GetNodeByName(name
, fallback_data
)
354 local node
= self
.named_nodes
[name
]
355 if not node
and fallback_data
then
356 node
= self
:CreateAndAddZoneNode(self
.zone_nodes
[fallback_data
[1]]
, fallback_data
)
357 self
.named_nodes
[name
] = node
362 local function nodeLeavesContinent(node
, c
)
364 for n
, d
in pairs(node
.n
) do
373 local function isGoodPath(start_node
, end_node
, i
, j
)
374 -- Checks to make sure a path doesn't leave the continent only to reenter it.
377 if end_node
.c
== i
then
380 end_node
= end_node
.p
381 if end_node
.c
== j
then
385 return end_node
== start_node
390 local function shouldLink(a
, b
)
391 -- TODO: Need to have objectives not create links to unreachable nodes.
397 for id in pairs(a.id_from) do
398 if not b.id_to[id] then
399 for id in pairs(b.id_to) do
400 if not a.id_from[id] then
411 local function getNPCNode(npc
)
412 local npc_objective
= QuestHelper
:GetObjective("monster", npc
)
413 if npc_objective
:Known() then
414 npc_objective
:PrepareRouting()
415 local p
= npc_objective
:Position()
419 node
= QuestHelper
:CreateAndAddZoneNode(p
[1], p
[1].c
, p
[3], p
[4])
422 npc_objective
:DoneRouting()
428 function QuestHelper
:CreateAndAddTransitionNode(z1
, z2
, pos
)
429 QuestHelper
: Assert(z1
and z2
, "Zone couldn't be found, and should have been")
431 local node
= self
:CreateGraphNode(pos
)
433 local closest
, travel_time
= nil, 0
435 for i
, n
in ipairs(z1
) do
436 local t
= math
.sqrt((n
.x
-node
.x
)*(n
.x
-node
.x
)+(n
.y
-node
.y
)*(n
.y
-node
.y
))
437 if not closest
or t
< travel_time
then
438 closest
, travel_time
= n
, t
443 for i
, n
in ipairs(z2
) do
444 local t
= math
.sqrt((n
.x
-node
.x
)*(n
.x
-node
.x
)+(n
.y
-node
.y
)*(n
.y
-node
.y
))
445 if not closest
or t
< travel_time
then
446 closest
, travel_time
= n
, t
451 if closest
and travel_time
< 10 then
452 --QuestHelper:TextOut("Node already exists at "..closest.x..", "..closest.y..", name="..(closest.name or "nil"))
453 closest
.x
= (closest
.x
* closest
.w
+ node
.x
)/(closest
.w
+1)
454 closest
.y
= (closest
.y
* closest
.w
+ node
.y
)/(closest
.w
+1)
455 closest
.w
= closest
.w
+ 1
456 local z1_has
, z2_has
= false, false
458 -- Just because the node already exists, doesn't mean its already in both lists!
460 for i
, n
in ipairs(z1
) do
468 for i
, n
in ipairs(z2
) do
478 if not z1_has
then table.insert(z1
, closest
) end
479 if not z2_has
then table.insert(z2
, closest
) end
481 self
.world_graph
:DestroyNode(node
)
485 table.insert(z1
, node
)
486 if z1
~= z2
then table.insert(z2
, node
) end
492 function QuestHelper
:ReleaseObjectivePathingInfo(o
)
494 for z
, pl
in pairs(o
.p
) do
495 self
:ReleaseTable(o
.d
[z
])
497 for i
, p
in ipairs(pl
) do
498 self
:ReleaseTable(p
[2])
502 self
:ReleaseTable(pl
)
505 self
:ReleaseTable(o
.d
)
506 self
:ReleaseTable(o
.p
)
507 self
:ReleaseTable(o
.nm
)
508 self
:ReleaseTable(o
.nm2
)
509 self
:ReleaseTable(o
.nl
)
511 local cache
= o
.distance_cache
512 for k
, v
in pairs(cache
) do
516 self
:ReleaseTable(cache
)
518 o
.d
, o
.p
, o
.nm
, o
.nm2
, o
.nl
= nil, nil, nil, nil, nil
519 o
.distance_cache
= nil
520 o
.pos
, o
.sop
= nil, nil -- ResetPathing will preserve these values if needed.
525 function QuestHelper
:SetupTeleportInfo(info
, can_create
)
526 self
:TeleportInfoClear(info
)
528 if QuestHelper_Home
then
529 local node
= self
:GetNodeByName("HOME_PORTAL", can_create
and QuestHelper_Home
)
531 local cooldown
= self
:ItemCooldown(6948)
533 self
:SetTeleportInfoTarget(info
, node
, GetTime()-60*60+cooldown
, 60*60, 10)
536 self
.defered_graph_reset
= true
540 -- TODO: Compact this. . . and find a better way to tell if the player has a spell.
542 if GetSpellTexture("Teleport: Darnassus") then
543 local node
= self
:GetNodeByName("DARNASSUS_PORTAL", can_create
and DARNASSUS_PORTAL
)
544 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
547 if GetSpellTexture("Teleport: Exodar") then
548 local node
= self
:GetNodeByName("EXODAR_PORTAL", can_create
and EXODAR_PORTAL
)
549 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
552 if GetSpellTexture("Teleport: Ironforge") then
553 local node
= self
:GetNodeByName("IRONFORGE_PORTAL", can_create
and IRONFORGE_PORTAL
)
554 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
557 if GetSpellTexture("Teleport: Moonglade") then
558 local node
= self
:GetNodeByName("MOONGLADE_PORTAL", can_create
and MOONGLADE_PORTAL
)
559 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10) else self
.defered_graph_reset
= true end
562 if GetSpellTexture("Teleport: Orgrimmar") then
563 local node
= self
:GetNodeByName("ORGRIMMAR_PORTAL", can_create
and ORGRIMMAR_PORTAL
)
564 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
567 if GetSpellTexture("Teleport: Shattrath") then
568 local node
= self
:GetNodeByName("SHATTRATH_CITY_PORTAL", can_create
and SHATTRATH_CITY_PORTAL
)
569 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
572 if GetSpellTexture("Teleport: Silvermoon") then
573 local node
= self
:GetNodeByName("SILVERMOON_CITY_PORTAL", can_create
and SILVERMOON_CITY_PORTAL
)
574 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
577 if GetSpellTexture("Teleport: Stormwind") then
578 local node
= self
:GetNodeByName("STORMWIND_CITY_PORTAL", can_create
and STORMWIND_CITY_PORTAL
)
579 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
582 if GetSpellTexture("Teleport: Thunder Bluff") then
583 local node
= self
:GetNodeByName("THUNDER_BLUFF_PORTAL", can_create
and THUNDER_BLUFF_PORTAL
)
584 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
587 if GetSpellTexture("Teleport: Undercity") then
588 local node
= self
:GetNodeByName("UNDERCITY_PORTAL", can_create
and UNDERCITY_PORTAL
)
589 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
592 self
:SetTeleportInfoReagent(info
, 17031, self
:CountItem(17031))
595 function QuestHelper
:ResetPathing()
596 for key
in pairs(self
.named_nodes
) do
597 self
.named_nodes
[key
] = nil
600 -- Objectives may include cached information that depends on the world graph.
603 while i
<= #self
.prepared_objectives
do
604 local o
= self
.prepared_objectives
[i
]
606 if o
.setup_count
== 0 then
607 table.remove(self
.prepared_objectives
, i
)
608 self
:ReleaseObjectivePathingInfo(o
)
610 -- Routing should reset the positions of objectives in the route after the reset is complete.
612 self
:ReleaseObjectivePathingInfo(o
)
617 local to_readd
= self
.prepared_objectives
618 self
.prepared_objectives
= self
.old_prepared_objectives
or {}
619 self
.old_prepared_objectives
= to_readd
621 local zone_nodes
= self
.zone_nodes
622 if not zone_nodes
then
624 self
.zone_nodes
= zone_nodes
627 local flight_master_nodes
= self
.flight_master_nodes
628 if not flight_master_nodes
then
629 flight_master_nodes
= {}
630 self
.flight_master_nodes
= flight_master_nodes
632 for key
in pairs(flight_master_nodes
) do
633 flight_master_nodes
[key
] = nil
637 self
.world_graph
:Reset()
640 local continent_scales_x
, continent_scales_y
= self
.continent_scales_x
, self
.continent_scales_y
641 if not continent_scales_x
then
642 continent_scales_x
= {}
643 continent_scales_y
= {}
644 self
.continent_scales_x
= continent_scales_x
645 self
.continent_scales_y
= continent_scales_y
648 for c
in pairs(self
.Astrolabe
:GetMapVirtualContinents()) do
649 if not continent_scales_x
[c
] then
650 local _
, x
, y
= self
.Astrolabe
:ComputeDistance(c
, 0, 0.25, 0.25, c
, 0, 0.75, 0.75)
652 continent_scales_x
[c
] = x
*walkspeed_multiplier
*2
653 continent_scales_y
[c
] = y
*walkspeed_multiplier
*2
657 for i
, name
in pairs(QuestHelper_NameLookup
) do
658 local z
= zone_nodes
[i
]
660 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.
662 z
.i
, z
.c
, z
.z
= i
, unpack(QuestHelper_ZoneLookup
[i
])
664 for key
in pairs(z
) do
667 z
.i
, z
.c
, z
.z
= i
, unpack(QuestHelper_ZoneLookup
[i
])
671 self
:SetupTeleportInfo(self
.teleport_info
, true)
674 --[[for node, info in pairs(self.teleport_info.node) do
675 self:TextOut("You can teleport to "..(node.name or "nil").. " in "..self:TimeString(info[1]+info[2]-GetTime()))
678 if self
.faction
== 1 then
679 for i
, data
in ipairs(static_alliance_routes
) do
680 self
:CreateAndAddStaticNodePair(data
)
682 elseif self
.faction
== 2 then
683 for i
, data
in ipairs(static_horde_routes
) do
684 self
:CreateAndAddStaticNodePair(data
)
688 for i
, data
in ipairs(static_shared_routes
) do
689 self
:CreateAndAddStaticNodePair(data
)
692 if self
.player_level
>= 58 then
693 dark_portal_route
[3] = 5
695 -- If you can't take the route yet, we'll still add it and just pretend it will take a really long time.
696 dark_portal_route
[3] = 86400
699 self
:CreateAndAddStaticNodePair(dark_portal_route
)
701 local st
= self
:CreateTable("ResetPathing local st")
703 for i
, data
in pairs(static_zone_transitions
) do
704 st
[1], st
[2], st
[3] = data
[1], data
[3], data
[4]
706 local transnode
= self
:CreateAndAddTransitionNode(zone_nodes
[data
[1]]
,
709 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
712 self
:ReleaseTable(st
)
714 -- Create and link the flight route nodes.
715 local flight_times
= self
.flight_times
716 if not flight_times
then
717 self
:buildFlightTimes()
718 flight_times
= self
.flight_times
721 for start
, list
in pairs(flight_times
) do
722 for dest
, duration
in pairs(list
) do
723 local a_npc
, b_npc
= self
:getFlightInstructor(start
), self
:getFlightInstructor(dest
)
725 if a_npc
and b_npc
then
726 local a
, b
= flight_master_nodes
[start
], flight_master_nodes
[dest
]
729 a
= getNPCNode(a_npc
)
731 flight_master_nodes
[start
] = a
732 a
.name
= (select(3, string.find(start
, "^(.*),")) or start
).." flight point"
737 b
= getNPCNode(b_npc
)
739 flight_master_nodes
[dest
] = b
740 b
.name
= (select(3, string.find(dest
, "^(.*),")) or dest
).." flight point"
745 a
:Link(b
, duration
+5)
752 -- id_from, id_to, and id_local will be used in determining whether there is a point to linking nodes together.
753 for i
, n
in ipairs(self
.world_graph
.nodes
) do
754 n
.id_from
= self
:CreateTable("ResetPathing n.id_from")
755 n
.id_to
= self
:CreateTable("ResetPathing n.id_to")
756 n
.id_local
= self
:CreateTable("ResetPathing n.id_local")
759 -- Setup the local ids a node exists in.
760 for i
, list
in pairs(zone_nodes
) do
761 for _
, n
in ipairs(list
) do
768 -- Figure out where each node can come from or go to.
769 for i
, list
in pairs(zone_nodes
) do
770 for _
, node
in ipairs(list
) do
771 for n
in pairs(node
.n
) do
772 for id
in pairs(n
.id_local
) do node
.id_to
[id
] = true end
773 for id
in pairs(node
.id_local
) do n
.id_from
[id
] = true end
778 -- We'll treat 0 as a special id for where ever it is the player happens to be.
779 for node
in pairs(self
.teleport_info
.node
) do
780 node
.id_from
[0] = true
783 -- Will go through each zone and link all the nodes we have so far with every other node.
784 for _
, list
in pairs(zone_nodes
) do
787 if shouldLink(list
[i
], list
[j
]) then
788 list
[i
]:Link(list
[j
], cont_dist(list
[i
], list
[j
]))
796 -- We don't need to know where the nodes can go or come from now.
797 for i
, n
in ipairs(self
.world_graph
.nodes
) do
798 self
:ReleaseTable(n
.id_from
)
799 self
:ReleaseTable(n
.id_to
)
800 self
:ReleaseTable(n
.id_local
)
801 n
.id_from
, n
.id_to
, n
.id_local
= nil, nil, nil
804 -- TODO: This is a work around until I fix shouldLink
805 for start
, list
in pairs(flight_times
) do
806 for dest
, duration
in pairs(list
) do
807 local a
, b
= flight_master_nodes
[start
], flight_master_nodes
[dest
]
809 a
:Link(b
, duration
+5)
815 -- self.world_graph:SanityCheck()
817 -- Remove objectives again, since we created some for the flight masters.
819 local o
= table.remove(self
.prepared_objectives
)
820 if not o
then break end
822 self
:ReleaseObjectivePathingInfo(o
)
824 if o
.setup_count
> 0 then
825 -- There's a chance an objective could end up in the list twice, but we'll deal with that by not actually
826 -- adding locations for it if it's already setup.
827 table.insert(to_readd
, o
)
832 local obj
= table.remove(to_readd
)
833 if not obj
then break end
835 if not obj
.setup
then -- In case the objective was added multiple times to the to_readd list.
836 obj
.d
= QuestHelper
:CreateTable("ResetPathing obj.d")
837 obj
.p
= QuestHelper
:CreateTable("ResetPathing obj.p")
838 obj
.nm
= QuestHelper
:CreateTable("ResetPathing obj.nm")
839 obj
.nm2
= QuestHelper
:CreateTable("ResetPathing obj.nm2")
840 obj
.nl
= QuestHelper
:CreateTable("ResetPathing obj.nl")
841 obj
.distance_cache
= QuestHelper
:CreateTable("ResetPathing obj.distance_cache")
842 obj
:AppendPositions(obj
, 1, nil)
848 self
.pos
[1] = self
.zone_nodes
[self
.i
]
849 for i
, n
in ipairs(self
.pos
[1]) do
850 local a
, b
= n
.x
-self
.pos
[3], n
.y
-self
.pos
[4]
851 self
.pos
[2][i
] = math
.sqrt(a
*a
+b
*b
)
855 -- And if all went according to plan, we now have a graph we can follow to get from anywhere to anywhere.
857 if self
.graph_walker
then
858 self
.graph_walker
:GraphChanged()
863 function QuestHelper
:Disallowed(index
)
864 return QuestHelper_RestrictedZones
[index
] ~= QuestHelper_RestrictedZones
[self
.i
]