1 local IRONFORGE_PORTAL
= {25,0.255,0.084, "Ironforge portal site"}
2 local STORMWIND_CITY_PORTAL
= {36,0.387,0.802, "Stormwind City portal site"}
3 local DARNASSUS_PORTAL
= {21,0.397,0.824, "Darnassus portal site"}
4 local EXODAR_PORTAL
= {12,0.476,0.598, "Exodar portal site"}
6 local SHATTRATH_CITY_PORTAL
= {60,0.530,0.492, "Shattrath City portal site"}
7 local MOONGLADE_PORTAL
= {20,0.563,0.320, "Moonglade portal site"}
9 local SILVERMOON_CITY_PORTAL
= {52,0.583,0.192, "Silvermoon City portal site"}
10 local UNDERCITY_PORTAL
= {45,0.846,0.163, "Undercity portal site"}
11 local ORGRIMMAR_PORTAL
= {1,0.386,0.859, "Orgrimmar portal site"}
12 local THUNDER_BLUFF_PORTAL
= {23,0.222,0.168, "Thunder Bluff portal site"}
14 local static_horde_routes
=
16 {{7, 0.505, 0.124}, {38, 0.313, 0.303}, 210}, -- Durotar <--> Grom'gol Base Camp
17 {{38, 0.316, 0.289}, {43, 0.621, 0.591}, 210}, -- Grom'gol Base Camp <--> Tirisfal Glades
18 {{43, 0.605, 0.587}, {7, 0.509, 0.141}, 210}, -- Tirisfal Glades <--> Durotar
19 {{45, 0.549, 0.11}, {52, 0.495, 0.148}, 5}, -- Undercity <--> Silvermoon City
21 {{60, 0.592, 0.483}, SILVERMOON_CITY_PORTAL
, 5, true, nil, "SILVERMOON_CITY_PORTAL"}, -- Shattrath City --> Silvermoon City
22 {{60, 0.528, 0.531}, THUNDER_BLUFF_PORTAL
, 5, true, nil, "THUNDER_BLUFF_PORTAL"}, -- Shattrath City --> Thunder Bluff
23 {{60, 0.522, 0.529}, ORGRIMMAR_PORTAL
, 5, true, nil, "ORGRIMMAR_PORTAL"}, -- Shattrath City --> Orgrimmar
24 {{60, 0.517, 0.525}, UNDERCITY_PORTAL
, 5, true, nil, "UNDERCITY_PORTAL"} -- Shattrath City --> Undercity
27 local static_alliance_routes
=
29 {{36, 0.639, 0.083}, {25, 0.764, 0.512}, 120}, -- Deeprun Tram
30 {{51, 0.044, 0.569}, {16, 0.323, 0.441}, 210}, --Menethil Harbor <--> Auberdine
31 {{10, 0.718, 0.565}, {51, 0.047, 0.636}, 210}, -- Theramore Isle <--> Menethil Harmor
33 {{60, 0.558, 0.366}, STORMWIND_CITY_PORTAL
, 5, true, nil, "STORMWIND_CITY_PORTAL"}, -- Shattrath City --> Stormwind City
34 {{60, 0.563, 0.37}, IRONFORGE_PORTAL
, 5, true, nil, "IRONFORGE_PORTAL"}, -- Shattrath City --> Ironforge
35 {{60, 0.552, 0.364}, DARNASSUS_PORTAL
, 5, true, nil, "DARNASSUS_PORTAL"}, -- Shattrath City --> Darnassus
36 {{60, 0.596, 0.467}, EXODAR_PORTAL
, 5, true, nil, "EXODAR_PORTAL"} -- Shattrath City --> Exodar
39 local static_shared_routes
=
41 {{11, 0.638, 0.387}, {38, 0.257, 0.73}, 210}, -- Ratchet <--> Booty Bay
42 {{40, 0.318, 0.503}, {32, 0.347, 0.84}, 130}, -- Burning Steppes <--> Searing Gorge
44 -- More Alliance routes than anything, but without them theres no valid path to these areas for Horde characters.
45 {{24, 0.559, 0.896}, {21, 0.305, 0.414}, 5}, -- Rut'Theran Village <--> Darnassus
46 {{16, 0.332, 0.398}, {24, 0.548, 0.971}, 210}, -- Auberdine <--> Rut'Theran Village
47 {{16, 0.306, 0.409}, {3, 0.2, 0.546}, 210}, -- Auberdine <--> Azuremyst Isle
49 -- Route to new zone. Not valid, exists only to keep routing from exploding if you don't have the flight routes there.
50 {{41, 0.5, 0.5}, {64, 0.5, 0.5}, 7200} -- Eversong Woods <--> Sunwell
53 -- Darkportal is handled specially, depending on whether or not you're level 58+ or not.
54 local dark_portal_route
= {{33, 0.587, 0.599}, {56, 0.898, 0.502}, 5}
56 local static_zone_transitions
=
58 {2, 11, 0.687, 0.872}, -- Ashenvale <--> The Barrens
59 {2, 6, 0.423, 0.711}, -- Ashenvale <--> Stonetalon Mountains
60 {2, 15, 0.954, 0.484}, -- Ashenvale <--> Azshara
61 {2, 16, 0.289, 0.144}, -- Ashenvale <--> Darkshore
62 {2, 13, 0.557, 0.29}, -- Ashenvale <--> Felwood
63 {21, 24, 0.894, 0.358}, -- Darnassus <--> Teldrassil
64 {22, 11, 0.697, 0.604}, -- Mulgore <--> The Barrens
65 {22, 23, 0.376, 0.33}, -- Mulgore <--> Thunder Bluff
66 {22, 23, 0.403, 0.193}, -- Mulgore <--> Thunder Bluff
67 {3, 12, 0.247, 0.494}, -- Azuremyst Isle <--> The Exodar
68 {3, 12, 0.369, 0.469}, -- Azuremyst Isle <--> The Exodar
69 {3, 9, 0.42, 0.013}, -- Azuremyst Isle <--> Bloodmyst Isle
70 {4, 6, 0.539, 0.032}, -- Desolace <--> Stonetalon Mountains
71 {4, 17, 0.428, 0.976}, -- Desolace <--> Feralas
72 {5, 18, 0.865, 0.115}, -- Silithus <--> Un'Goro Crater
73 {7, 11, 0.341, 0.424}, -- Durotar <--> The Barrens
74 {7, 1, 0.455, 0.121}, -- Durotar <--> Orgrimmar
75 {8, 18, 0.269, 0.516}, -- Tanaris <--> Un'Goro Crater
76 {8, 14, 0.512, 0.21}, -- Tanaris <--> Thousand Needles
77 {10, 11, 0.287, 0.472}, -- Dustwallow Marsh <--> The Barrens
78 {10, 11, 0.563, 0.077}, -- Dustwallow Marsh <--> The Barrens
79 {11, 14, 0.442, 0.915}, -- The Barrens <--> Thousand Needles
80 {13, 19, 0.685, 0.06}, -- Felwood <--> Winterspring
81 {13, 20, 0.669, -0.063}, -- Felwood <--> Moonglade
82 {1, 11, 0.118, 0.69}, -- Orgrimmar <--> The Barrens
83 {17, 14, 0.899, 0.46}, -- Feralas <--> Thousand Needles
84 {6, 11, 0.836, 0.973}, -- Stonetalon Mountains <--> The Barrens
85 {26, 48, 0.521, 0.7}, -- Alterac Mountains <--> Hillsbrad Foothills
86 {26, 35, 0.173, 0.482}, -- Alterac Mountains <--> Silverpine Forest
87 {26, 50, 0.807, 0.347}, -- Alterac Mountains <--> Western Plaguelands
88 {39, 51, 0.454, 0.89}, -- Arathi Highlands <--> Wetlands
89 {39, 48, 0.2, 0.293}, -- Arathi Highlands <--> Hillsbrad Foothills
90 {27, 29, 0.49, 0.071}, -- Badlands <--> Loch Modan
91 {27, 32, -0.005, 0.636}, -- Badlands <--> Searing Gorge
92 {33, 46, 0.519, 0.051}, -- Blasted Lands <--> Swamp of Sorrows
93 {40, 30, 0.79, 0.842}, -- Burning Steppes <--> Redridge Mountains
94 {47, 31, 0.324, 0.363}, -- Deadwind Pass <--> Duskwood
95 {47, 46, 0.605, 0.41}, -- Deadwind Pass <--> Swamp of Sorrows
96 {28, 25, 0.534, 0.349}, -- Dun Morogh <--> Ironforge
97 {28, 29, 0.863, 0.514}, -- Dun Morogh <--> Loch Modan
98 {28, 29, 0.844, 0.31}, -- Dun Morogh <--> Loch Modan
99 {31, 37, 0.801, 0.158}, -- Duskwood <--> Elwynn Forest
100 {31, 37, 0.15, 0.214}, -- Duskwood <--> Elwynn Forest
101 {31, 38, 0.447, 0.884}, -- Duskwood <--> Stranglethorn Vale
102 {31, 38, 0.209, 0.863}, -- Duskwood <--> Stranglethorn Vale
103 {31, 30, 0.941, 0.103}, -- Duskwood <--> Redridge Mountains
104 {31, 49, 0.079, 0.638}, -- Duskwood <--> Westfall
105 {34, 50, 0.107, 0.726}, -- Eastern Plaguelands <--> Western Plaguelands
106 {34, 44, 0.625, 0.03}, -- Eastern Plaguelands <--> Ghostlands
107 {37, 36, 0.321, 0.493}, -- Elwynn Forest <--> Stormwind City
108 {37, 49, 0.202, 0.804}, -- Elwynn Forest <--> Westfall
109 {37, 30, 0.944, 0.724}, -- Elwynn Forest <--> Redridge Mountains
110 {41, 52, 0.567, 0.494}, -- Eversong Woods <--> Silvermoon City
111 {41, 44, 0.486, 0.916}, -- Eversong Woods <--> Ghostlands
112 {35, 43, 0.678, 0.049}, -- Silverpine Forest <--> Tirisfal Glades
113 {42, 50, 0.217, 0.264}, -- The Hinterlands <--> Western Plaguelands
114 {43, 45, 0.619, 0.651}, -- Tirisfal Glades <--> Undercity
115 {43, 50, 0.851, 0.703}, -- Tirisfal Glades <--> Western Plaguelands
116 {38, 49, 0.292, 0.024}, -- Stranglethorn Vale <--> Westfall
117 {48, 35, 0.137, 0.458}, -- Hillsbrad Foothills <--> Silverpine Forest
118 {48, 42, 0.899, 0.253}, -- Hillsbrad Foothills <--> The Hinterlands
119 {29, 51, 0.252, 0}, -- Loch Modan <--> Wetlands
121 -- These are just guesses, since I haven't actually been to these areas.
122 {58, 60, 0.783, 0.545}, -- Nagrand <--> Shattrath City
123 {60, 55, 0.782, 0.492}, -- Shattrath City <--> Terokkar Forest
124 {54, 59, 0.842, 0.284}, -- Blade's Edge Mountains <--> Netherstorm
125 {54, 57, 0.522, 0.996}, -- Blade's Edge Mountains <--> Zangarmarsh
126 {54, 57, 0.312, 0.94}, -- Blade's Edge Mountains <--> Zangarmarsh
127 {56, 55, 0.353, 0.901}, -- Hellfire Peninsula <--> Terokkar Forest
128 {56, 57, 0.093, 0.519}, -- Hellfire Peninsula <--> Zangarmarsh
129 {58, 55, 0.8, 0.817}, -- Nagrand <--> Terokkar Forest
130 {58, 57, 0.343, 0.159}, -- Nagrand <--> Zangarmarsh
131 {58, 57, 0.754, 0.331}, -- Nagrand <--> Zangarmarsh
132 {53, 55, 0.208, 0.271}, -- Shadowmoon Valley <--> Terokkar Forest
133 {55, 57, 0.341, 0.098}, -- Terokkar Forest <--> Zangarmarsh
136 local walkspeed_multiplier
= 1/7 -- Every yard walked takes this many seconds.
138 local cont_heuristic
= {} -- Contains a 2D table of heuristics to use for getting from one continent to another.
140 local function nil_heuristic(a
, b
)
144 QuestHelper
.prepared_objectives
= {}
145 QuestHelper
.named_nodes
= {}
147 local function heuristic(a
, b
)
148 if type(b
) ~= "table" then QuestHelper
:Error("Boom?!") end
149 QuestHelper
:TextOut("c="..a
.c
.." x="..a
.x
.." y="..a
.y
.." to c="..b
.c
.." x="..b
.x
.." y="..b
.y
.." ="..cont_heuristic
[a
.c
][b
.c
](a
, b
))
150 return cont_heuristic
[a
.c
][b
.c
](a
, b
)
153 local function same_cont_heuristic(a
, b
)
154 local x
, y
= a
.x
-b
.x
, a
.y
-b
.y
155 return math
.sqrt(x
*x
+y
*y
)
158 function QuestHelper
:ComputeRoute(p1
, p2
)
159 if not p1
or not p2
then QuestHelper
:Error("Boom!") end
160 local graph
= self
.world_graph
162 graph
:PrepareSearch()
166 for i
, n
in ipairs(el
) do
172 for i
, n
in ipairs(p1
[1]) do
173 graph
:AddRouteStartNode(n
, l
[i
], el
)
176 local e
= graph
:DoRouteSearch(el
)
182 if p1
[1] == p2
[1] then
183 local x
, y
= p1
[3]-p2
[3], p1
[4]-p2
[4]
184 local d2
= math
.sqrt(x
*x
+y
*y
)
194 function QuestHelper
:ComputeTravelTime(p1
, p2
)
195 if not p1
or not p2
then QuestHelper
:Error("Boom!") end
196 local graph
= self
.world_graph
198 graph
:PrepareSearch()
202 for i
, n
in ipairs(el
) do
209 for i
, n
in ipairs(p1
[1]) do
210 graph
:AddStartNode(n
, l
[i
], el
)
213 local e
= graph
:DoSearch(el
)
219 if p1
[1] == p2
[1] then
220 local x
, y
= p1
[3]-p2
[3], p1
[4]-p2
[4]
221 d
= math
.min(d
, math
.sqrt(x
*x
+y
*y
))
227 function QuestHelper
:CreateGraphNode(c
, x
, y
, n
)
228 local node
= self
.world_graph
:CreateNode()
236 local cont
, zone
= unpack(QuestHelper_ZoneLookup
[c
[1]]
)
238 node
.x
, node
.y
= self
.Astrolabe
:TranslateWorldMapPosition(cont
, zone
, c
[2], c
[3], cont
, 0)
239 node
.x
= node
.x
* self
.continent_scales_x
[node
.c
]
240 node
.y
= node
.y
* self
.continent_scales_y
[node
.c
]
241 node
.name
= c
[5] or QuestHelper_NameLookup
[c
[1]]
248 function QuestHelper
:CreateAndAddZoneNode(z
, c
, x
, y
)
249 local node
= self
:CreateGraphNode(c
, x
, y
)
251 -- Not going to merge nodes.
252 --[[local closest, travel_time = nil, 0
254 for i, n in ipairs(z) do
255 local t = math.sqrt((n.x-node.x)*(n.x-node.x)+(n.y-node.y)*(n.y-node.y))
256 if not closest or t < travel_time then
257 closest, travel_time = n, t
261 if closest and travel_time < 10 then
262 closest.x = (closest.x * closest.w + node.x)/(closest.w+1)
263 closest.y = (closest.y * closest.w + node.y)/(closest.w+1)
264 closest.w = closest.w + 1
265 self.world_graph:DestroyNode(node)
268 table.insert(z
, node
)
273 function QuestHelper
:CreateAndAddStaticNodePair(data
)
276 if data
[5] and self
.named_nodes
[data
[5]]
then
277 node1
= self
.named_nodes
[data
[5]]
279 node1
= self
:CreateAndAddZoneNode(self
.zone_nodes
[data
[1][1]]
, data
[1])
280 if data
[5] then self
.named_nodes
[data
[5]]
= node1
end
283 if data
[6] and self
.named_nodes
[data
[6]]
then
284 node2
= self
.named_nodes
[data
[6]]
286 node2
= self
:CreateAndAddZoneNode(self
.zone_nodes
[data
[2][1]]
, data
[2])
287 if data
[6] then self
.named_nodes
[data
[6]]
= node2
end
290 node1
.name
= node1
.name
or "route to "..QuestHelper_NameLookup
[data
[2][1]]
291 node2
.name
= node2
.name
or "route to "..QuestHelper_NameLookup
[data
[1][1]]
293 node1
:Link(node2
, data
[3])
295 if not data
[4] then -- If data[4] is true, then this is a one-way trip.
296 node2
:Link(node1
, data
[3])
302 function QuestHelper
:GetNodeByName(name
, fallback_data
)
303 local node
= self
.named_nodes
[name
]
304 if not node
and fallback_data
then
305 node
= self
:CreateAndAddZoneNode(self
.zone_nodes
[fallback_data
[1]]
, fallback_data
)
306 self
.named_nodes
[name
] = node
311 local function nodeLeavesContinent(node
, c
)
313 for n
, d
in pairs(node
.n
) do
322 local function isGoodPath(start_node
, end_node
, i
, j
)
323 -- Checks to make sure a path doesn't leave the continent only to reenter it.
326 if end_node
.c
== i
then
329 end_node
= end_node
.p
330 if end_node
.c
== j
then
334 return end_node
== start_node
339 local function shouldLink(a
, b
)
340 -- TODO: Need to have objectives not create links to unreachable nodes.
346 for id in pairs(a.id_from) do
347 if not b.id_to[id] then
348 for id in pairs(b.id_to) do
349 if not a.id_from[id] then
360 local function getNPCNode(npc
)
361 local npc_objective
= QuestHelper
:GetObjective("monster", npc
)
362 if npc_objective
:Known() then
363 npc_objective
:PrepareRouting()
364 local p
= npc_objective
:Position()
368 node
= QuestHelper
:CreateAndAddZoneNode(p
[1], p
[1].c
, p
[3], p
[4])
371 npc_objective
:DoneRouting()
377 function QuestHelper
:CreateAndAddTransitionNode(z1
, z2
, pos
)
378 local node
= self
:CreateGraphNode(pos
)
380 local closest
, travel_time
= nil, 0
382 for i
, n
in ipairs(z1
) do
383 local t
= math
.sqrt((n
.x
-node
.x
)*(n
.x
-node
.x
)+(n
.y
-node
.y
)*(n
.y
-node
.y
))
384 if not closest
or t
< travel_time
then
385 closest
, travel_time
= n
, t
390 for i
, n
in ipairs(z2
) do
391 local t
= math
.sqrt((n
.x
-node
.x
)*(n
.x
-node
.x
)+(n
.y
-node
.y
)*(n
.y
-node
.y
))
392 if not closest
or t
< travel_time
then
393 closest
, travel_time
= n
, t
398 if closest
and travel_time
< 10 then
399 --QuestHelper:TextOut("Node already exists at "..closest.x..", "..closest.y..", name="..(closest.name or "nil"))
400 closest
.x
= (closest
.x
* closest
.w
+ node
.x
)/(closest
.w
+1)
401 closest
.y
= (closest
.y
* closest
.w
+ node
.y
)/(closest
.w
+1)
402 closest
.w
= closest
.w
+ 1
403 local z1_has
, z2_has
= false, false
405 -- Just because the node already exists, doesn't mean its already in both lists!
407 for i
, n
in ipairs(z1
) do
415 for i
, n
in ipairs(z2
) do
425 if not z1_has
then table.insert(z1
, closest
) end
426 if not z2_has
then table.insert(z2
, closest
) end
428 self
.world_graph
:DestroyNode(node
)
431 table.insert(z1
, node
)
432 if z1
~= z2
then table.insert(z2
, node
) end
437 function QuestHelper
:ReleaseObjectivePathingInfo(o
)
439 for z
, pl
in pairs(o
.p
) do
440 self
:ReleaseTable(o
.d
[z
])
442 for i
, p
in ipairs(pl
) do
443 self
:ReleaseTable(p
[2])
447 self
:ReleaseTable(pl
)
450 self
:ReleaseTable(o
.d
)
451 self
:ReleaseTable(o
.p
)
452 self
:ReleaseTable(o
.nm
)
453 self
:ReleaseTable(o
.nm2
)
454 self
:ReleaseTable(o
.nl
)
456 o
.d
, o
.p
, o
.nm
, o
.nm2
, o
.nl
= nil, nil, nil, nil, nil
457 o
.pos
, o
.sop
= nil, nil -- ResetPathing will preserve these values if needed.
462 function QuestHelper
:SetupTeleportInfo(info
, can_create
)
463 self
:TeleportInfoClear(info
)
465 if QuestHelper_Home
then
466 local node
= self
:GetNodeByName("HOME_PORTAL", can_create
and QuestHelper_Home
)
468 local cooldown
= self
:ItemCooldown(6948)
470 self
:SetTeleportInfoTarget(info
, node
, GetTime()-60*60+cooldown
, 60*60, 10)
473 self
.defered_graph_reset
= true
477 -- TODO: Compact this. . . and find a better way to tell if the player has a spell.
479 if GetSpellTexture("Teleport: Darnassus") then
480 local node
= self
:GetNodeByName("DARNASSUS_PORTAL", can_create
and DARNASSUS_PORTAL
)
481 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
484 if GetSpellTexture("Teleport: Exodar") then
485 local node
= self
:GetNodeByName("EXODAR_PORTAL", can_create
and EXODAR_PORTAL
)
486 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
489 if GetSpellTexture("Teleport: Ironforge") then
490 local node
= self
:GetNodeByName("IRONFORGE_PORTAL", can_create
and IRONFORGE_PORTAL
)
491 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
494 if GetSpellTexture("Teleport: Moonglade") then
495 local node
= self
:GetNodeByName("MOONGLADE_PORTAL", can_create
and MOONGLADE_PORTAL
)
496 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10) else self
.defered_graph_reset
= true end
499 if GetSpellTexture("Teleport: Orgrimmar") then
500 local node
= self
:GetNodeByName("ORGRIMMAR_PORTAL", can_create
and ORGRIMMAR_PORTAL
)
501 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
504 if GetSpellTexture("Teleport: Shattrath") then
505 local node
= self
:GetNodeByName("SHATTRATH_CITY_PORTAL", can_create
and SHATTRATH_CITY_PORTAL
)
506 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
509 if GetSpellTexture("Teleport: Silvermoon") then
510 local node
= self
:GetNodeByName("SILVERMOON_CITY_PORTAL", can_create
and SILVERMOON_CITY_PORTAL
)
511 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
514 if GetSpellTexture("Teleport: Stormwind") then
515 local node
= self
:GetNodeByName("STORMWIND_CITY_PORTAL", can_create
and STORMWIND_CITY_PORTAL
)
516 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
519 if GetSpellTexture("Teleport: Thunder Bluff") then
520 local node
= self
:GetNodeByName("THUNDER_BLUFF_PORTAL", can_create
and THUNDER_BLUFF_PORTAL
)
521 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
524 if GetSpellTexture("Teleport: Undercity") then
525 local node
= self
:GetNodeByName("UNDERCITY_PORTAL", can_create
and UNDERCITY_PORTAL
)
526 if node
then self
:SetTeleportInfoTarget(info
, node
, 0, 0, 10, 17031) else self
.defered_graph_reset
= true end
529 self
:SetTeleportInfoReagent(info
, 17031, self
:CountItem(17031))
532 function QuestHelper
:ResetPathing()
533 for key
in pairs(self
.named_nodes
) do
534 self
.named_nodes
[key
] = nil
537 -- Objectives may include cached information that depends on the world graph.
540 while i
<= #self
.prepared_objectives
do
541 local o
= self
.prepared_objectives
[i
]
543 if o
.setup_count
== 0 then
544 table.remove(self
.prepared_objectives
, i
)
545 self
:ReleaseObjectivePathingInfo(o
)
548 o
.old_pos
= self
:CreateTable()
549 o
.old_pos
[1], o
.old_pos
[2], o
.old_pos
[3] = o
.pos
[1].c
, o
.pos
[3], o
.pos
[4]
553 o
.old_sop
= self
:CreateTable()
554 o
.old_sop
[1], o
.old_sop
[2], o
.old_sop
[3] = o
.sop
[1].c
, o
.sop
[3], o
.sop
[4]
557 self
:ReleaseObjectivePathingInfo(o
)
562 local to_readd
= self
.prepared_objectives
563 self
.prepared_objectives
= self
.old_prepared_objectives
or {}
564 self
.old_prepared_objectives
= to_readd
566 self
.world_graph
:SetHeuristic(nil_heuristic
)
568 local zone_nodes
= self
.zone_nodes
569 if not zone_nodes
then
571 self
.zone_nodes
= zone_nodes
574 local flight_master_nodes
= self
.flight_master_nodes
575 if not flight_master_nodes
then
576 flight_master_nodes
= {}
577 self
.flight_master_nodes
= flight_master_nodes
579 for key
in pairs(flight_master_nodes
) do
580 flight_master_nodes
[key
] = nil
584 self
.world_graph
:Reset()
586 local continent_scales_x
, continent_scales_y
= self
.continent_scales_x
, self
.continent_scales_y
587 if not continent_scales_x
then
588 continent_scales_x
= {}
589 continent_scales_y
= {}
590 self
.continent_scales_x
= continent_scales_x
591 self
.continent_scales_y
= continent_scales_y
594 for c
=1,select("#", GetMapContinents()) do
595 if not continent_scales_x
[c
] then
596 local _
, x
, y
= self
.Astrolabe
:ComputeDistance(c
, 0, 0.25, 0.25, c
, 0, 0.75, 0.75)
598 continent_scales_x
[c
] = x
*walkspeed_multiplier
*2
599 continent_scales_y
[c
] = y
*walkspeed_multiplier
*2
602 if not cont_heuristic
[c
] then
603 cont_heuristic
[c
] = {}
607 for i
, name
in pairs(QuestHelper_NameLookup
) do
608 if not zone_nodes
[i
] then
611 for key
in pairs(zone_nodes
[i
]) do
612 zone_nodes
[i
][key
] = nil
616 zone_nodes
[i
].c
, zone_nodes
[i
].z
= unpack(QuestHelper_ZoneLookup
[i
])
619 self
:SetupTeleportInfo(self
.teleport_info
, true)
621 --[[for node, info in pairs(self.teleport_info.node) do
622 self:TextOut("You can teleport to "..(node.name or "nil").. " in "..self:TimeString(info[1]+info[2]-GetTime()))
625 if self
.faction
== 1 then
626 for i
, data
in ipairs(static_alliance_routes
) do
627 self
:CreateAndAddStaticNodePair(data
)
629 elseif self
.faction
== 2 then
630 for i
, data
in ipairs(static_horde_routes
) do
631 self
:CreateAndAddStaticNodePair(data
)
635 for i
, data
in ipairs(static_shared_routes
) do
636 self
:CreateAndAddStaticNodePair(data
)
639 if self
.player_level
>= 58 then
640 dark_portal_route
[3] = 5
642 -- If you can't take the route yet, we'll still add it and just pretend it will take a really long time.
643 dark_portal_route
[3] = 86400
646 self
:CreateAndAddStaticNodePair(dark_portal_route
)
648 local st
= self
:CreateTable()
650 for i
, data
in pairs(static_zone_transitions
) do
651 st
[1], st
[2], st
[3] = data
[1], data
[3], data
[4]
653 self
:CreateAndAddTransitionNode(zone_nodes
[data
[1]]
,
655 st
).name
= QHFormat("ZONE_BORDER", QuestHelper_NameLookup
[data
[1]]
, QuestHelper_NameLookup
[data
[2]]
)
658 self
:ReleaseTable(st
)
660 -- Create and link the flight route nodes.
661 local flight_times
= self
.flight_times
662 if not flight_times
then
663 self
:buildFlightTimes()
664 flight_times
= self
.flight_times
667 for start
, list
in pairs(flight_times
) do
668 for dest
, duration
in pairs(list
) do
669 local a_npc
, b_npc
= self
:getFlightInstructor(start
), self
:getFlightInstructor(dest
)
671 if a_npc
and b_npc
then
672 local a
, b
= flight_master_nodes
[start
], flight_master_nodes
[dest
]
675 a
= getNPCNode(a_npc
)
677 flight_master_nodes
[start
] = a
678 a
.name
= (select(3, string.find(start
, "^(.*),")) or start
).." flight point"
683 b
= getNPCNode(b_npc
)
685 flight_master_nodes
[dest
] = b
686 b
.name
= (select(3, string.find(dest
, "^(.*),")) or dest
).." flight point"
691 a
:Link(b
, duration
+5)
697 -- id_from, id_to, and id_local will be used in determining whether there is a point to linking nodes together.
698 for i
, n
in ipairs(self
.world_graph
.nodes
) do
699 n
.id_from
= self
:CreateTable()
700 n
.id_to
= self
:CreateTable()
701 n
.id_local
= self
:CreateTable()
704 -- Setup the local ids a node exists in.
705 for i
, list
in pairs(zone_nodes
) do
706 for _
, n
in ipairs(list
) do
713 -- Figure out where each node can come from or go to.
714 for i
, list
in pairs(zone_nodes
) do
715 for _
, node
in ipairs(list
) do
716 for n
in pairs(node
.n
) do
717 for id
in pairs(n
.id_local
) do node
.id_to
[id
] = true end
718 for id
in pairs(node
.id_local
) do n
.id_from
[id
] = true end
723 -- We'll treat 0 as a special id for where ever it is the player happens to be.
724 for node
in pairs(self
.teleport_info
.node
) do
725 node
.id_from
[0] = true
728 -- Will go through each zone and link all the nodes we have so far with every other node.
729 for _
, list
in pairs(zone_nodes
) do
732 if shouldLink(list
[i
], list
[j
]) then
733 list
[i
]:Link(list
[j
], same_cont_heuristic(list
[i
], list
[j
]))
739 -- We don't need to know where the nodes can go or come from now.
740 for i
, n
in ipairs(self
.world_graph
.nodes
) do
741 self
:ReleaseTable(n
.id_from
)
742 self
:ReleaseTable(n
.id_to
)
743 self
:ReleaseTable(n
.id_local
)
744 n
.id_from
, n
.id_to
, n
.id_local
= nil, nil, nil
747 -- TODO: This is a work around until I fix shouldLink
748 for start
, list
in pairs(flight_times
) do
749 for dest
, duration
in pairs(list
) do
750 local a
, b
= flight_master_nodes
[start
], flight_master_nodes
[dest
]
752 a
:Link(b
, duration
+5)
757 -- TODO: Create a heuristic for this.
759 for i = 1,select("#", GetMapContinents()) do
760 for j = 1,select("#", GetMapContinents()) do
762 cont_heuristic[i][j] = same_cont_heuristic
771 for _, start_node in ipairs(self.world_graph.nodes) do
772 if nodeLeavesContinent(start_node, i) then
773 for _, end_node in ipairs(self.world_graph.nodes) do
774 if nodeLeavesContinent(end_node, j) then
775 if self.world_graph:Search(start_node, end_node) then
776 if isGoodPath(start_node, end_node, i, j) then
777 if not nodes[start_node] then
778 nodes[start_node] = "n"..index
779 code1 = code1.."local "..nodes[start_node].."=math.sqrt(a.x*a.x+a.y*a.y-a.x*("..(2*start_node.x)..")-a.y*("..(2*start_node.y)..")+("..(start_node.x*start_node.x+start_node.y*start_node.y).."))\n"
782 if not nodes[end_node] then
783 nodes[end_node] = "n"..index
784 code1 = code1.."local "..nodes[end_node].."=math.sqrt(b.x*b.x+b.y*b.y-b.x*("..(2*end_node.x)..")-b.y*("..(2*end_node.y)..")+("..(end_node.x*end_node.x+end_node.y*end_node.y).."))\n"
787 if code2 ~= "" then code2 = code2 .. ", " single = false end
788 code2 = code2 .. nodes[start_node] .. "+"..nodes[end_node] .. "+"..(end_node.g)
796 cont_heuristic[i][j] = loadstring("local a,b = ...\n"..code1.."return "..(single and code2 or ("math.min("..code2..")")))
801 -- self.world_graph:SanityCheck()
803 -- TODO: heuristic returns NaNs, fix this.
804 --self.world_graph:SetHeuristic(heuristic)
806 -- Remove objectives again, since we created some for the flight masters.
808 local o
= table.remove(self
.prepared_objectives
)
809 if not o
then break end
811 self
:ReleaseObjectivePathingInfo(o
)
813 if o
.setup_count
> 0 then
814 -- There's a chance an objective could end up in the list twice, but we'll deal with that by not actually
815 -- adding locations for it if it's already setup.
816 table.insert(to_readd
, o
)
821 local obj
= table.remove(to_readd
)
822 if not obj
then break end
824 if not obj
.setup
then -- In case the objective was added multiple times to the to_readd list.
825 obj
.d
= QuestHelper
:CreateTable()
826 obj
.p
= QuestHelper
:CreateTable()
827 obj
.nm
= QuestHelper
:CreateTable()
828 obj
.nm2
= QuestHelper
:CreateTable()
829 obj
.nl
= QuestHelper
:CreateTable()
830 obj
:AppendPositions(obj
, 1, nil)
833 -- Make sure the objectives still contain the positions set by routing.
834 -- The might have shifted slightly, but there's not much I can do about that.
839 for z
, pl
in pairs(obj
.p
) do
840 for i
, point
in ipairs(pl
) do
841 if obj
.old_pos
[1] == point
[1].c
then
842 local x
, y
= obj
.old_pos
[2]-point
[3], obj
.old_pos
[3]-point
[4]
844 if not p
or d2
< d
then
854 self
:ReleaseTable(obj
.old_pos
)
861 for z
, pl
in pairs(obj
.p
) do
862 for i
, point
in ipairs(pl
) do
863 if obj
.old_sop
[1] == point
[1].c
then
864 local x
, y
= obj
.old_sop
[2]-point
[3], obj
.old_sop
[3]-point
[4]
866 if not p
or d2
< d
then
876 self
:ReleaseTable(obj
.old_sop
)
883 self
.pos
[1] = self
.zone_nodes
[self
.i
]
884 for i
, n
in ipairs(self
.pos
[1]) do
885 local a
, b
= n
.x
-self
.pos
[3], n
.y
-self
.pos
[4]
886 self
.pos
[2][i
] = math
.sqrt(a
*a
+b
*b
)
890 -- And if all went according to plan, we now have a graph we can follow to get from anywhere to anywhere.
892 if self
.graph_walker
then
893 self
.graph_walker
:GraphChanged()