mirror of
https://github.com/Lacustat/Freewood-OctoWoW-Codex.git
synced 2026-11-03 02:56:58 +00:00
0.1.223: Start-Wegenetz aus Karten-Rohdaten (A* auf Raster), Questorte als Rueckfall
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016onJCSHLwhs9udhC9ZLzk9
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
a69ac8131e
commit
c045446264
@@ -4039,8 +4039,19 @@ function Codex.CollectPoints(spec)
|
||||
end
|
||||
elseif kind == "quests" then
|
||||
local q = Codex.V().quests[id]
|
||||
local sg, sturn
|
||||
if Codex.QuestSpot then sg, sturn = Codex.QuestSpot(id) end
|
||||
if not q and (sg or sturn) then
|
||||
for _, def in ipairs({ { sg, L.CODEX_TAG_GIVE, "give" }, { sturn, L.CODEX_TAG_TURN, "turn" } }) do
|
||||
local ref = def[1]
|
||||
if ref then
|
||||
points[#points + 1] = { m = ref.m, x = ref.x, y = ref.y, n = 4, pin = true, tag = def[3],
|
||||
name = string.format(def[2], ref.n or "?") }
|
||||
end
|
||||
end
|
||||
end
|
||||
if q then
|
||||
for _, def in ipairs({ { q.give, L.CODEX_TAG_GIVE, "give" }, { q.turn, L.CODEX_TAG_TURN, "turn" } }) do
|
||||
for _, def in ipairs({ { q.give or sg, L.CODEX_TAG_GIVE, "give" }, { q.turn or sturn, L.CODEX_TAG_TURN, "turn" } }) do
|
||||
local ref = def[1]
|
||||
if ref and ref.m and ref.x and ref.y then
|
||||
points[#points + 1] = { m = ref.m, x = ref.x, y = ref.y, n = 4, pin = true, tag = def[3],
|
||||
|
||||
+1263
File diff suppressed because it is too large
Load Diff
+218
-1
@@ -592,7 +592,7 @@ end
|
||||
-- Netzpunkten), oder nil, wenn dort kein zusammenhaengender Weg bekannt ist (dann rechnet der Plan in Luftlinie).
|
||||
|
||||
local BLOCK = 4
|
||||
local SNAP_BLOCKS = 3 -- so weit (in Bloecken) darf ein Ort vom Netz entfernt sein
|
||||
local SNAP_BLOCKS = 8 -- so weit (in Bloecken zu 40 Yards) darf ein Ort vom Netz entfernt sein (die Strecke dorthin zaehlt als Luftlinie)
|
||||
local MAX_EXPAND = 20000
|
||||
local GRAPH_STALE = 120 -- Sekunden, die ein vorhandener Graph trotz neuer Daten weiterverwendet wird
|
||||
local graphs = {}
|
||||
@@ -750,6 +750,202 @@ if graphFrame then
|
||||
graphFrame:SetScript("OnUpdate", function() Path.StepJobs() end)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Start-Wegenetz: grobes Begehbarkeits-Raster aus den Karten-Rohdaten (Codex.NavSeed, 41,7 Yards je Zelle).
|
||||
-- Die Wegsuche laeuft darauf (A*, 8 Nachbarn); gelaufene Zellen (db.path) gelten als bewiesen und sind etwas billiger.
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local NAV_COST = { [0] = 0, 1.0 / 1.4, 1.0, 1.4, 1.8, 2.2 } -- Kostenfaktor je Klasse (a gesperrt, b Strasse, c normal, d huegelig, e steil, f Wasser)
|
||||
local NAV_WALKED = 0.8
|
||||
local NAV_MAX_EXPAND = 30000
|
||||
local NAV_SNAP = 6 -- Zellen, so weit darf ein Ort von begehbarem Gelaende entfernt sein
|
||||
local navs = {} -- [welt] = { w, h, cell, left, top, cls = { [idx] = klasse }, walked = { [idx] = true }, rev, built, cache = {} }
|
||||
|
||||
local function NavLoad(world)
|
||||
local n = navs[world]
|
||||
if n ~= nil then return n or nil end
|
||||
local src = Codex.NavSeed and Codex.NavSeed[world]
|
||||
if not src then navs[world] = false return nil end
|
||||
local gm = string.gmatch or string.gfind
|
||||
local cls, i = {}, 0
|
||||
for _, row in ipairs(src.rows) do
|
||||
for letter, len in gm(row, "(%a)(%d+)") do
|
||||
local c = string.byte(letter) - 97
|
||||
for _ = 1, tonumber(len) do
|
||||
i = i + 1
|
||||
cls[i] = c
|
||||
end
|
||||
end
|
||||
end
|
||||
n = { w = src.w, h = src.h, cell = src.cell, left = src.left, top = src.top, cls = cls, walked = {}, rev = -1, built = 0, cache = {} }
|
||||
navs[world] = n
|
||||
return n
|
||||
end
|
||||
|
||||
-- gelaufene Zellen auf das Raster legen (hoechstens alle GRAPH_STALE Sekunden neu, wenn es neue Daten gibt)
|
||||
local function NavWalked(n, world)
|
||||
local rev = Path.rev or 0
|
||||
local now = Now()
|
||||
if n.rev == rev or (n.built > 0 and now - n.built < GRAPH_STALE) then return end
|
||||
local cells = Path.Cells(world)
|
||||
local walked = {}
|
||||
local floor = math.floor
|
||||
for key, mask in pairs(cells or {}) do
|
||||
if mask >= 65536 then
|
||||
local cx, cy = floor(key / 65536) - OFFSET, key % 65536 - OFFSET
|
||||
local row, col = floor((n.top - (cx * CELL + CELL / 2)) / n.cell), floor((n.left - (cy * CELL + CELL / 2)) / n.cell)
|
||||
if row >= 0 and row < n.h and col >= 0 and col < n.w then walked[row * n.w + col + 1] = true end
|
||||
end
|
||||
end
|
||||
n.walked, n.rev, n.built, n.cache = walked, rev, now, {}
|
||||
end
|
||||
|
||||
local function NavCell(n, x, y)
|
||||
local row, col = math.floor((n.top - x) / n.cell), math.floor((n.left - y) / n.cell)
|
||||
return row, col
|
||||
end
|
||||
|
||||
-- naechste begehbare Zelle zu (row, col); Rueckgabe: Index, row, col
|
||||
local function NavSnap(n, row, col)
|
||||
local w, h, cls = n.w, n.h, n.cls
|
||||
if row >= 0 and row < h and col >= 0 and col < w and cls[row * w + col + 1] ~= 0 then return row * w + col + 1, row, col end
|
||||
for r = 1, NAV_SNAP do
|
||||
local best, bd, br, bc
|
||||
for dr = -r, r do
|
||||
for dc = -r, r do
|
||||
if math.max(math.abs(dr), math.abs(dc)) == r then
|
||||
local rr, cc = row + dr, col + dc
|
||||
if rr >= 0 and rr < h and cc >= 0 and cc < w then
|
||||
local idx = rr * w + cc + 1
|
||||
if cls[idx] ~= 0 then
|
||||
local d = dr * dr + dc * dc
|
||||
if not bd or d < bd then best, bd, br, bc = idx, d, rr, cc end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if best then return best, br, bc end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local NAV_DIRS = { { 1, 0, 1 }, { -1, 0, 1 }, { 0, 1, 1 }, { 0, -1, 1 }, { 1, 1, 1.4142 }, { 1, -1, 1.4142 }, { -1, 1, 1.4142 }, { -1, -1, 1.4142 } }
|
||||
|
||||
-- A* von Zelle a nach Zelle b. Rueckgabe: Liste von Zellindizes (a ... b) oder nil
|
||||
local function NavSearch(n, a, b)
|
||||
local w, h, cls, walked = n.w, n.h, n.cls, n.walked
|
||||
local bi = b - 1
|
||||
local br, bc = math.floor(bi / w), bi % w
|
||||
local g, prev, closed = { [a] = 0 }, {}, {}
|
||||
local heap, size = {}, 0
|
||||
local function Push(k, f)
|
||||
size = size + 1
|
||||
local i = size
|
||||
heap[i] = { k, f }
|
||||
while i > 1 do
|
||||
local p = math.floor(i / 2)
|
||||
if heap[p][2] <= heap[i][2] then break end
|
||||
heap[p], heap[i] = heap[i], heap[p]
|
||||
i = p
|
||||
end
|
||||
end
|
||||
local function Pop()
|
||||
local top = heap[1]
|
||||
local last = heap[size]
|
||||
heap[size] = nil
|
||||
size = size - 1
|
||||
if size > 0 then
|
||||
heap[1] = last
|
||||
local i = 1
|
||||
while true do
|
||||
local l, r, m = i * 2, i * 2 + 1, i
|
||||
if l <= size and heap[l][2] < heap[m][2] then m = l end
|
||||
if r <= size and heap[r][2] < heap[m][2] then m = r end
|
||||
if m == i then break end
|
||||
heap[m], heap[i] = heap[i], heap[m]
|
||||
i = m
|
||||
end
|
||||
end
|
||||
return top
|
||||
end
|
||||
local function H(r, c) return 0.7 * math.sqrt((r - br) ^ 2 + (c - bc) ^ 2) * 1.15 end
|
||||
local ai = a - 1
|
||||
Push(a, H(math.floor(ai / w), ai % w))
|
||||
local expanded = 0
|
||||
while size > 0 do
|
||||
local top = Pop()
|
||||
local k = top[1]
|
||||
if k == b then
|
||||
local chain, c = {}, k
|
||||
while c do table.insert(chain, 1, c) c = prev[c] end
|
||||
return chain
|
||||
end
|
||||
if not closed[k] then
|
||||
closed[k] = true
|
||||
expanded = expanded + 1
|
||||
if expanded > NAV_MAX_EXPAND then return nil end
|
||||
local ki = k - 1
|
||||
local r, c = math.floor(ki / w), ki % w
|
||||
local gk = g[k]
|
||||
for i = 1, 8 do
|
||||
local d = NAV_DIRS[i]
|
||||
local rr, cc = r + d[1], c + d[2]
|
||||
if rr >= 0 and rr < h and cc >= 0 and cc < w then
|
||||
local nk = rr * w + cc + 1
|
||||
local cl = cls[nk]
|
||||
if cl and cl ~= 0 and not closed[nk] then
|
||||
local f = walked[nk] and NAV_WALKED or NAV_COST[cl]
|
||||
if walked[nk] and cl == 5 then f = NAV_COST[5] end
|
||||
local ng = gk + d[3] * f
|
||||
if not g[nk] or ng < g[nk] then
|
||||
g[nk] = ng
|
||||
prev[nk] = k
|
||||
Push(nk, ng + H(rr, cc))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Weg zwischen zwei Orten (Yards im Zellraum): Zellmitten als { x, y }, Laenge in Yards (ohne Anschluss an die Enden), Entfernung der Enden zum Netz
|
||||
local function NavPath(world, ax, ay, bx, by)
|
||||
local n = NavLoad(world)
|
||||
if not n then return nil end
|
||||
NavWalked(n, world)
|
||||
local ar, ac = NavCell(n, ax, ay)
|
||||
local br, bc = NavCell(n, bx, by)
|
||||
local a, ar2, ac2 = NavSnap(n, ar, ac)
|
||||
local b, br2, bc2 = NavSnap(n, br, bc)
|
||||
if not a or not b then return nil end
|
||||
local ck = a .. ":" .. b
|
||||
local hit = n.cache[ck]
|
||||
local chain
|
||||
if hit ~= nil then
|
||||
chain = hit or nil
|
||||
else
|
||||
chain = a == b and { a } or NavSearch(n, a, b)
|
||||
n.cache[ck] = chain or false
|
||||
end
|
||||
if not chain then return nil end
|
||||
local pts = {}
|
||||
local len = 0
|
||||
for i, k in ipairs(chain) do
|
||||
local ki = k - 1
|
||||
local r, c = math.floor(ki / n.w), ki % n.w
|
||||
pts[i] = { n.top - (r + 0.5) * n.cell, n.left - (c + 0.5) * n.cell }
|
||||
if i > 1 then len = len + math.sqrt((pts[i][1] - pts[i - 1][1]) ^ 2 + (pts[i][2] - pts[i - 1][2]) ^ 2) end
|
||||
end
|
||||
local da = math.sqrt((pts[1][1] - ax) ^ 2 + (pts[1][2] - ay) ^ 2)
|
||||
local db_ = math.sqrt((pts[#pts][1] - bx) ^ 2 + (pts[#pts][2] - by) ^ 2)
|
||||
return pts, len, da, db_
|
||||
end
|
||||
|
||||
function Path.NavReady(world) return NavLoad(world) ~= nil end
|
||||
|
||||
-- Naechster Netzpunkt zu (x, y) in Yards. Rueckgabe: Knotenschluessel, Entfernung in Yards
|
||||
local function Snap(g, x, y)
|
||||
local sk = math.floor(x / 10) * 100000 + math.floor(y / 10)
|
||||
@@ -876,6 +1072,8 @@ function Path.Distance(world, ax, ay, bx, by)
|
||||
if type(world) ~= "number" then return nil end
|
||||
local straight = math.sqrt((ax - bx) ^ 2 + (ay - by) ^ 2)
|
||||
if straight < 2 * BLOCK * CELL then return nil end
|
||||
local pts, len, na, nb = NavPath(world, ax, ay, bx, by)
|
||||
if pts then return na + len + nb end
|
||||
local g = Graph(world)
|
||||
if not g then return nil end
|
||||
local sa, da = Snap(g, ax, ay)
|
||||
@@ -898,6 +1096,25 @@ function Path.Route(world, ax, ay, bx, by, step)
|
||||
if type(world) ~= "number" then return nil end
|
||||
local straight = math.sqrt((ax - bx) ^ 2 + (ay - by) ^ 2)
|
||||
if straight < 2 * BLOCK * CELL then return nil end
|
||||
local pts, len, na, nb = NavPath(world, ax, ay, bx, by)
|
||||
if pts then
|
||||
-- Ecken glaetten (gleitender Mittelwert), dann alle "step" Yards einen Punkt; Ziel zuletzt
|
||||
local sm = {}
|
||||
for i, p in ipairs(pts) do
|
||||
if i == 1 or i == #pts then sm[i] = p
|
||||
else sm[i] = { (pts[i - 1][1] + p[1] + pts[i + 1][1]) / 3, (pts[i - 1][2] + p[2] + pts[i + 1][2]) / 3 } end
|
||||
end
|
||||
step = step or 250
|
||||
local out, last = {}, { ax, ay }
|
||||
for i, p in ipairs(sm) do
|
||||
if math.sqrt((p[1] - last[1]) ^ 2 + (p[2] - last[2]) ^ 2) >= step then
|
||||
out[#out + 1] = p
|
||||
last = p
|
||||
end
|
||||
end
|
||||
out[#out + 1] = { bx, by }
|
||||
return out, na + len + nb
|
||||
end
|
||||
local g = Graph(world)
|
||||
if not g then return nil end
|
||||
local sa = Snap(g, ax, ay)
|
||||
|
||||
+6398
File diff suppressed because it is too large
Load Diff
@@ -706,3 +706,33 @@ function Seed.Count()
|
||||
end
|
||||
return n(S.q), n(S.u), n(S.o), n(S.i)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Orte von Geber und Abgabe aus der mitgelieferten Liste (Codex.QuestSeed, nur Rueckfall: eigene Daten gehen vor)
|
||||
------------------------------------------------------------------------
|
||||
|
||||
local spotCache = {}
|
||||
local function SpotRef(a, b)
|
||||
local m, x, y = tonumber(a[2]), tonumber(a[3]), tonumber(a[4])
|
||||
if not m or not x or not y then return nil end
|
||||
return { n = (a[1] ~= "" and a[1]) or nil, m = m, x = x / 1000, y = y / 1000, seed = true }
|
||||
end
|
||||
|
||||
-- Rueckgabe: Geber, Abgabe (je { n, m, x, y } oder nil)
|
||||
function Codex.QuestSpot(id)
|
||||
local c = spotCache[id]
|
||||
if c == nil then
|
||||
local raw = Codex.QuestSeed and Codex.QuestSeed[id]
|
||||
if raw then
|
||||
local f = {}
|
||||
for part in string.gfind and string.gfind(raw .. "|", "([^|]*)|") or string.gmatch(raw .. "|", "([^|]*)|") do f[#f + 1] = part end
|
||||
c = { SpotRef({ f[1], f[2], f[3], f[4] }, nil), SpotRef({ f[5], f[6], f[7], f[8] }, nil) }
|
||||
c.n = true
|
||||
else
|
||||
c = false
|
||||
end
|
||||
spotCache[id] = c
|
||||
end
|
||||
if not c then return nil, nil end
|
||||
return c[1], c[2]
|
||||
end
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## Notes: Learns quests, creatures and drops while you play: who gives and takes a quest, rewards, objectives, drop chances.
|
||||
## Notes-deDE: Lernt beim Spielen Quests, Kreaturen und Beute: wer eine Quest vergibt und annimmt, Belohnungen, Ziele, Drop-Chancen.
|
||||
## Author: Lacustat
|
||||
## Version: 0.1.222
|
||||
## Version: 0.1.223
|
||||
## Dependencies: Freewood-OctoWoW-Core
|
||||
## SavedVariablesPerCharacter: FreewoodSnapshot
|
||||
## OptionalDeps: Freewood-OctoWoW-CodexData, Freewood-OctoWoW-ForeverData
|
||||
@@ -25,6 +25,8 @@ CodexMapAPI.lua
|
||||
CodexPathMap.lua
|
||||
CodexTaxi.lua
|
||||
CodexTaxiSeed.lua
|
||||
CodexNavSeed.lua
|
||||
CodexQuestSeed.lua
|
||||
CodexTaxiUI.lua
|
||||
CodexTransport.lua
|
||||
CodexSpots.lua
|
||||
|
||||
Reference in New Issue
Block a user