6 local function yieldIfNeeded()
7 if call_count
== QuestHelper
.Astrolabe
.WorldMapVisible
and map_rpf
or normal_rpf
then
11 call_count
= call_count
+ 1
15 local function CalcObjectivePriority(obj
)
16 local priority
= obj
.priority
18 for o
in pairs(obj
.before
) do
20 priority
= math
.min(priority
, CalcObjectivePriority(o
))
24 obj
.real_priority
= priority
28 function CalcObjectiveIJ(route
, obj
)
31 for p
, o
in ipairs(route
) do
32 if obj
.real_priority
> o
.real_priority
or obj
.after
[o
] then
34 elseif obj
.real_priority
< o
.real_priority
or obj
.before
[o
] then
42 function QuestHelper
:RemoveIndexFromRoute(array
, distance
, extra
, index
)
46 table.remove(array
, 1)
47 elseif index
== 1 then
48 distance
= distance
- array
[1].len
49 extra
= self
:ComputeTravelTime(self
.pos
, array
[2].pos
)
50 table.remove(array
, 1)
52 elseif index
== #array
then
53 distance
= distance
- array
[index
-1].len
54 table.remove(array
, index
)
56 local a
, b
= array
[index
-1], table.remove(array
, index
)
57 distance
= distance
- a
.len
- b
.len
58 a
.len
= self
:ComputeTravelTime(a
.pos
, array
[index
].pos
)
59 distance
= distance
+ a
.len
63 return distance
, extra
66 function QuestHelper
:InsertObjectiveIntoRoute(array
, distance
, extra
, objective
)
67 -- array - Contains the path you want to insert into.
68 -- distance - How long is the path so far?
69 -- extra - How far is it from the player to the first node?
70 -- objective - Where are we trying to get to?
72 -- In addition, objective needs i and j set, for the min and max indexes it can be inserted into.
75 -- index - The index to inserted into.
76 -- distance - The new length of the path.
77 -- extra - The new distance from the first node to the player.
80 extra
, objective
.pos
= objective
:TravelTime(self
.pos
)
82 table.insert(array
, 1, objective
)
86 local best_index
, best_extra
, best_total
, best_len1
, best_len2
, bp
88 local low
, high
= CalcObjectiveIJ(array
, objective
)
92 best_extra
, best_len2
, bp
= objective
:TravelTime2(self
.pos
, array
[1].pos
)
93 best_total
= best_extra
+distance
+best_len2
94 elseif low
== #array
+1 then
95 local o
= array
[#array
]
96 o
.len
, objective
.pos
= objective
:TravelTime(array
[#array
].pos
)
98 table.insert(array
, objective
)
99 return #array
, distance
+o
.len
, extra
101 local a
= array
[low
-1]
103 best_len1
, best_len2
, bp
= objective
:TravelTime2(a
.pos
, array
[low
].pos
)
105 best_total
= distance
- a
.len
+ best_len1
+ best_len2
+ extra
109 local total
= distance
+extra
111 for i
= low
+1, math
.min(#array
, high
) do
113 local l1
, l2
, p
= objective
:TravelTime2(a
.pos
, array
[i
].pos
)
114 local d
= total
- a
.len
+ l1
+ l2
115 if d
< best_total
then
126 if high
== #array
+1 then
127 local l1
, p
= objective
:TravelTime(array
[#array
].pos
)
130 if d
< best_total
then
132 array
[#array
].len
= l1
133 table.insert(array
, objective
)
134 return #array
, d
-extra
, extra
140 if best_index
> 1 then array
[best_index
-1].len
= best_len1
end
141 objective
.len
= best_len2
142 table.insert(array
, best_index
, objective
)
143 return best_index
, best_total
-best_extra
, best_extra
147 function QuestHelper
:RemoveIndexFromRouteSOP(array
, distance
, extra
, index
)
151 table.remove(array
, 1)
152 elseif index
== 1 then
153 distance
= distance
- array
[1].nel
154 extra
= self
:ComputeTravelTime(self
.pos
, array
[2].sop
)
155 table.remove(array
, 1)
157 elseif index
== #array
then
158 distance
= distance
- array
[index
-1].nel
159 table.remove(array
, index
)
161 local a
, b
= array
[index
-1], table.remove(array
, index
)
162 distance
= distance
- a
.nel
- b
.nel
163 a
.nel
= self
:ComputeTravelTime(a
.sop
, array
[index
].sop
)
164 distance
= distance
+ a
.nel
168 return distance
, extra
171 function QuestHelper
:InsertObjectiveIntoRouteSOP(array
, distance
, extra
, objective
)
172 -- array - Contains the path you want to insert into.
173 -- distance - How long is the path so far?
174 -- extra - How far is it from the player to the first node?
175 -- objective - Where are we trying to get to?
177 -- In addition, objective needs i and j set, for the min and max indexes it can be inserted into.
180 -- index - The index to inserted into.
181 -- distance - The new length of the path.
182 -- extra - The new distance from the first node to the player.
185 extra
, objective
.sop
= objective
:TravelTime(self
.pos
)
187 table.insert(array
, 1, objective
)
191 local best_index
, best_extra
, best_total
, best_len1
, best_len2
, bp
193 local low
, high
= CalcObjectiveIJ(array
, objective
)
197 best_extra
, best_len2
, bp
= objective
:TravelTime2(self
.pos
, array
[1].sop
)
198 best_total
= best_extra
+distance
+best_len2
200 elseif low
== #array
+1 then
201 local o
= array
[#array
]
202 o
.nel
, objective
.sop
= objective
:TravelTime(array
[#array
].sop
)
204 table.insert(array
, objective
)
205 return #array
, distance
+o
.nel
, extra
207 local a
= array
[low
-1]
209 best_len1
, best_len2
, bp
= objective
:TravelTime2(a
.sop
, array
[low
].sop
)
211 best_total
= distance
- a
.nel
+ best_len1
+ best_len2
+ extra
215 local total
= distance
+extra
217 for i
= low
+1, math
.min(#array
, high
) do
219 local l1
, l2
, p
= objective
:TravelTime2(a
.sop
, array
[i
].sop
)
220 local d
= total
- a
.nel
+ l1
+ l2
221 if d
< best_total
then
232 if high
== #array
+1 then
233 local l1
, p
= objective
:TravelTime(array
[#array
].sop
)
236 if d
< best_total
then
238 array
[#array
].nel
= l1
239 table.insert(array
, objective
)
240 return #array
, d
-extra
, extra
246 if best_index
> 1 then array
[best_index
-1].nel
= best_len1
end
247 objective
.nel
= best_len2
248 table.insert(array
, best_index
, objective
)
249 return best_index
, best_total
-best_extra
, best_extra
255 local function RouteUpdateRoutine(self
)
256 map_walker
= self
:CreateWorldMapWalker()
257 local minimap_dodad
= self
:CreateMipmapDodad()
258 local swap_table
= {}
259 local distance
, extra
, route
, new_distance
, new_extra
, new_route
, shuffle
, insert
, point
= 0, 0, self
.route
, 0, 0, {}, {}, 0, nil
260 local recheck_pos
, new_recheck_pos
, new_local_minima
= 1, 99999, true
262 self
.minimap_dodad
= minimap_dodad
265 for i
,o
in ipairs(route
) do
266 o
.filter_zone
= o
.location
[1] ~= self
.pos
[1]
268 if not o
:Known() then
269 -- Objective was probably made to depend on an objective that we don't know about yet.
270 -- We add it to both lists, because although we need to remove it, we need it added again when we can.
271 -- This creats an inconsistancy, but it'll get fixed in the removal loop before anything has a chance to
274 self
.to_remove
[o
] = true
275 self
.to_add
[o
] = true
277 CalcObjectivePriority(o
)
279 if o
.is_sharing
~= o
.want_share
then
280 o
.is_sharing
= o
.want_share
283 self
:DoShareObjective(o
)
285 self
:DoUnshareObjective(o
)
291 local original_size
= #route
293 -- Remove any waypoints if needed.
295 local obj
= next(self
.to_remove
)
296 if not obj
then break end
297 self
.to_remove
[obj
] = nil
299 if obj
.is_sharing
then
300 obj
.is_sharing
= false
301 self
:DoUnshareObjective(obj
)
304 self
:ReleaseTeleportInfo(obj
.tele_pos
)
305 self
:ReleaseTeleportInfo(obj
.tele_sop
)
306 obj
.tele_pos
, obj
.tele_sop
= nil, nil
308 for i
, o
in ipairs(route
) do
312 minimap_dodad
:SetObjective(nil)
314 minimap_dodad
:SetObjective(route
[2])
318 if recheck_pos
> i
then recheck_pos
= recheck_pos
- 1 end
320 distance
, extra
= self
:RemoveIndexFromRoute(route
, distance
, extra
, i
)
325 for i
, o
in ipairs(new_route
) do
327 if new_recheck_pos
> i
then new_recheck_pos
= new_recheck_pos
- 1 end
328 new_distance
, new_extra
= self
:RemoveIndexFromRouteSOP(new_route
, new_distance
, new_extra
, i
)
337 local obj
= next(self
.to_add
)
338 if not obj
then break end
339 self
.to_add
[obj
] = nil
344 obj
.filter_zone
= obj
.location
[1] ~= self
.pos
[1]
346 if obj
.filter_zone
and QuestHelper_Pref
.filter_zone
then
347 -- Not going to add it, wrong zone.
349 swap_table
[obj
] = true
351 obj
.tele_pos
= self
:CreateTeleportInfo()
352 obj
.tele_sop
= self
:CreateTeleportInfo()
354 if not obj
.is_sharing
and obj
.want_share
then
355 obj
.is_sharing
= true
356 self
:DoShareObjective(obj
)
359 CalcObjectivePriority(obj
)
362 insert
, distance
, extra
= self
:InsertObjectiveIntoRoute(route
, 0, 0, obj
)
363 insert
, new_distance
, new_extra
= self
:InsertObjectiveIntoRouteSOP(new_route
, 0, 0, obj
)
365 minimap_dodad
:SetObjective(obj
)
367 insert
, distance
, extra
= self
:InsertObjectiveIntoRoute(route
, distance
, extra
, obj
)
370 minimap_dodad
:SetObjective(obj
)
373 insert
, new_distance
, new_extra
= self
:InsertObjectiveIntoRouteSOP(new_route
, new_distance
, new_extra
, obj
)
377 swap_table
[obj
] = true
381 for obj
in pairs(swap_table
) do
382 -- If one of the objectives we were considering adding was removed, it would be in both lists.
383 -- That would be bad. We can't remove it because we haven't actually added it yet, so
384 -- handle that special case here.
385 if self
.to_remove
[obj
] then
386 self
.to_remove
[obj
] = nil
387 self
.to_add
[obj
] = nil
391 self
.to_add
, swap_table
= swap_table
, self
.to_add
393 -- If size decreased, all the old indexes need to be reset.
394 if #route
< original_size
then
400 -- Append new indexes to shuffle.
401 for i
=original_size
+1,#route
do
406 if recheck_pos
> #route
then recheck_pos
= 1 end
407 if new_recheck_pos
> #route
then
409 if new_local_minima
then
410 -- Start try something new, we can't seem to get what we have to be any better.
412 for i
=1,#route
-1 do -- Shuffling the order we'll add the nodes in.
413 local r
= math
.random(i
, #route
)
415 shuffle
[i
], shuffle
[r
] = shuffle
[r
], shuffle
[i
]
419 while #new_route
> 0 do
420 table.remove(new_route
)
423 point
= route
[shuffle
[1]]
424 insert
, new_distance
, new_extra
= self
:InsertObjectiveIntoRouteSOP(new_route
, 0, 0, point
)
426 -- Insert the rest of the points.
428 point
= route
[shuffle
[i]]
430 insert
, new_distance
, new_extra
= self
:InsertObjectiveIntoRouteSOP(new_route
, new_distance
, new_extra
, point
)
433 new_local_minima
= true
436 point
= route
[recheck_pos
]
437 self
.limbo_node
= point
438 distance
, extra
= self
:RemoveIndexFromRoute(route
, distance
, extra
, recheck_pos
)
439 insert
, distance
, extra
= self
:InsertObjectiveIntoRoute(route
, distance
, extra
, point
)
440 self
.limbo_node
= nil
442 if insert
== 1 or recheck_pos
== 1 then
443 minimap_dodad
:SetObjective(route
[1])
446 point
= new_route
[new_recheck_pos
]
447 new_distance
, new_extra
= self
:RemoveIndexFromRouteSOP(new_route
, new_distance
, new_extra
, new_recheck_pos
)
448 insert
, new_distance
, new_extra
= self
:InsertObjectiveIntoRouteSOP(new_route
, new_distance
, new_extra
, point
)
449 if insert
~= new_recheck_pos
then
450 new_local_minima
= false
453 recheck_pos
= recheck_pos
+ 1
454 new_recheck_pos
= new_recheck_pos
+ 1
457 if new_distance
+new_extra
+0.001 < distance
+extra
then
458 for i
, node
in ipairs(route
) do
460 node
.tele_pos
, node
.tele_sop
= node
.tele_sop
, node
.tele_pos
461 node
.pos
, node
.sop
= node
.sop
, node
.pos
464 route
, new_route
= new_route
, route
465 distance
, new_distance
= new_distance
, distance
466 extra
, new_extra
= new_extra
, extra
470 minimap_dodad
:SetObjective(route
[1])
472 for i
=1,#route
-1 do -- Shuffling the order we'll add the nodes in.
473 local r
= math
.random(i
, #route
)
475 shuffle
[i
], shuffle
[r
] = shuffle
[r
], shuffle
[i
]
479 while #new_route
> 0 do
480 table.remove(new_route
)
483 point
= route
[shuffle
[1]]
484 insert
, new_distance
, new_extra
= self
:InsertObjectiveIntoRouteSOP(new_route
, 0, 0, point
)
486 -- Insert the rest of the points.
488 point
= route
[shuffle
[i]]
489 insert
, new_distance
, new_extra
= self
:InsertObjectiveIntoRouteSOP(new_route
, new_distance
, new_extra
, point
)
492 recheck_pos
= new_recheck_pos
494 new_local_minima
= true
497 -- Thats enough work for now, we'll continue next frame.
498 if route_pass
> 0 then
499 route_pass
= route_pass
- 1
502 map_walker
:RouteChanged()
506 self
:SetupTeleportInfo(self
.teleport_info
)
508 if self
.defered_graph_reset
then
510 self
.defered_graph_reset
= false
517 function QuestHelper
:ForceRouteUpdate(passes
)
518 route_pass
= math
.max(2, passes
or 0)
520 while route_pass
~= 0 do
521 if coroutine
.status(self
.update_route
) == "dead" then
525 local state
, err
= coroutine
.resume(self
.update_route
, self
)
527 self
:TextOut("|cffff0000The routing co-routine just exploded|r: |cffffff77"..err
.."|r")
533 QuestHelper
.update_route
= coroutine
.create(RouteUpdateRoutine
)