Files
fs25-adsmartpickup/FS25_ADSmartPickup/adFlowView.lua
T
masterdraco 76f51c7be7 fix(flows): udkørselsregler vises kun når en vare bruger dem
Rent lager-flow viste 'sælg fra 90 %' / 'pris >= 90 %' selv om det aldrig
sælger. Guidens Regler-trin, resuméet og statuslinjen viser nu kun
salgsgrænser ved salg og lagergrænsen ved lager (v1.60.11.0).
2026-09-29 21:02:10 +02:00

200 lines
7.5 KiB
Lua

-- AD Flow View
-- Rene regler for Flows-fanens oprydning (ingen spil-API): tavlens grupper og rækkefølge, hvilke
-- traktorer og bygninger der vises som standard, og hvad "Vis alle" tilføjer.
ADFlowView = {}
-- Naturlig sortering: tal i navne sammenlignes som tal ("Biogas 9" < "Biogas 10").
local function naturalKey(text)
local parts = {}
local value = string.lower(tostring(text or ""))
local position = 1
while position <= #value do
local digits = string.match(value, "^%d+", position)
if digits ~= nil then
table.insert(parts, {number = tonumber(digits)})
position = position + #digits
else
local chunk = string.match(value, "^%D+", position)
table.insert(parts, {text = chunk})
position = position + #chunk
end
end
return parts
end
function ADFlowView.naturalLess(a, b)
local ka, kb = naturalKey(a), naturalKey(b)
for index = 1, math.max(#ka, #kb) do
local pa, pb = ka[index], kb[index]
if pa == nil then return true end
if pb == nil then return false end
if pa.number ~= nil and pb.number ~= nil then
if pa.number ~= pb.number then return pa.number < pb.number end
elseif pa.number ~= nil or pb.number ~= nil then
return pa.number ~= nil
elseif pa.text ~= pb.text then
return pa.text < pb.text
end
end
return false
end
local function sortedBy(list, field)
local copy = {}
for _, item in ipairs(list or {}) do table.insert(copy, item) end
table.sort(copy, function(a, b)
if a[field] ~= b[field] then return ADFlowView.naturalLess(a[field], b[field]) end
return tostring(a.id) < tostring(b.id)
end)
return copy
end
-- Tavlen: forsyning ("Ind til bygning") først, så udkørsel ("Ud fra bygning"); hver gruppe efter navn.
-- {{kind, runs}, ...}; tomme grupper udelades.
function ADFlowView.boardGroups(runs)
local byKind = {supply = {}, outbound = {}}
for _, run in ipairs(runs or {}) do
table.insert(byKind[run.kind == "outbound" and "outbound" or "supply"], run)
end
local groups = {}
for _, kind in ipairs({"supply", "outbound"}) do
if #byKind[kind] > 0 then
table.insert(groups, {kind = kind, runs = sortedBy(byKind[kind], "name")})
end
end
return groups
end
-- Samme rækkefølge som tavlen, som én liste (flow-vælgeren).
function ADFlowView.orderedRuns(runs)
local result = {}
for _, group in ipairs(ADFlowView.boardGroups(runs)) do
for _, run in ipairs(group.runs) do table.insert(result, run) end
end
return result
end
-- Traktorer: vehicles = {{id, label, state = "in"|"free"|"other"|"blocked"}}. Standard: flowets egne,
-- så ledige der kan bruges. showAll tilføjer traktorer i andre flows og låste (vogn passer ikke).
local STATE_ORDER = {"in", "free", "other", "blocked"}
function ADFlowView.vehicleRows(vehicles, showAll)
local byState = {}
for _, entry in ipairs(vehicles or {}) do
byState[entry.state] = byState[entry.state] or {}
table.insert(byState[entry.state], entry)
end
local rows = {}
for _, state in ipairs(STATE_ORDER) do
if showAll or state == "in" or state == "free" then
for _, entry in ipairs(sortedBy(byState[state], "label")) do table.insert(rows, entry) end
end
end
return rows
end
function ADFlowView.hiddenCount(all, shown)
return #(all or {}) - #(shown or {})
end
-- Bygninger: buildings = {{id, name, hasMarker}}, chosen = {[id] = true}. Valgte først; bygninger uden
-- markør kun hvis valgte eller showAll (de kan alligevel ikke bruges før en markør er sat).
function ADFlowView.buildingRows(buildings, chosen, showAll)
local shown, withoutMarker = {}, {}
for _, building in ipairs(buildings or {}) do
if (chosen ~= nil and chosen[building.id]) or building.hasMarker then
table.insert(shown, building)
elseif showAll then
table.insert(withoutMarker, building)
end
end
-- fast rækkefølge: bygningsnavn, så markørnavn (label) — et tryk må ikke flytte rækken under markøren
local function byNameThenLabel(list)
local copy = {}
for _, item in ipairs(list) do table.insert(copy, item) end
table.sort(copy, function(a, b)
if a.name ~= b.name then return ADFlowView.naturalLess(a.name, b.name) end
if (a.label or "") ~= (b.label or "") then return ADFlowView.naturalLess(a.label or "", b.label or "") end
return tostring(a.id) < tostring(b.id)
end)
return copy
end
local rows = {}
for _, list in ipairs({byNameThenLabel(shown), byNameThenLabel(withoutMarker)}) do
for _, building in ipairs(list) do table.insert(rows, building) end
end
return rows
end
-- "Chicken Coop x3 + Cow Barn": ens navne slås sammen med antal, i rækkefølge efter første forekomst.
function ADFlowView.joinCounted(names, separator, empty)
local order, counts = {}, {}
for _, name in ipairs(names or {}) do
local key = tostring(name)
if counts[key] == nil then
counts[key] = 0
table.insert(order, key)
end
counts[key] = counts[key] + 1
end
if #order == 0 then
return empty
end
local parts = {}
for _, key in ipairs(order) do
table.insert(parts, counts[key] > 1 and string.format("%s x%d", key, counts[key]) or key)
end
return table.concat(parts, separator)
end
-- Lang tekst -> linjer på højst maxChars (ombrydes ved mellemrum; indrykning gentages på hver linje,
-- så en indrykket forklaring forbliver indrykket). Ét ord længere end linjen deles ikke.
function ADFlowView.wrapText(text, maxChars)
text = tostring(text or "")
if #text <= maxChars then return {text} end
local indent = string.match(text, "^%s*") or ""
local lines, line = {}, indent
for word in string.gmatch(string.sub(text, #indent + 1), "%S+") do
if line ~= indent and #line + 1 + #word > maxChars then
table.insert(lines, line)
line = indent .. word
else
line = line == indent and (indent .. word) or (line .. " " .. word)
end
end
if line ~= indent then table.insert(lines, line) end
return lines
end
-- ---- status pr. flow (Flows-listen) ----
ADFlowView.LEVEL_COLORS = {
running = {0.30, 0.75, 0.35, 1},
waiting = {0.95, 0.80, 0.25, 1},
error = {0.90, 0.35, 0.30, 1},
stopped = {0.55, 0.55, 0.55, 1},
}
-- s = {running, total, stopped, canStop, warning, reason, lastStartFailed} -> "running"|"waiting"|"error"|"stopped"
-- Hvilke udkørselsregler betyder noget for varernes politik? policies = {"store"|"sell"|"sellElseStore"|"off"}
-- -> sells, stores. Ingen varer med salg/lager endnu = begge (reglerne kan sættes før varerne vælges).
function ADFlowView.policyUse(policies)
local sells, stores = false, false
for _, policy in ipairs(policies or {}) do
if policy == "sell" or policy == "sellElseStore" then sells = true end
if policy == "store" or policy == "sellElseStore" then stores = true end
end
if not sells and not stores then return true, true end
return sells, stores
end
function ADFlowView.flowStatus(s)
if s.stopped then return "stopped" end
if s.warning ~= nil then return "error" end
if (s.running or 0) == 0 and s.lastStartFailed then return "error" end
if (s.running or 0) > 0 and s.reason == nil then return "running" end
if (s.running or 0) > 0 or s.canStop then return "waiting" end
return "stopped"
end