965e8589c9
I implemented a QuestHelper_GetTime function for the next time Blizzard decides to fiddle with the time functions. It returns debugprofilestop() / 1000, to exactly match the precision of GetTime(). I also re-removed references to Cartographer from the rollback.
673 lignes
23 Kio
Lua
673 lignes
23 Kio
Lua
|
|
local GetTime = QuestHelper_GetTime
|
|
|
|
QuestHelper_File["graph_core.lua"] = "4.0.1.$svnversion$"
|
|
QuestHelper_Loadtime["graph_core.lua"] = GetTime()
|
|
|
|
-- Alright so what's the interface here
|
|
-- There's the obvious "find a path from A to B"
|
|
-- function QH_Graph_Pathfind(st, nd, make_path)
|
|
-- Right now, we're pretending that the world consists of infinite planes with links between them. That's where the whole "between-zone" thing occurs. So one way or another, we need to add links. We'll use name as an identifier so we can get rid of flight paths links later, and the coords will include a plane ID number.
|
|
-- function QH_Graph_Plane_Makelink(name, coord1, coord2, cost)
|
|
-- And then we need a way to get rid of links, so:
|
|
|
|
-- how does flying work zorba
|
|
-- how can we fly
|
|
-- howwwwww
|
|
|
|
-- Make a map from "phase" to "flyphase". Store all the links we're being told to make. When placing links, if the flyphase is flyable, we use the flyphase instead of the phase for placing. We don't place if it's an internal boundary (there's a few ways we can detect this, but let's just use the hacky one where we just look at the ID.) From there it's all pretty standard.
|
|
|
|
-- performance hack :(
|
|
local QH_Timeslice_Yield = QH_Timeslice_Yield
|
|
local tinsert, tremove = table.insert, table.remove
|
|
local pairs, ipairs = pairs, ipairs
|
|
local sqrt = math.sqrt
|
|
local GetTime = GetTime
|
|
|
|
local function heap_left(x) return (2*x) end
|
|
local function heap_right(x) return (2*x + 1) end
|
|
|
|
local function heap_sane(heap)
|
|
local dmp = ""
|
|
local finishbefore = 2
|
|
for i = 1, #heap do
|
|
if i == finishbefore then
|
|
print(dmp)
|
|
dmp = ""
|
|
finishbefore = finishbefore * 2
|
|
end
|
|
dmp = dmp .. string.format("%f ", heap[i].c)
|
|
end
|
|
print(dmp)
|
|
print("")
|
|
for i = 1, #heap do
|
|
--[[ assert(not heap[heap_left(i)] or heap[i].c <= heap[heap_left(i)].c) ]]
|
|
--[[ assert(not heap[heap_right(i)] or heap[i].c <= heap[heap_right(i)].c) ]]
|
|
end
|
|
end
|
|
|
|
local function heap_insert(heap, item)
|
|
--[[ assert(item) ]]
|
|
tinsert(heap, item)
|
|
local pt = #heap
|
|
while pt > 1 do
|
|
local ptd2 = math.floor(pt / 2)
|
|
if heap[ptd2].c <= heap[pt].c then
|
|
break
|
|
end
|
|
local tmp = heap[pt]
|
|
heap[pt] = heap[ptd2]
|
|
heap[ptd2] = tmp
|
|
pt = ptd2
|
|
end
|
|
--heap_sane(heap)
|
|
end
|
|
|
|
local function heap_extract(heap)
|
|
local rv = heap[1]
|
|
if #heap == 1 then tremove(heap) return rv end
|
|
heap[1] = tremove(heap)
|
|
local idx = 1
|
|
while idx < #heap do
|
|
local minix = idx
|
|
--local hl, hr = heap_left(idx), heap_right(idx)
|
|
local hl, hr = 2*idx, 2*idx+1 -- these had better be equivalent to the line above one
|
|
if heap[hl] and heap[hl].c < heap[minix].c then minix = hl end
|
|
if heap[hr] and heap[hr].c < heap[minix].c then minix = hr end
|
|
if minix ~= idx then
|
|
local tx = heap[minix]
|
|
heap[minix] = heap[idx]
|
|
heap[idx] = tx
|
|
idx = minix
|
|
else
|
|
break
|
|
end
|
|
end
|
|
--heap_sane(heap)
|
|
return rv
|
|
end
|
|
|
|
-- incremented on each graph iteration, so we don't have to clear everything. "GRaph ID" :D
|
|
local grid = 0
|
|
|
|
-- Plane format: each plane contains an array of nodes that exist in that plane.
|
|
-- Each node contains both its parent ID and its coordinates within that plane. It may contain a node it links to, along with cost.
|
|
local plane = {}
|
|
|
|
local plane_to_flyplane = {}
|
|
local continent_to_flyplane = {}
|
|
local flyplanes_enabled = {}
|
|
local plane_multiplier = {}
|
|
local plane_cull = {}
|
|
|
|
-- canonical plane :D
|
|
local function canoplane(plane)
|
|
if flyplanes_enabled[plane_to_flyplane[plane]] then return plane_to_flyplane[plane] else return plane end
|
|
end
|
|
|
|
local function xydist_raw(plane, stx, sty, ndx, ndy)
|
|
local dx, dy = stx - ndx, sty - ndy
|
|
return sqrt(dx * dx + dy * dy) / (plane_multiplier[plane] or 7)
|
|
end
|
|
local function xydist(st, nd)
|
|
--QuestHelper: Assert(canoplane(st.p) == canoplane(nd.p))
|
|
local dx, dy = st.x - nd.x, st.y - nd.y
|
|
return sqrt(dx * dx + dy * dy) / (plane_multiplier[canoplane(nd.p)] or 7) -- we're getting a result in seconds, not in yards
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
function QH_Graph_Pathfind(st, nd, reverse, make_path)
|
|
return QH_Graph_Pathmultifind(st, {nd}, reverse, make_path)[1]
|
|
end
|
|
|
|
local active = false
|
|
local prepared = false
|
|
|
|
|
|
local extrctime = 0
|
|
local wextrctime = 0
|
|
local actualtime = 0
|
|
local planetime = 0
|
|
local linktime = 0
|
|
|
|
function QH_Graph_Pathmultifind(st, nda, reverse, make_path)
|
|
--QuestHelper:TextOut("Starting PMF")
|
|
QuestHelper: Assert(not active)
|
|
QuestHelper: Assert(prepared)
|
|
QuestHelper: Assert(st.x and st.y and st.p)
|
|
active = true -- The fun thing about coroutines is that this is actually safe.
|
|
local out = QuestHelper:CreateTable("graphcore output") -- THIS HAD BETTER BE RELEASABLE OR IT WILL BE BAD
|
|
|
|
local undone = QuestHelper:CreateTable("graphcore undone")
|
|
local remaining = 0
|
|
|
|
local link = QuestHelper:CreateTable("graphcore link")
|
|
|
|
--local stats = QuestHelper:CreateTable("graphcore --stats")
|
|
|
|
--stats.dests_complex = 0
|
|
--stats.dests_total = 0
|
|
|
|
-- Ugly database hack
|
|
if st.p == 26 then st.p = 48 end -- Alterac Mountains merged to Hillsbrad Foothills
|
|
if st.p == 38 then st.p = 168 end -- Ditto Stranglethorn
|
|
-- end hack
|
|
|
|
for k, v in ipairs(nda) do
|
|
QuestHelper: Assert(v.x and v.y and v.p)
|
|
-- Ugly database hack pt. 2
|
|
if v.p == 26 then v.p = 48 end -- Alterac Mountains merged to Hillsbrad Foothills
|
|
if v.p == 38 then v.p = 168 end -- Ditto Stranglethorn
|
|
-- end hack
|
|
local cpvp = canoplane(v.p)
|
|
if plane[cpvp] then
|
|
--print("Destination plane insertion")
|
|
local dest = QuestHelper:CreateTable("graphcore destination")
|
|
dest.x, dest.y, dest.p, dest.goal = v.x, v.y, cpvp, k
|
|
link[k] = dest
|
|
tinsert(plane[cpvp], link[k])
|
|
undone[k] = true
|
|
remaining = remaining + 1
|
|
--stats.dests_complex = --stats.dests_complex + 1
|
|
end
|
|
--stats.dests_total = --stats.dests_total + 1
|
|
end
|
|
|
|
local link_id = reverse and "rlinks" or "links"
|
|
|
|
--stats.node_initialized_first = 0
|
|
--stats.node_done = 0
|
|
--stats.node_done_already = 0
|
|
--stats.node_modified_before_use = 0
|
|
--stats.node_link_reprocessed = 0
|
|
--stats.node_link_first = 0
|
|
--stats.node_link_alreadydone = 0
|
|
--stats.node_inner_reprocessed = 0
|
|
--stats.node_inner_first = 0
|
|
--stats.node_inner_alreadydone = 0
|
|
|
|
local dijheap = QuestHelper:CreateTable("graphcore heap container")
|
|
local stnode = QuestHelper:CreateTable("graphcore stnode")
|
|
do
|
|
stnode.x, stnode.y, stnode.p, stnode.c, stnode.scan_id, stnode.scan_cost = st.x, st.y, canoplane(st.p), st.c, grid, 0
|
|
QuestHelper: Assert(plane[canoplane(st.p)], "Plane " .. canoplane(st.p) .. " does not exist.")
|
|
tinsert(plane[stnode.p], stnode)
|
|
|
|
local hep = QuestHelper:CreateTable("graphcore heap")
|
|
hep.c, hep.n = 0, stnode -- more than the subtraction, less than the minimum
|
|
heap_insert(dijheap, hep)
|
|
end
|
|
|
|
--stats.heap_max = #dijheap
|
|
|
|
--QuestHelper:TextOut("Starting routing")
|
|
|
|
local extrc = 0
|
|
local actual = 0
|
|
local planescan = 0
|
|
local linkscan = 0
|
|
|
|
local planeyes = 0
|
|
local planeno = 0
|
|
|
|
local linkyes = 0
|
|
local linkno = 0
|
|
|
|
while remaining > 0 and #dijheap > 0 do
|
|
QH_Timeslice_Yield()
|
|
local ett = GetTime()
|
|
extrc = extrc + 1
|
|
--stats.heap_max = math.max(--stats.heap_max, #dijheap)
|
|
local cdj = heap_extract(dijheap)
|
|
|
|
local snode = cdj.n
|
|
--print(string.format("Extracted cost %f/%s pointing at %d/%f/%f", cdj.c, tostring(cdj.n.scan_cost), cdj.n.p, cdj.n.x, cdj.n.y))
|
|
if snode.scan_cost == cdj.c then -- if we've modified it since then, don't bother
|
|
local actt = GetTime()
|
|
actual = actual + 1
|
|
-- Are we at an end node?
|
|
if snode.goal then
|
|
-- we wouldn't be here if we hadn't found a better solution
|
|
--QuestHelper: Assert(undone[snode.goal])
|
|
out[snode.goal] = cdj.c
|
|
undone[snode.goal] = nil
|
|
remaining = remaining - 1
|
|
end
|
|
|
|
-- Link to everything else on the plane
|
|
if not cdj.pi then -- Did we come from this plane? If so, there's no reason to scan it again (flag means "plane internal")
|
|
local planet = GetTime()
|
|
planescan = planescan + 1
|
|
for _, v in ipairs(plane[snode.p]) do
|
|
if v.scan_id ~= grid or v.scan_cost > snode.scan_cost then
|
|
planeyes = planeyes + 1
|
|
local dst = xydist(snode, v)
|
|
local modcost = snode.scan_cost + dst
|
|
--print(string.format("Doing %d/%f vs %s/%s at %d/%f/%f", grid, modcost, tostring(v.scan_id), tostring(v.scan_cost), v.p, v.x, v.y))
|
|
if v.scan_id ~= grid or v.scan_cost > modcost then
|
|
v.scan_id = grid
|
|
v.scan_cost = modcost
|
|
v.scan_from = snode
|
|
v.scan_processed = false
|
|
v.scan_outnode = nil
|
|
v.scan_outnode_from = nil
|
|
|
|
do
|
|
local ncdj = QuestHelper:CreateTable("graphcore heap")
|
|
ncdj.c, ncdj.n, ncdj.pi = modcost, v, true
|
|
heap_insert(dijheap, ncdj)
|
|
--print(string.format("Inserting %f at %d/%f/%f, plane", snude.c, v.p, v.x, v.y))
|
|
end
|
|
end
|
|
else
|
|
planeno = planeno + 1
|
|
end
|
|
end
|
|
planetime = planetime + GetTime() - planet
|
|
end
|
|
|
|
-- Link to everything we link to
|
|
if not cdj.li and snode[link_id] then
|
|
local linkt = GetTime()
|
|
linkscan = linkscan + 1
|
|
for _, lnk in pairs(snode[link_id]) do
|
|
local mc2 = snode.scan_cost + lnk.cost
|
|
local linkto = lnk.link
|
|
if linkto.scan_id ~= grid or linkto.scan_cost > mc2 then
|
|
linkyes = linkyes + 1
|
|
linkto.scan_id = grid
|
|
linkto.scan_cost = mc2
|
|
linkto.scan_from = snode
|
|
linkto.scan_outnode = lnk.outnode_to
|
|
linkto.scan_outnode_from = lnk.outnode_from
|
|
|
|
local hep = QuestHelper:CreateTable("graphcore heap")
|
|
hep.c, hep.n, hep.li = mc2, linkto, true
|
|
heap_insert(dijheap, hep)
|
|
--print(string.format("Inserting %f at %d/%f/%f, link", hep.c, linkto.p, linkto.x, linkto.y))
|
|
else
|
|
linkno = linkno + 1
|
|
end
|
|
end
|
|
linktime = linktime + GetTime() - linkt
|
|
end
|
|
actualtime = actualtime + GetTime() - actt
|
|
extrctime = extrctime + GetTime() - ett
|
|
else
|
|
wextrctime = wextrctime + GetTime() - ett
|
|
end
|
|
QuestHelper:ReleaseTable(cdj)
|
|
end
|
|
|
|
--QuestHelper:TextOut(string.format("%d extracted, %d processed, %d planescan, %d linkscan, %d/%d plane, %d/%d link", extrc, actual, planescan, linkscan, planeyes, planeno, linkyes, linkno))
|
|
--QuestHelper:TextOut(string.format("times: %f/%f extracted, %f processed, %f planescan, %f linkscan", extrctime, wextrctime, actualtime, planetime, linktime))
|
|
|
|
for _, v in ipairs(dijheap) do
|
|
QuestHelper:ReleaseTable(v)
|
|
end
|
|
QuestHelper:ReleaseTable(dijheap)
|
|
dijheap = nil
|
|
|
|
--QuestHelper:TextOut(string.format("Earlyout with %d/%d remaining", #dijheap, remaining))
|
|
if remaining > 0 then
|
|
for k, v in ipairs(nda) do
|
|
if not out[k] then
|
|
QuestHelper: Assert(false, string.format("Couldn't find path to %d/%f/%f from %d/%f/%f", nda[k].p, nda[k].x, nda[k].y, st.p, st.x, st.y))
|
|
end
|
|
end
|
|
end
|
|
QuestHelper: Assert(remaining == 0)
|
|
|
|
grid = grid + 1
|
|
QuestHelper: Assert(grid < 1000000000) -- if this ever triggers I will be amazed
|
|
|
|
if make_path then
|
|
--print("mpath")
|
|
for k, v in pairs(out) do
|
|
QH_Timeslice_Yield()
|
|
--print(out[k])
|
|
local rp = QuestHelper:CreateTable("graphcore return")
|
|
rp.d = v
|
|
|
|
out[k] = rp
|
|
--print(out[k])
|
|
|
|
if link[k] then
|
|
QuestHelper: Assert(link[k].scan_from)
|
|
local tpath = reverse and rp or QuestHelper:CreateTable("graphcore path reversal")
|
|
local cpx = link[k].scan_from
|
|
local mdlast = nil
|
|
while cpx do
|
|
QuestHelper: Assert(cpx)
|
|
|
|
if cpx.scan_outnode then
|
|
tinsert(tpath, cpx.scan_outnode)
|
|
end
|
|
if cpx.scan_outnode_from then
|
|
tinsert(tpath, cpx.scan_outnode_from)
|
|
end
|
|
|
|
cpx = cpx.scan_from
|
|
end
|
|
QuestHelper: Assert(not mdlast)
|
|
|
|
if not reverse then
|
|
for i = #tpath, 1, -1 do
|
|
tinsert(rp, tpath[i])
|
|
end
|
|
|
|
QuestHelper: Assert(tpath ~= rp)
|
|
QuestHelper:ReleaseTable(tpath)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
QuestHelper:ReleaseTable(table.remove(plane[stnode.p])) -- always added last, so we remove it first
|
|
for k, v in ipairs(nda) do
|
|
if plane[canoplane(v.p)] and plane[canoplane(v.p)][#plane[canoplane(v.p)]].goal then -- might not be the exact one, but we'll remove 'em all once we get there anyway :D
|
|
QuestHelper:ReleaseTable(table.remove(plane[canoplane(v.p)]))
|
|
end
|
|
end
|
|
|
|
QuestHelper:ReleaseTable(link)
|
|
QuestHelper:ReleaseTable(undone)
|
|
|
|
--QuestHelper:TextOut("Finishing")
|
|
|
|
--for k, v in pairs(stats) do
|
|
--print(k, v)
|
|
--end
|
|
|
|
active = false
|
|
return out -- THIS HAD BETTER BE RELEASABLE OR IT WILL BE BAD
|
|
end
|
|
|
|
function QH_Graph_Init()
|
|
for c, d in pairs(QuestHelper_IndexLookup) do
|
|
if type(c) == "number" then
|
|
QuestHelper: Assert(d[0])
|
|
continent_to_flyplane[c] = d[0]
|
|
for z, p in pairs(d) do
|
|
if type(z) == "number" then
|
|
--QuestHelper:TextOut(string.format("%d/%d: %d", c, z, p))
|
|
plane_to_flyplane[p] = d[0]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local linkages = {}
|
|
|
|
local function findnode(coord)
|
|
local p = canoplane(coord.p)
|
|
if not plane[p] then plane[p] = {} end
|
|
for _, v in ipairs(plane[p]) do
|
|
if v.x == coord.x and v.y == coord.y and v.name == coord.name then
|
|
return v
|
|
end
|
|
end
|
|
|
|
local nd = {x = coord.x, y = coord.y, p = p, name = coord.name}
|
|
tinsert(plane[p], nd)
|
|
return nd
|
|
end
|
|
|
|
function QH_Graph_Plane_Makelink(name, coord1, coord2, cost, cost_reverse)
|
|
QuestHelper: Assert(not active)
|
|
prepared = false
|
|
|
|
--QuestHelper: TextOut(string.format("Link '%s' made from %d/%f/%f to %d/%f/%f of cost %f, asymflag %s", name, coord1.p, coord1.x, coord1.y, coord2.p, coord2.x, coord2.y, cost, tostring(not not asymmetrical)))
|
|
QuestHelper: Assert(name)
|
|
QuestHelper: Assert(coord1)
|
|
QuestHelper: Assert(coord2)
|
|
QuestHelper: Assert(cost)
|
|
|
|
QuestHelper: Assert(cost >= 0)
|
|
QuestHelper: Assert(not cost_reverse or cost_reverse >= 0)
|
|
|
|
--cost = math.max(cost, 0.01)
|
|
--if cost_reverse then cost_reverse = math.max(cost_reverse, 0.01) end
|
|
|
|
local tlink = {name, coord1, coord2, cost, cost_reverse}
|
|
if not linkages[name] then linkages[name] = {} end
|
|
tinsert(linkages[name], tlink)
|
|
--print(name, coord1.map_desc[1], coord2.map_desc[1], coord)
|
|
end
|
|
|
|
function QH_Graph_Plane_Destroylinks(name)
|
|
QuestHelper: Assert(not active)
|
|
prepared = false
|
|
|
|
linkages[name] = nil
|
|
|
|
-- we'll actually clean things out once the refresh function is called
|
|
end
|
|
|
|
function QH_Graph_Flyplaneset(fpset, speed, cull)
|
|
QuestHelper: Assert(not active)
|
|
prepared = false
|
|
|
|
local index = QuestHelper_IndexLookup[fpset][0]
|
|
if not flyplanes_enabled[index] or plane_multiplier[index] ~= speed or plane_cull[index] ~= cull then
|
|
flyplanes_enabled[index] = true
|
|
plane_multiplier[index] = speed
|
|
plane_cull[index] = cull
|
|
end
|
|
|
|
-- we'll actually clean things out once the refresh function is called
|
|
end
|
|
|
|
function QH_Graph_Plane_Refresh()
|
|
--QuestHelper:TextOut("Graph plane refresh now")
|
|
QuestHelper: Assert(not active)
|
|
|
|
plane = {} -- reset it all
|
|
|
|
-- we take all our links, process them, and jam them together
|
|
-- mmmm
|
|
-- jam
|
|
|
|
for name, v in pairs(linkages) do
|
|
--print("Starting linkage", name)
|
|
local titx = {}
|
|
local nodeitems = {}
|
|
local nodedests = {}
|
|
|
|
local function makenodedest(outnode, package)
|
|
if not nodedests[outnode] then
|
|
nodedests[outnode] = {x = package.x, y = package.y, p = package.p, c = package.c, map_desc = package.map_desc, condense_class = package.condense_class} -- note: this is where the actual node objects that eventually get passed into the routing controller's path replot engine come from. So if you intend for anything to exist in that module, it's gotta be inserted here.
|
|
end
|
|
end
|
|
|
|
for _, i in ipairs(v) do
|
|
local name, coord1, coord2, cost, cost_reverse = unpack(i)
|
|
|
|
QuestHelper: Assert(name)
|
|
QuestHelper: Assert(coord1)
|
|
QuestHelper: Assert(coord2)
|
|
QuestHelper: Assert(cost)
|
|
|
|
i.n1, i.n2 = findnode(coord1), findnode(coord2)
|
|
|
|
table.insert(titx, i)
|
|
|
|
if not nodeitems[i.n1] then nodeitems[i.n1] = QuestHelper:CreateTable("graph plane refresh") end
|
|
if not nodeitems[i.n2] then nodeitems[i.n2] = QuestHelper:CreateTable("graph plane refresh") end
|
|
|
|
table.insert(nodeitems[i.n1], i)
|
|
table.insert(nodeitems[i.n2], i)
|
|
|
|
makenodedest(i.n1, coord1)
|
|
makenodedest(i.n2, coord2)
|
|
end
|
|
|
|
--QuestHelper:TextOut(string.format("%d titx", #titx))
|
|
|
|
-- all nodes are created, links are posted
|
|
|
|
local nodedone = {}
|
|
local mark
|
|
mark = function(tnode, acum)
|
|
if acum[tnode] then return end
|
|
acum[tnode] = true
|
|
nodedone[tnode] = true
|
|
|
|
for _, d in ipairs(nodeitems[tnode]) do
|
|
mark(d.n1, acum)
|
|
mark(d.n2, acum)
|
|
end
|
|
end
|
|
|
|
local infinity = 1e10
|
|
|
|
for _, connect in ipairs(titx) do
|
|
QH_Timeslice_Yield()
|
|
|
|
QuestHelper: Assert(nodedone[connect.n1] == nodedone[connect.n2])
|
|
|
|
if not nodedone[connect.n1] then
|
|
local nods = QuestHelper:CreateTable("graph plane nods")
|
|
local nods_reverse = QuestHelper:CreateTable("graph plane nods_reverse")
|
|
mark(connect.n1, nods)
|
|
|
|
local lookupindex = 1
|
|
for k, v in pairs(nods) do
|
|
nods[k] = lookupindex
|
|
nods_reverse[lookupindex] = k
|
|
lookupindex = lookupindex + 1
|
|
end
|
|
|
|
--QuestHelper:TextOut(string.format("Processing cluster of %d", lookupindex))
|
|
|
|
local tab = QuestHelper:CreateTable("graph plane floyd core")
|
|
for r = 1, lookupindex do
|
|
local inner = QuestHelper:CreateTable("graph plane floyd inner")
|
|
table.insert(tab, inner)
|
|
for tr = 1, lookupindex do
|
|
table.insert(inner, infinity)
|
|
end
|
|
end
|
|
|
|
for k, _ in pairs(nods) do
|
|
for _, titem in pairs(nodeitems[k]) do
|
|
local a, b = nods[titem.n1], nods[titem.n2]
|
|
tab[a][b] = math.min(tab[a][b], titem[4])
|
|
if titem[5] then
|
|
tab[b][a] = math.min(tab[b][a], titem[5])
|
|
end
|
|
end
|
|
end
|
|
|
|
for pivot in ipairs(tab) do
|
|
for s in ipairs(tab) do
|
|
for e in ipairs(tab) do
|
|
tab[s][e] = math.min(tab[s][e], tab[s][pivot] + tab[pivot][e])
|
|
end
|
|
end
|
|
end
|
|
|
|
-- add node link destinations here (we only need one sample per item)
|
|
for s, t in ipairs(tab) do
|
|
local n1 = nods_reverse[s]
|
|
for e, c in ipairs(t) do
|
|
local n2 = nods_reverse[e]
|
|
|
|
local doit = true
|
|
|
|
if c == infinity then doit = false end
|
|
if doit then
|
|
local n1p = canoplane(nodedests[n1].p)
|
|
local n2p = canoplane(nodedests[n2].p)
|
|
|
|
if n1p == n2p then
|
|
if name == "static_transition" then doit = false end -- ha ha, yep, that's how we find out, tooootally reliable
|
|
if plane_cull[n1p] then doit = false end -- DENIED
|
|
|
|
if doit then
|
|
local xyd = xydist_raw(n1p, nodedests[n1].x, nodedests[n1].y, nodedests[n2].x, nodedests[n2].y)
|
|
|
|
if c >= xyd then doit = false end -- it's faster to just fly directly. this won't fuck with the total-connectedness at all, because if it is faster to just fly directly from cluster A to cluster B, we'll just pick up cluster B when we get there
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
if doit then
|
|
if not n1.links then n1.links = {} end
|
|
if not n2.rlinks then n2.rlinks = {} end
|
|
|
|
--if name == "flightpath" then print("linking from", nodedests[n1].map_desc[1], "to", nodedests[n2].map_desc[1]) end
|
|
tinsert(n1.links, {cost = c, link = n2, outnode_to = nodedests[n2], outnode_from = nodedests[n1]})
|
|
tinsert(n2.rlinks, {cost = c, link = n1, outnode_to = nodedests[n1], outnode_from = nodedests[n2]})
|
|
end
|
|
end
|
|
end
|
|
|
|
QuestHelper:ReleaseTable(nods)
|
|
QuestHelper:ReleaseTable(nods_reverse)
|
|
for _, v in ipairs(tab) do QuestHelper:ReleaseTable(v) end
|
|
QuestHelper:ReleaseTable(tab)
|
|
end
|
|
end
|
|
|
|
for _, v in pairs(titx) do v.n1, v.n2 = nil, nil end
|
|
for _, v in pairs(nodeitems) do QuestHelper:ReleaseTable(v) end
|
|
end
|
|
|
|
prepared = true
|
|
--QuestHelper:TextOut("Graph plane refresh done")
|
|
end
|
|
|
|
|
|
|
|
--[[
|
|
local function QH_Graph_Plane_ReallyMakeLink(item)
|
|
local name, coord1, coord2, cost, cost_reverse = unpack(item)
|
|
|
|
QuestHelper: Assert(not active)
|
|
|
|
--QuestHelper: TextOut(string.format("Link '%s' made from %d/%f/%f to %d/%f/%f of cost %f, asymflag %s", name, coord1.p, coord1.x, coord1.y, coord2.p, coord2.x, coord2.y, cost, tostring(not not asymmetrical)))
|
|
QuestHelper: Assert(name)
|
|
QuestHelper: Assert(coord1)
|
|
QuestHelper: Assert(coord2)
|
|
QuestHelper: Assert(cost)
|
|
|
|
local n1p = canoplane(coord1.p)
|
|
local n2p = canoplane(coord2.p)
|
|
|
|
if n1p == n2p then
|
|
if name == "static_transition" then return end -- ha ha, yep, that's how we find out, tooootally reliable
|
|
|
|
local xyd = xydist_raw(n1p, coord1.x, coord1.y, coord2.x, coord2.y)
|
|
if plane_cull[n1p] then return end -- DENIED
|
|
if cost >= xyd and (not cost_reverse or cost_reverse >= xyd) then
|
|
return -- DENIED
|
|
end
|
|
end
|
|
|
|
local node1 = findnode(coord1)
|
|
local node2 = findnode(coord2)
|
|
|
|
local n1d = {x = coord1.x, y = coord1.y, p = coord1.p, c = coord1.c, map_desc = coord1.map_desc, condense_class = coord1.condense_class}
|
|
local n2d = {x = coord2.x, y = coord2.y, p = coord2.p, c = coord2.c, map_desc = coord2.map_desc, condense_class = coord2.condense_class}
|
|
|
|
if not node1.links then node1.links = {} end
|
|
if not node2.rlinks then node2.rlinks = {} end
|
|
|
|
tinsert(node1.links, {cost = cost, link = node2, outnode_to = n2d, outnode_from = n1d})
|
|
tinsert(node2.rlinks, {cost = cost, link = node1, outnode_to = n1d, outnode_from = n2d})
|
|
|
|
if cost_reverse then
|
|
if not node1.rlinks then node1.rlinks = {} end
|
|
if not node2.links then node2.links = {} end
|
|
|
|
tinsert(node1.rlinks, {cost = cost_reverse, link = node2, outnode_to = n2d, outnode_from = n1d})
|
|
tinsert(node2.links, {cost = cost_reverse, link = node1, outnode_to = n1d, outnode_from = n2d})
|
|
end
|
|
end
|
|
]]
|