231 lines
7.9 KiB
Lua
231 lines
7.9 KiB
Lua
-- Freewood-OctoWoW-Codex: Verkehrsverbindungen (Schiff, Zeppelin, U-Bahn, Portal)
|
|
--
|
|
-- Berechtigte (Modul Freewood-OctoWoW-Dev, Rolle Bearbeiter oder hoeher) erfassen Verbindungen: Start hier, Fahrt, Ziel hier,
|
|
-- die Fahrzeit wird gemessen. Gespeichert wird in db.transport[id] und ueber das normale Teilen als Datensatz "g<ID>"
|
|
-- verschickt; angenommen werden sie nur von Absendern, die selbst berechtigt sind.
|
|
--
|
|
-- db.transport[id] = { k = Art, f = "A" | "H" | nil, n = Name, t = Sekunden, a = { m, x, y }, b = { m, x, y },
|
|
-- two = Rueckfahrt moeglich, del = geloescht, upd = Stand }
|
|
-- Die ID ist ein Pruefwert aus Art, Fraktion und beiden Orten: derselbe Weg, von zwei Berechtigten erfasst, ergibt denselben Datensatz.
|
|
|
|
local ADDON, NS = ...
|
|
|
|
local FW = _G.Freewood
|
|
local Codex = NS and NS.Codex
|
|
if not FW or not Codex then
|
|
return
|
|
end
|
|
|
|
local Transport = { draft = {} }
|
|
Codex.Transport = Transport
|
|
NS.Transport = Transport
|
|
|
|
Transport.KINDS = { "boat", "zeppelin", "subway", "portal" }
|
|
local KIND_OK = {}
|
|
for _, k in ipairs(Transport.KINDS) do KIND_OK[k] = true end
|
|
|
|
local function Now() return _G.time and _G.time() or 0 end
|
|
|
|
local function DB()
|
|
local db = Codex.db
|
|
if not db then return nil end
|
|
db.transport = db.transport or {}
|
|
return db
|
|
end
|
|
|
|
local function CanEdit()
|
|
return Codex.Trust ~= nil and Codex.Trust.CanEdit()
|
|
end
|
|
|
|
local function Round(v, f) return math.floor(v * f + 0.5) end
|
|
|
|
local function Hash(text)
|
|
local h = 5381
|
|
for i = 1, #text do h = (h * 33 + text:byte(i)) % 99999989 end
|
|
return h
|
|
end
|
|
|
|
-- Pruefwert (0..99999988) aus Art, Fraktion und den auf etwa 0,5 % gerundeten Orten
|
|
function Transport.Id(rec)
|
|
local a, b = rec.a or {}, rec.b or {}
|
|
return Hash(table.concat({ tostring(rec.k), tostring(rec.f or "-"), tostring(a.m), Round(a.x or 0, 200), Round(a.y or 0, 200),
|
|
tostring(b.m), Round(b.x or 0, 200), Round(b.y or 0, 200) }, "|"))
|
|
end
|
|
|
|
-- Aktueller Ort { m, x, y } oder nil
|
|
function Transport.Here()
|
|
local loc = FW.Util and FW.Util.GetPlayerLocation and FW.Util.GetPlayerLocation()
|
|
if loc and loc.mapID and loc.x and loc.y then
|
|
return { m = loc.mapID, x = math.floor(loc.x * 10000 + 0.5) / 10000, y = math.floor(loc.y * 10000 + 0.5) / 10000 }
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function Clock() return _G.GetTime and _G.GetTime() or 0 end
|
|
|
|
------------------------------------------------------------------------
|
|
-- Erfassen (Entwurf)
|
|
------------------------------------------------------------------------
|
|
|
|
function Transport.Draft()
|
|
return Transport.draft
|
|
end
|
|
|
|
function Transport.Reset()
|
|
local d = Transport.draft
|
|
d.a, d.b, d.t0, d.t = nil, nil, nil, nil
|
|
FW:Fire("CODEX_TRANSPORT_DRAFT")
|
|
end
|
|
|
|
function Transport.Start()
|
|
local here = Transport.Here()
|
|
if not here then return false end
|
|
local d = Transport.draft
|
|
d.a, d.b, d.t = here, nil, nil
|
|
d.t0 = Clock()
|
|
FW:Fire("CODEX_TRANSPORT_DRAFT")
|
|
return true
|
|
end
|
|
|
|
function Transport.Finish()
|
|
local d = Transport.draft
|
|
if not d.a or not d.t0 then return false end
|
|
local here = Transport.Here()
|
|
if not here then return false end
|
|
d.b = here
|
|
d.t = math.max(1, math.floor(Clock() - d.t0 + 0.5))
|
|
FW:Fire("CODEX_TRANSPORT_DRAFT")
|
|
return true
|
|
end
|
|
|
|
-- Laeuft gerade eine Aufnahme (Start gesetzt, Ziel noch nicht)? Dann zeichnet das Wegenetz nicht mit.
|
|
function Transport.Recording()
|
|
local d = Transport.draft
|
|
return d.a ~= nil and d.b == nil and d.t0 ~= nil
|
|
end
|
|
|
|
function Transport.SetDraft(key, value)
|
|
Transport.draft[key] = value
|
|
end
|
|
|
|
-- Entwurf pruefen. Rueckgabe: true oder false, Grund
|
|
function Transport.CheckDraft()
|
|
local d = Transport.draft
|
|
if not d.a then return false, "start" end
|
|
if not d.b or not d.t then return false, "finish" end
|
|
if not KIND_OK[d.kind or "boat"] then return false, "kind" end
|
|
if d.a.m == d.b.m and math.abs(d.a.x - d.b.x) < 0.005 and math.abs(d.a.y - d.b.y) < 0.005 then return false, "same" end
|
|
return true
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
-- Speichern und Loeschen
|
|
------------------------------------------------------------------------
|
|
|
|
local function Touch(id)
|
|
if Codex.Share and Codex.Share.InvalidateScan then Codex.Share.InvalidateScan() end
|
|
Transport.rev = (Transport.rev or 0) + 1
|
|
FW:Fire("CODEX_TRANSPORT_CHANGED", id)
|
|
FW:Fire("CODEX_CHANGED", "transport", id)
|
|
end
|
|
|
|
local function Log(id, field, old, new)
|
|
if Codex.DevLog then
|
|
pcall(Codex.DevLog, { who = Codex.Trust and Codex.Trust.SelfKey() or "?", kind = "g", id = id, field = field, old = old, new = new })
|
|
end
|
|
end
|
|
|
|
local function Describe(rec)
|
|
local M = Codex.MapName
|
|
return string.format("%s: %s -> %s (%ds)", tostring(rec.k), M and M(rec.a.m) or rec.a.m, M and M(rec.b.m) or rec.b.m, rec.t or 0)
|
|
end
|
|
|
|
-- Speichert den Entwurf. Rueckgabe: id oder nil, Grund
|
|
function Transport.Save()
|
|
if not CanEdit() then return nil, "rights" end
|
|
local ok, why = Transport.CheckDraft()
|
|
if not ok then return nil, why end
|
|
local d = Transport.draft
|
|
local rec = {
|
|
k = d.kind or "boat", f = (d.faction == "A" or d.faction == "H") and d.faction or nil,
|
|
n = type(d.name) == "string" and d.name:sub(1, 40) ~= "" and d.name:sub(1, 40) or nil,
|
|
t = d.t, a = { m = d.a.m, x = d.a.x, y = d.a.y }, b = { m = d.b.m, x = d.b.x, y = d.b.y },
|
|
two = d.two ~= false, upd = Now(),
|
|
}
|
|
local db = DB()
|
|
local id = Transport.Id(rec)
|
|
local old = db.transport[id]
|
|
rec.upd = math.max(rec.upd, (old and old.upd or 0) + 1)
|
|
db.transport[id] = rec
|
|
Log(id, "add", old and not old.del and Describe(old) or nil, Describe(rec))
|
|
Touch(id)
|
|
Transport.Reset()
|
|
return id
|
|
end
|
|
|
|
function Transport.Delete(id)
|
|
if not CanEdit() then return false end
|
|
local db = DB()
|
|
local own = db.transport[id]
|
|
local rec = own
|
|
if not rec then
|
|
for _, r in ipairs(Transport.All(true)) do
|
|
if r.id == id then rec = r break end
|
|
end
|
|
end
|
|
if not rec then return false end
|
|
local del = { k = rec.k, f = rec.f, n = rec.n, t = rec.t, a = rec.a, b = rec.b, two = rec.two, del = true,
|
|
upd = math.max(Now(), (rec.upd or 0) + 1) }
|
|
db.transport[id] = del
|
|
Log(id, "del", Describe(rec), nil)
|
|
Touch(id)
|
|
return true
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
-- Lesen: eigene und empfangene Verbindungen
|
|
------------------------------------------------------------------------
|
|
|
|
-- Liste { id, k, f, n, t, a, b, two, upd }, je ID der neueste Stand; geloeschte nur mit withDeleted
|
|
function Transport.All(withDeleted)
|
|
local best = {}
|
|
local db = DB()
|
|
if not db then return {} end
|
|
for id, rec in pairs(db.transport) do
|
|
if type(rec) == "table" and rec.a and rec.b then best[id] = rec end
|
|
end
|
|
if Codex.Share and Codex.Share.RemoteRecords then
|
|
for _, rec in ipairs(Codex.Share.RemoteRecords("g")) do
|
|
local cur = best[rec.id]
|
|
if not cur or (rec.upd or 0) > (cur.upd or 0) then best[rec.id] = rec end
|
|
end
|
|
end
|
|
local out = {}
|
|
for id, rec in pairs(best) do
|
|
if withDeleted or not rec.del then
|
|
out[#out + 1] = { id = id, k = rec.k, f = rec.f, n = rec.n, t = rec.t, a = rec.a, b = rec.b, two = rec.two ~= false, upd = rec.upd, del = rec.del }
|
|
end
|
|
end
|
|
table.sort(out, function(x, y)
|
|
if x.k ~= y.k then return x.k < y.k end
|
|
return x.id < y.id
|
|
end)
|
|
return out
|
|
end
|
|
|
|
-- Fahrten als gerichtete Kanten { id, k, t, from = { m, x, y }, to = { m, x, y } }; faction = "A" | "H" | nil (alle)
|
|
function Transport.Edges(faction)
|
|
local out = {}
|
|
for _, r in ipairs(Transport.All()) do
|
|
if not (faction and r.f and r.f ~= faction) then
|
|
out[#out + 1] = { id = r.id, k = r.k, t = r.t, from = r.a, to = r.b, name = r.n }
|
|
if r.two then out[#out + 1] = { id = r.id, k = r.k, t = r.t, from = r.b, to = r.a, name = r.n } end
|
|
end
|
|
end
|
|
return out
|
|
end
|
|
|
|
function Transport.Count()
|
|
return #Transport.All()
|
|
end
|