Files
fs25-adsmartpickup/FS25_ADSmartPickup/adChainView.lua
T

188 lines
8.9 KiB
Lua

-- AD Chain View (ren logik)
-- Rækkerne til fanen Kæder ud fra ADChain.last: rod -> noder rekursivt (kilde, behov pr. måned, flowstatus),
-- forslag/auto-skift under afgrøden, og nederst frie marker + antal automatiske skift. Ingen spiladgang:
-- titleOf(navn) -> visningstitel, fmt(liter) -> kompakt tal, textOf(l10n-nøgle) -> tekst gives udefra.
-- Række: {kind="root"|"node"|"suggest"|"dismiss"|"auto"|"info"|"empty", indent, text, level, cells={c1..c5},
-- action={kind="approve"|"dismiss"|"pause"|"resume", fieldId|rootKey}, fillType}
ADChainView = {}
ADChainView.INDENT = " "
-- nøgler ud over spu_tab_chains/spu_chain_empty/... som rækkerne bruger (til l10n-testen)
ADChainView.TEXT_KEYS = {"spu_chain_perMonth", "spu_chain_noField", "spu_chain_buySource", "spu_chain_cycle",
"spu_chain_fieldShort", "spu_chain_flowOk", "spu_chain_flowNone", "spu_chain_flowStopped", "spu_chain_approve",
"spu_chain_dismiss", "spu_chain_switches", "spu_chain_rootOk", "spu_chain_rootError", "spu_chain_rootWaiting",
"spu_chain_rootPaused"}
local FLOW_KEYS = {ok = "spu_chain_flowOk", missing = "spu_chain_flowNone", stopped = "spu_chain_flowStopped", noVehicle = "spu_chain_flowMissing"}
local function identity(value) return tostring(value) end
local function plannedFruit(field)
if field.targetFruit ~= nil and field.targetFruit ~= "" then return field.targetFruit end
return field.fruit
end
local function haText(ha)
return string.format("%.0f", ha or 0)
end
local function edgeKey(node)
return tostring(node.consumer) .. "|" .. tostring(node.fillType)
end
local function shortageByFruit(last)
local result = {}
for _, shortage in ipairs(last.shortages or {}) do result[shortage.fruit] = shortage end
return result
end
-- Alle noder under roden (dybde først).
local function eachNode(nodes, fn, depth)
for _, node in ipairs(nodes or {}) do
fn(node, depth or 1)
eachNode(node.children, fn, (depth or 1) + 1)
end
end
function ADChainView.rootLevel(root, last)
last = last or {}
if (last.paused or {})[root.key] then return "stopped" end
for _, problem in ipairs((last.chain or {}).problems or {}) do
if problem.rootKey == root.key then return "error" end
end
local short, flowStatus = shortageByFruit(last), last.flowStatus or {}
local hasShortage, hasWaiting = false, false
eachNode(root.children, function(node)
if node.fruit ~= nil and short[node.fruit] ~= nil then hasShortage = true end
if (node.shortPerMonth or 0) > 0 then hasShortage = true end
local status = node.consumer ~= nil and flowStatus[edgeKey(node)] or nil
if status ~= nil and status ~= "ok" then hasWaiting = true end
end)
if hasShortage then return "error" end
if hasWaiting then return "waiting" end
return "running"
end
-- Kilden som tekst: fabrikker / stalde / marker med planlagt afgrøde / køb.
local function sourceText(node, last, textOf)
local names = {}
if node.source == "production" then
for _, producer in ipairs(node.producers or {}) do table.insert(names, tostring(producer.buildingName)) end
elseif node.source == "husbandry" then
for _, herd in ipairs(node.herds or {}) do table.insert(names, tostring(herd.buildingName)) end
elseif node.source == "field" then
for _, field in ipairs(last.fields or {}) do
if plannedFruit(field) == node.fruit then
table.insert(names, string.format("F%s (%s ha)", tostring(field.id), haText(field.areaHa)))
end
end
if #names == 0 then return textOf("spu_chain_noField") end
elseif node.source == "buy" then
return textOf("spu_chain_buySource")
else
return "-"
end
return table.concat(names, ", ")
end
-- Nodens status (tekst, niveau): mangel/flaskehals/køb/kredsløb først, ellers flowets status.
local function nodeStatus(node, last, titleOf, fmt, textOf, short)
local title = titleOf(node.fillType)
if node.source == "cycle" then return textOf("spu_chain_cycle"), "error" end
if (node.shortPerMonth or 0) > 0 then
if node.source == "production" then
return string.format(textOf("spu_chain_bottleneck"), title, fmt(node.shortPerMonth)), "error"
end
return string.format(textOf("spu_chain_buy"), title, fmt(node.shortPerMonth)), "error"
end
local shortage = node.fruit ~= nil and short[node.fruit] or nil
if shortage ~= nil then
return string.format(textOf("spu_chain_fieldShort"), haText(shortage.missingHa)), "error"
end
local status = node.consumer ~= nil and (last.flowStatus or {})[edgeKey(node)] or nil
if status == nil then return "", "running" end
return textOf(FLOW_KEYS[status] or "spu_chain_flowNone"), status == "ok" and "running" or "waiting"
end
local function joinCells(cells)
local parts = {}
for _, cell in ipairs(cells) do
if cell ~= nil and cell ~= "" then table.insert(parts, cell) end
end
return table.concat(parts, " · ")
end
local function makeRow(kind, indent, level, cells, action)
return {kind = kind, indent = indent, level = level, cells = cells, action = action, text = joinCells(cells)}
end
-- Forslag (godkend + afvis) og auto-skift for afgrøden, lige under dens node.
local function actionRows(rows, fruit, indent, last, titleOf, textOf, shown)
if fruit == nil or shown[fruit] then return end
shown[fruit] = true
local pad = string.rep(ADChainView.INDENT, indent)
for _, action in ipairs(last.actions or {}) do
if action.to == fruit then
local fromTitle = (action.from ~= nil and action.from ~= "") and titleOf(action.from) or "-"
if action.mode == "suggest" then
local text = string.format(textOf("spu_chain_suggest"), action.fieldId, fromTitle, titleOf(action.to), action.hectares or 0)
table.insert(rows, makeRow("suggest", indent, "waiting", {pad, "", "", text, textOf("spu_chain_approve")},
{kind = "approve", fieldId = action.fieldId}))
table.insert(rows, makeRow("dismiss", indent, "waiting", {pad, "", "", "", textOf("spu_chain_dismiss")},
{kind = "dismiss", fieldId = action.fieldId}))
else
local text = string.format(textOf("spu_chain_auto"), action.fieldId, titleOf(action.to))
table.insert(rows, makeRow("auto", indent, "running", {pad, "", "", text, ""}, nil))
end
end
end
end
local ROOT_STATUS = {running = "spu_chain_rootOk", error = "spu_chain_rootError", waiting = "spu_chain_rootWaiting", stopped = "spu_chain_rootPaused"}
local function rootRows(rows, root, last, titleOf, fmt, textOf, short)
local level = ADChainView.rootLevel(root, last)
local paused = (last.paused or {})[root.key] == true
table.insert(rows, makeRow("root", 0, level, {titleOf(root.name), tostring(root.buildingName or ""), "",
textOf(ROOT_STATUS[level]), textOf(paused and "spu_chain_resume" or "spu_chain_pause")},
{kind = paused and "resume" or "pause", rootKey = root.key}))
local shown = {}
eachNode(root.children, function(node, depth)
local status, nodeLevel = nodeStatus(node, last, titleOf, fmt, textOf, short)
local row = makeRow("node", depth, nodeLevel, {string.rep(ADChainView.INDENT, depth) .. titleOf(node.fillType),
sourceText(node, last, textOf), string.format(textOf("spu_chain_perMonth"), fmt(node.perMonth or 0)), status, ""}, nil)
row.fillType = node.fillType
table.insert(rows, row)
if node.source == "field" then actionRows(rows, node.fruit, depth + 1, last, titleOf, textOf, shown) end
end)
end
local function footerRows(rows, last, textOf)
local free = {}
for _, field in ipairs(last.fields or {}) do
if field.chainFree then table.insert(free, field.id) end
end
table.sort(free)
local names = {}
for _, id in ipairs(free) do table.insert(names, "F" .. tostring(id)) end
local freeText = string.format(textOf("spu_chain_free"), #names > 0 and table.concat(names, ", ") or "-")
table.insert(rows, {kind = "info", indent = 0, text = freeText})
table.insert(rows, {kind = "info", indent = 0, text = string.format(textOf("spu_chain_switches"), last.switchCount or 0)})
end
function ADChainView.rows(last, titleOf, fmt, textOf)
textOf, fmt = textOf or identity, fmt or identity
local titles = last ~= nil and last.titles or {}
titleOf = titleOf or function(name) return (titles or {})[name] or tostring(name) end
local rows = {}
local roots = last ~= nil and last.chain ~= nil and last.chain.roots or {}
if #roots == 0 then
table.insert(rows, {kind = "empty", indent = 0, text = textOf("spu_chain_empty")})
end
if last == nil then return rows end
local short = shortageByFruit(last)
for _, root in ipairs(roots) do rootRows(rows, root, last, titleOf, fmt, textOf, short) end
footerRows(rows, last, textOf)
return rows
end