feat(kæde): ét flow pr. bygning, slutprodukter sælges straks (Sælg/Behold), tomme kædeflows foldes sammen; såmaskine ikke gødningsspreder (v1.61.4.0)

This commit is contained in:
2026-09-30 17:44:06 +02:00
parent d2970a1bda
commit 74537f7ec2
13 changed files with 297 additions and 40 deletions
+1
View File
@@ -8,6 +8,7 @@ står i [docs/BRUGERMANUAL.md](docs/BRUGERMANUAL.md) (dansk) og [docs/USER_MANUA
## 1.61 — Produktionskæder
- **1.61.4** Kæder mere realistiske: **ét kædeflow pr. bygning** med alle bygningens manglende varer (før ét pr. vare og bygning); nye varer lægges i bygningens eksisterende kædeflow, og tidligere tomme kædeflows (uden traktor) til samme bygning lægges sammen. **Slutprodukter sælges straks** som standard (fabrikkens udgang sættes til *Sælg direkte*); feltet *Sælg*/*Behold* på kædelinjen skifter, og *Behold* sætter varer, kæden selv satte til salg, tilbage. I Flows-listen foldes kædeflows uden traktor sammen i én linje. En såmaskine med gødningstank (fx Kinze 4905) bruges kun til såning, ikke som gødningsspreder.
- **1.61.3** Fanen **Kæder** sorteret: forslag til ny afgrøde står samlet øverst, én gang pr. mark og efter marknummer (før gentaget under hver kæde, der bruger afgrøden); kæderne står efter status (mangel/fejl, venter, dækket), så bygning og produkt. Kædelinjen viser stadig "· N forslag".
- **1.61.2** Flows-listen: dine egne flows står øverst; kædernes auto-oprettede flows samles nederst under *Kædernes egne flows* og vises uden "Kæde: ", så bygningen kan ses.
- **1.61.1** Fanen **Kæder** står nu efter **Marker**, og navne i kædelisten afkortes ikke længere (fx "+ Fermented Chaff", "+ Whole Crop Silage").
+28 -5
View File
@@ -17,7 +17,7 @@ ADChain.LOG_PREFIX = "ADSmartPickup: kæde: "
-- Et afvist forslag vises igen, når manglen for afgrøden har ændret sig mere end dette.
ADChain.DISMISS_CHANGE = 0.25
ADChain.state = {byChain = {}, paused = {}, dismissed = {}, userOff = {}}
ADChain.state = {byChain = {}, paused = {}, dismissed = {}, userOff = {}, finalKeep = {}, soldByChain = {}}
ADChain.last = nil
-- {[nøgle] = true}: produktioner (ikke kædens) der var tændt ved sidste tick — til at se spillerens slukninger
ADChain.prevEnabled = nil
@@ -53,12 +53,14 @@ end
-- userOff = {[recipeKey] = true}: produktioner spilleren slukkede selv — kæden tænder dem ikke igen.
-- roots = {[recipeKey] = true} rødderne ved sidste tick (nil = ukendt, fx gem fra en ældre version).
function ADChain.newState()
return {byChain = {}, paused = {}, dismissed = {}, userOff = {}, roots = nil}
-- finalKeep = {[rodnøgle] = true}: slutproduktet beholdes (standard: sælg straks); soldByChain = {["rod|vare"] = true}
return {byChain = {}, paused = {}, dismissed = {}, userOff = {}, roots = nil, finalKeep = {}, soldByChain = {}}
end
function ADChain.stateToRecords(state)
local records = {byChain = sortedKeys(state.byChain), paused = sortedKeys(state.paused), userOff = sortedKeys(state.userOff), dismissed = {},
roots = state.roots ~= nil and sortedKeys(state.roots) or nil}
roots = state.roots ~= nil and sortedKeys(state.roots) or nil,
finalKeep = sortedKeys(state.finalKeep or {}), soldByChain = sortedKeys(state.soldByChain or {})}
local ids = {}
for id in pairs(state.dismissed or {}) do table.insert(ids, id) end
table.sort(ids)
@@ -75,6 +77,8 @@ function ADChain.stateFromRecords(records)
for _, key in ipairs(records.byChain or {}) do state.byChain[key] = true end
for _, key in ipairs(records.paused or {}) do state.paused[key] = true end
for _, key in ipairs(records.userOff or {}) do state.userOff[key] = true end
for _, key in ipairs(records.finalKeep or {}) do state.finalKeep[key] = true end
for _, key in ipairs(records.soldByChain or {}) do state.soldByChain[key] = true end
if records.roots ~= nil then
state.roots = {}
for _, key in ipairs(records.roots) do state.roots[key] = true end
@@ -378,7 +382,7 @@ function ADChain.evaluate(adEnv, act)
for key in pairs(state.paused) do paused[key] = true end
ADChain.last = {chain = result.chain, shortages = result.shortages, actions = result.actions, edges = result.edges,
missingFlows = ADChainActions.missingFlows(result.edges, runs), flowStatus = ADChain.flowStatus(result.edges, runs, ADChain.noVehicle),
paused = paused, userOff = copySet(state.userOff), data = data, fields = data.fields, fieldsIncomplete = data.fieldsIncomplete == true, titles = data.fillTitles, at = ADChain.now, switchCount = ADChain.switchCount,
paused = paused, userOff = copySet(state.userOff), finalKeep = copySet(state.finalKeep or {}), data = data, fields = data.fields, fieldsIncomplete = data.fieldsIncomplete == true, titles = data.fillTitles, at = ADChain.now, switchCount = ADChain.switchCount,
dependency = dependency}
return ADChain.last
end
@@ -491,6 +495,13 @@ function ADChain.dismiss(fieldId)
return true
end
-- Slutprodukt for en kæde: keep = true -> "Behold", ellers "Sælg straks" (standard). Anvendes ved næste tick.
function ADChain.setFinalKeep(rootKey, keep)
if rootKey == nil then return end
ADChain.state.finalKeep[rootKey] = keep and true or nil
Logging.info(ADChain.LOG_PREFIX .. "%s: slutprodukt %s", tostring(rootKey), keep and "beholdes" or "sælges straks")
end
function ADChain.setPaused(rootKey, isPaused)
if rootKey == nil then return end
ADChain.state.paused[rootKey] = isPaused and true or nil
@@ -520,6 +531,12 @@ function ADChain.save()
for index, key in ipairs(records.userOff) do
xmlFile:setString(string.format("%s.userOff.recipe(%d)#key", ADChain.ROOT, index - 1), key)
end
for index, key in ipairs(records.finalKeep) do
xmlFile:setString(string.format("%s.finalKeep.root(%d)#key", ADChain.ROOT, index - 1), key)
end
for index, key in ipairs(records.soldByChain) do
xmlFile:setString(string.format("%s.soldByChain.output(%d)#key", ADChain.ROOT, index - 1), key)
end
if records.roots ~= nil then
xmlFile:setBool(ADChain.ROOT .. ".roots#known", true)
for index, key in ipairs(records.roots) do
@@ -547,7 +564,13 @@ function ADChain.load()
if path == nil or not fileExists(path) then return end
local xmlFile = XMLFile.load("ADSmartPickupChain", path)
if xmlFile == nil then return end
local records = {byChain = {}, paused = {}, userOff = {}, dismissed = {}}
local records = {byChain = {}, paused = {}, userOff = {}, dismissed = {}, finalKeep = {}, soldByChain = {}}
xmlFile:iterate(ADChain.ROOT .. ".finalKeep.root", function(_, key)
table.insert(records.finalKeep, xmlFile:getString(key .. "#key"))
end)
xmlFile:iterate(ADChain.ROOT .. ".soldByChain.output", function(_, key)
table.insert(records.soldByChain, xmlFile:getString(key .. "#key"))
end)
xmlFile:iterate(ADChain.ROOT .. ".byChain.recipe", function(_, key)
table.insert(records.byChain, xmlFile:getString(key .. "#key"))
end)
+77
View File
@@ -65,3 +65,80 @@ function ADChainActions.flowsToStop(runs, activeRootKeys)
end
return list
end
-- ---- ét kædeflow pr. bygning (in-game 30/9: ét flow pr. vare og bygning gav mange stoppede flows) ----
-- Manglende kanter samlet pr. modtagerbygning: {consumer, consumerName, rootKey, fillTypes} i fundet rækkefølge.
function ADChainActions.groupMissing(missing)
local groups, byConsumer = {}, {}
for _, edge in ipairs(missing or {}) do
local group = byConsumer[edge.consumer]
if group == nil then
group = {consumer = edge.consumer, consumerName = edge.consumerName, rootKey = edge.rootKey, fillTypes = {}}
byConsumer[edge.consumer] = group
table.insert(groups, group)
end
if not contains(group.fillTypes, edge.fillType) then table.insert(group.fillTypes, edge.fillType) end
end
return groups
end
local function isChainRun(run)
return run.chain ~= nil and run.chain ~= "" and run.kind == "supply" and run.ingredientMode == "manual"
end
-- Kædens eget flow (manuelle varer) til bygningen -> run-id, så nye varer lægges i det i stedet for et nyt flow.
function ADChainActions.chainRunFor(runs, consumer)
for _, run in ipairs(runs or {}) do
if isChainRun(run) and contains(run.targetBuildingIds, consumer) then return run.id end
end
return nil
end
-- Flere tomme kædeflows (uden traktorer) til samme bygning: det første beholdes med alle varerne, resten slettes.
-- -> {{keepId, add = {varer}, deleteIds = {id}}}
function ADChainActions.mergeIdle(runs)
local byConsumer, order = {}, {}
for _, run in ipairs(runs or {}) do
local target = (run.targetBuildingIds or {})[1]
if isChainRun(run) and target ~= nil and #(run.targetBuildingIds or {}) == 1 and #(run.vehicleIds or {}) == 0 then
if byConsumer[target] == nil then byConsumer[target] = {}; table.insert(order, target) end
table.insert(byConsumer[target], run)
end
end
local merges = {}
for _, target in ipairs(order) do
local list = byConsumer[target]
if #list > 1 then
local keep, add, deleteIds = list[1], {}, {}
for index = 2, #list do
for _, name in ipairs(list[index].ingredients or {}) do
if not contains(keep.ingredients, name) and not contains(add, name) then table.insert(add, name) end
end
table.insert(deleteIds, list[index].id)
end
table.insert(merges, {keepId = keep.id, add = add, deleteIds = deleteIds})
end
end
return merges
end
-- Slutprodukter der skal sælges straks: aktive rødders udgange, som ikke selv er en kant i kæden (mellemvare).
-- keep = {[rodnøgle] = true} for rødder spilleren har sat til "Behold". -> {{key, fillType}}
function ADChainActions.finalSales(chain, recipes, edges, keep)
local intermediate, byKey, list = {}, {}, {}
for _, edge in ipairs(edges or {}) do intermediate[edge.fillType] = true end
for _, recipe in ipairs(recipes or {}) do byKey[recipe.key] = recipe end
for _, root in ipairs((chain or {}).roots or {}) do
local recipe = byKey[root.key]
if recipe ~= nil and not (keep or {})[root.key] then
local names = {}
for name in pairs(recipe.outputs or {}) do table.insert(names, name) end
table.sort(names)
for _, name in ipairs(names) do
if not intermediate[name] then table.insert(list, {key = root.key, fillType = name}) end
end
end
end
return list
end
+138 -25
View File
@@ -125,26 +125,15 @@ local function createStopped(run, kind, reason, ...)
log("flow '%s' oprettet stoppet: " .. reason, tostring(run.name), ...)
end
local function createFlow(adEnv, data, edge, pool, used)
local title = data.fillTitles[edge.fillType] or edge.fillType
local created = ADRunsController.createRun(ADChain.flowName(title, edge.consumerName or edge.consumer), "supply")
local runId = created.id
ADRunsController.updateRun(runId, {targetBuildingIds = {edge.consumer}, ingredientMode = "manual", ingredients = {edge.fillType},
waitPoolGroup = ADChain.waitPoolFor(ADRunsController.store.runs, edge.consumer), chain = edge.rootKey})
-- updateRun erstatter store'en: slå altid run'et op igen
local run = ADRuns.getRun(ADRunsController.store, runId)
local fillIndex = data.fillIndex[edge.fillType]
if fillIndex ~= nil and ADSources.isPalletFillType(fillIndex) then
createStopped(run, "pallet", "palle-vare: vælg vogn med Universal Autoload (%s)", tostring(title))
return
end
if pool.implements == nil then pool.implements, pool.tractors = collectPool(adEnv) end
if pool.blocked == nil then pool.blocked = ADPool.blocked(ADFieldJobs.now or 0) end
local pair = ADChainRun.choosePair(pool.implements, pool.tractors, fillIndex, used, pool.blocked)
if pair == nil then
createStopped(run, "noVehicle", "ingen ledig traktor/vogn til %s", tostring(title))
return
end
-- Ét flow pr. modtagerbygning med alle dens manglende bulkvarer (manuelle varer); palle-varer får hver sit
-- stoppede flow. group = {consumer, consumerName, rootKey, fillTypes}.
local function flowTitle(data, fillTypes)
local first = data.fillTitles[fillTypes[1]] or fillTypes[1]
if #fillTypes == 1 then return first end
return string.format("%s +%d", tostring(first), #fillTypes - 1)
end
local function startWithPair(adEnv, run, runId, pair, used)
used[pair.tractor.id], used[pair.implement.id] = true, true
ADRunsController.assignVehicle(runId, pair.tractor.vehicle)
run = ADRuns.getRun(ADRunsController.store, runId)
@@ -159,18 +148,137 @@ local function createFlow(adEnv, data, edge, pool, used)
tostring(pair.implement.name), started)
end
local function createGroupFlow(adEnv, data, group, fillTypes, pool, used)
local title = flowTitle(data, fillTypes)
local created = ADRunsController.createRun(ADChain.flowName(title, group.consumerName or group.consumer), "supply")
local runId = created.id
ADRunsController.updateRun(runId, {targetBuildingIds = {group.consumer}, ingredientMode = "manual", ingredients = fillTypes,
waitPoolGroup = ADChain.waitPoolFor(ADRunsController.store.runs, group.consumer), chain = group.rootKey})
-- updateRun erstatter store'en: slå altid run'et op igen
local run = ADRuns.getRun(ADRunsController.store, runId)
local fillIndex = data.fillIndex[fillTypes[1]]
if fillIndex ~= nil and ADSources.isPalletFillType(fillIndex) then
createStopped(run, "pallet", "palle-vare: vælg vogn med Universal Autoload (%s)", tostring(title))
return
end
if pool.implements == nil then pool.implements, pool.tractors = collectPool(adEnv) end
if pool.blocked == nil then pool.blocked = ADPool.blocked(ADFieldJobs.now or 0) end
local pair = ADChainRun.choosePair(pool.implements, pool.tractors, fillIndex, used, pool.blocked)
if pair == nil then
createStopped(run, "noVehicle", "ingen ledig traktor/vogn til %s", tostring(title))
return
end
startWithPair(adEnv, run, runId, pair, used)
end
-- Nye varer til en bygning, der allerede har et kædeflow: lægges i det flow.
local function extendFlow(runId, fillTypes)
local run = ADRuns.getRun(ADRunsController.store, runId)
if run == nil then return false end
local ingredients = {}
for _, name in ipairs(run.ingredients or {}) do table.insert(ingredients, name) end
local added = {}
for _, name in ipairs(fillTypes) do
local present = false
for _, have in ipairs(ingredients) do if have == name then present = true end end
if not present then table.insert(ingredients, name); table.insert(added, name) end
end
if #added == 0 then return false end
ADRunsController.updateRun(runId, {ingredients = ingredients})
log("flow '%s' får også %s", tostring(run.name), table.concat(added, ", "))
return true
end
-- Tidligere kædeflows (ét pr. vare) til samme bygning uden traktorer lægges sammen i ét.
function ADChainRun.mergeIdleFlows(adEnv)
local merged = 0
for _, merge in ipairs(ADChainActions.mergeIdle(ADChain.runsView(ADRunsController.store.runs))) do
if #merge.add > 0 then extendFlow(merge.keepId, merge.add) end
for _, id in ipairs(merge.deleteIds) do
local ok, err = pcall(ADRunsController.deleteRun, id, adEnv)
if ok then
ADChain.noVehicle[id] = nil
merged = merged + 1
else
warn("kædeflow %s kunne ikke lægges sammen: %s", tostring(id), tostring(err))
end
end
end
if merged > 0 then log("%d tomme kædeflows lagt sammen (ét flow pr. bygning)", merged) end
return merged
end
function ADChainRun.createFlows(adEnv, data, edges)
local missing = ADChainActions.missingFlows(edges, ADChain.runsView(ADRunsController.store.runs))
local runs = ADChain.runsView(ADRunsController.store.runs)
local missing = ADChainActions.missingFlows(edges, runs)
local pool, used, count = {}, {}, 0
for _, edge in ipairs(missing) do
for _, group in ipairs(ADChainActions.groupMissing(missing)) do
if count >= ADChain.MAX_NEW_FLOWS then break end
count = count + 1
local ok, err = pcall(createFlow, adEnv, data, edge, pool, used)
if not ok then warn("flow til %s (%s) kunne ikke oprettes: %s", tostring(edge.consumerName), tostring(edge.fillType), tostring(err)) end
local bulk, pallets = {}, {}
for _, name in ipairs(group.fillTypes) do
local index = data.fillIndex[name]
table.insert((index ~= nil and ADSources.isPalletFillType(index)) and pallets or bulk, name)
end
local ok, err = pcall(function()
if #bulk > 0 then
local existing = ADChainActions.chainRunFor(runs, group.consumer)
if existing ~= nil then
if extendFlow(existing, bulk) then count = count + 1 end
else
createGroupFlow(adEnv, data, group, bulk, pool, used)
count = count + 1
end
end
for _, name in ipairs(pallets) do
createGroupFlow(adEnv, data, group, {name}, pool, used)
count = count + 1
end
end)
if not ok then warn("flow til %s kunne ikke oprettes: %s", tostring(group.consumerName), tostring(err)) end
end
return count
end
-- Slutprodukter: sælg straks (fabrikkens udgang -> "Sælg direkte"), medmindre kæden står på "Behold". Varer kæden
-- selv satte til salg, sættes tilbage til "Behold", når spilleren vælger det.
local function outputMode(point, fillIndex)
if point.getOutputDistributionMode == nil then return nil end
local ok, mode = pcall(point.getOutputDistributionMode, point, fillIndex)
return ok and mode or nil
end
function ADChainRun.applyFinalSales(data, chain, edges)
if ProductionPoint == nil or ProductionPoint.OUTPUT_MODE == nil then return 0 end
local state, changed = ADChain.state, 0
local wanted = {}
for _, sale in ipairs(ADChainActions.finalSales(chain, data.recipes, edges, state.finalKeep)) do
local id = sale.key .. "|" .. sale.fillType
wanted[id] = true
local entry, fillIndex = (data.points or {})[sale.key], data.fillIndex[sale.fillType]
if entry ~= nil and entry.point ~= nil and fillIndex ~= nil and entry.point.setOutputDistributionMode ~= nil
and outputMode(entry.point, fillIndex) ~= ProductionPoint.OUTPUT_MODE.DIRECT_SELL then
entry.point:setOutputDistributionMode(fillIndex, ProductionPoint.OUTPUT_MODE.DIRECT_SELL)
state.soldByChain[id] = true
changed = changed + 1
log("slutprodukt %s i %s sælges straks", tostring(sale.fillType), tostring(entry.buildingName or sale.key))
end
end
for id in pairs(state.soldByChain) do
local key, fillType = id:match("^(.*)|([^|]+)$")
if key ~= nil and state.finalKeep[key] and not wanted[id] then
local entry, fillIndex = (data.points or {})[key], data.fillIndex[fillType]
if entry ~= nil and entry.point ~= nil and fillIndex ~= nil
and outputMode(entry.point, fillIndex) == ProductionPoint.OUTPUT_MODE.DIRECT_SELL then
entry.point:setOutputDistributionMode(fillIndex, ProductionPoint.OUTPUT_MODE.KEEP)
changed = changed + 1
log("slutprodukt %s beholdes igen", tostring(fillType))
end
state.soldByChain[id] = nil
end
end
return changed
end
-- Flows for rødder der er slået fra: flyt til en anden rod der stadig bruger kanten, ellers stop og slip (chain
-- ryddes, så spilleren kan starte flowet igen uden at kæden stopper det næste minut; det slettes ikke).
function ADChainRun.stopFlows(adEnv, chain, edges)
@@ -202,7 +310,12 @@ function ADChainRun.apply(adEnv, data, result)
if not okFields then warn("markskift fejlede: %s", tostring(errFields)) end
local okProd, errProd = pcall(ADChainRun.toggleProductions, data, result.prod)
if not okProd then warn("mellemled fejlede: %s", tostring(errProd)) end
local okMerge, mergedCount = pcall(ADChainRun.mergeIdleFlows, adEnv)
if not okMerge then warn("sammenlægning af kædeflows fejlede: %s", tostring(mergedCount)) end
local okNew, created = pcall(ADChainRun.createFlows, adEnv, data, result.edges)
local okSell, sellErr = pcall(ADChainRun.applyFinalSales, data, result.chain, result.edges)
if not okSell then warn("slutprodukter fejlede: %s", tostring(sellErr)) end
if okMerge and mergedCount > 0 then created = (okNew and created or 0) + mergedCount end
if not okNew then warn("nye flows fejlede: %s", tostring(created)) end
-- delvise data: en rod der ikke kunne læses, må ikke få sine flows stoppet
local okStop, stopped = true, 0
+6 -3
View File
@@ -20,7 +20,7 @@ ADChainView.TEXT_ROW_CHARS = 70
-- "stramt": lager for en mark-afgrøde under så mange måneders behov -> gul
ADChainView.TIGHT_MONTHS = 3
-- nøgler rækkerne bruger (til l10n-testen)
ADChainView.TEXT_KEYS = {"spu_chain_suggestHeader", "spu_chain_empty", "spu_chain_approve", "spu_chain_dismiss", "spu_chain_auto",
ADChainView.TEXT_KEYS = {"spu_chain_finalSell", "spu_chain_finalKeep", "spu_chain_suggestHeader", "spu_chain_empty", "spu_chain_approve", "spu_chain_dismiss", "spu_chain_auto",
"spu_chain_flowOk", "spu_chain_flowMissing", "spu_chain_flowPallet", "spu_chain_flowNone", "spu_chain_flowStopped", "spu_chain_bottleneck",
"spu_chain_buy", "spu_chain_cycle", "spu_chain_fieldShort", "spu_chain_tight", "spu_chain_noField", "spu_chain_buySource", "spu_chain_fromFields",
"spu_chain_pause", "spu_chain_resume", "spu_chain_scanning", "spu_chain_rootOk", "spu_chain_rootError", "spu_chain_rootWaiting",
@@ -313,9 +313,12 @@ local function rootRows(rows, root, last, ctx, isExpanded)
local status = ctx.textOf(ROOT_STATUS[level])
if #suggestions > 0 then status = string.format(ctx.textOf("spu_chain_rootSummary"), status, #suggestions) end
local marker = (#(root.children or {}) > 0) and (isExpanded and "– " or "+ ") or ""
table.insert(rows, makeRow("root", 0, level, {marker .. ctx.titleOf(root.name), tostring(root.buildingName or ""), "", status,
-- slutproduktet: "Sælg straks" (standard) eller "Behold" — klik på feltet skifter
local keep = (last.finalKeep or {})[root.key] == true
table.insert(rows, makeRow("root", 0, level, {marker .. ctx.titleOf(root.name), tostring(root.buildingName or ""),
ctx.textOf(keep and "spu_chain_finalKeep" or "spu_chain_finalSell"), status,
ctx.textOf(paused and "spu_chain_resume" or "spu_chain_pause")},
{[5] = {kind = paused and "resume" or "pause", rootKey = root.key}}, root.key))
{[3] = {kind = "finalMode", rootKey = root.key, keep = not keep}, [5] = {kind = paused and "resume" or "pause", rootKey = root.key}}, root.key))
if not isExpanded then return end
local shown, problems, told = {}, {}, {}
for _, problem in ipairs((last.chain or {}).problems or {}) do
+1 -1
View File
@@ -166,7 +166,7 @@ function ADFieldWork.getRigs(adEnv)
manual = (state.manual or {})[id] == true,
width = ADCpBridge.getWorkWidth(vehicle), x = x, z = z,
-- tom spreder kan flere ting (gødning og kalk): alle dens roller
roles = specs.sprayRoles or ADFieldFleet.rolesFor(role), filledRole = specs.filledRole, dumpable = specs.dumpable,
roles = ADPool.rolesOf(role, specs.sprayRoles), filledRole = specs.filledRole, dumpable = specs.dumpable,
}
if role == "harvester" then rig.fruits = ADCpBridge.getHarvestFruits(vehicle) or {} end
if role == "seeder" then
+9
View File
@@ -74,6 +74,15 @@ function ADFlowView.boardGroups(runs)
return groups
end
-- Kædeflows uden traktor foldes sammen i listen (de venter på en ledig traktor/vogn). -> active, idle
function ADFlowView.splitIdle(runs)
local active, idle = {}, {}
for _, run in ipairs(runs or {}) do
table.insert(#(run.vehicleIds or {}) > 0 and active or idle, run)
end
return active, idle
end
-- Navn i listen: kædeflows står under overskriften "Kædernes egne flows", så "Kæde: " fjernes (ellers skjuler
-- afkortningen hvilken bygning det gælder).
function ADFlowView.listName(run)
+8 -1
View File
@@ -18,6 +18,13 @@ function ADPool.roleOf(specs)
return role
end
-- Ekstra roller for et redskab: sprøjtens roller (gødning/kalk/ukrudt) — men ikke for en såmaskine med
-- gødningstank (Kinze 4905), den er kun såmaskine (in-game 30/9: hentet til ren gødning).
function ADPool.rolesOf(role, sprayRoles)
if role ~= "seeder" and sprayRoles ~= nil then return sprayRoles end
return ADFieldFleet.rolesFor(role)
end
-- Butiksdata er enten et tal eller en tabel pr. konfiguration: største tal, ellers nil.
function ADPool.numberOf(value)
if type(value) == "number" then return value end
@@ -402,7 +409,7 @@ function ADPool.collect(adEnv, isBusy)
local carries, capacity = ADPool.cargoOf(vehicle)
table.insert(implements, {carries = carries, capacity = capacity,
id = ADRunsController.getVehicleId(vehicle), vehicle = vehicle, name = ADRuns.vehicleLabel(vehicle),
role = role, roles = specs.sprayRoles or ADFieldFleet.rolesFor(role), x = x, z = z, width = ADCpBridge.getWorkWidth(vehicle),
role = role, roles = ADPool.rolesOf(role, specs.sprayRoles), x = x, z = z, width = ADCpBridge.getWorkWidth(vehicle),
neededHp = neededHpOf(vehicle), connectId = connect.id, parkId = park.id,
})
end
@@ -27,6 +27,8 @@ function SmartPickupFrame:onChainAction(action)
ok = ADChain.approve(action.fieldId)
elseif action.kind == "dismiss" then
ok = ADChain.dismiss(action.fieldId)
elseif action.kind == "finalMode" then
ADChain.setFinalKeep(action.rootKey, action.keep)
elseif action.kind == "pause" or action.kind == "resume" then
ADChain.setPaused(action.rootKey, action.kind == "pause")
end
@@ -232,10 +232,8 @@ function SmartPickupFrame:fillFlowTabRows(adEnv)
return
end
self:addTextRow(i18n:getText("spu_flowList_hint"))
for _, group in ipairs(ADFlowView.boardGroups(ADRunsController.store.runs)) do
self:addTextRow(i18n:getText(group.chain and "spu_board_chain" or group.kind == "outbound" and "spu_board_outbound" or "spu_board_supply"))
for _, run in ipairs(group.runs) do
local row = self:addRow(self.flowListPrefab, {kind = "flowList"})
local function addFlowRow(run)
local row = self:addRow(self.flowListPrefab, {kind = "flowList"})
local entry = {runId = run.id, row = row, dot = row:getDescendantByName("dot"),
cells = {row:getDescendantByName("c1"), row:getDescendantByName("c2"), row:getDescendantByName("c3"),
row:getDescendantByName("c4"), row:getDescendantByName("c5")}}
@@ -252,6 +250,22 @@ function SmartPickupFrame:fillFlowTabRows(adEnv)
end
end)
table.insert(self.flowListRows, entry)
end
for _, group in ipairs(ADFlowView.boardGroups(ADRunsController.store.runs)) do
self:addTextRow(i18n:getText(group.chain and "spu_board_chain" or group.kind == "outbound" and "spu_board_outbound" or "spu_board_supply"))
local runs, idle = group.runs, {}
-- kædeflows uden traktor (venter på en ledig traktor/vogn) foldes sammen (in-game 30/9)
if group.chain then runs, idle = ADFlowView.splitIdle(group.runs) end
for _, run in ipairs(runs) do addFlowRow(run) end
if #idle > 0 then
local key = self.showIdleChainFlows and "spu_board_chainIdleHide" or "spu_board_chainIdleShow"
self:addLinkRow(" " .. string.format(i18n:getText(key), #idle), function(frame)
frame.showIdleChainFlows = not frame.showIdleChainFlows
frame:requestRebuild()
end)
if self.showIdleChainFlows then
for _, run in ipairs(idle) do addFlowRow(run) end
end
end
end
self:refreshFlowListRows(adEnv)
+4
View File
@@ -377,6 +377,8 @@
<text name="spu_col_to" text="Til"/>
<text name="spu_col_tractors" text="Kører"/>
<text name="spu_col_status" text="Status"/>
<text name="spu_board_chainIdleShow" text="%d kædeflows venter på en ledig traktor/vogn — klik for at vise"/>
<text name="spu_board_chainIdleHide" text="%d kædeflows uden traktor — klik for at skjule"/>
<text name="spu_board_chain" text="· Kædernes egne flows (oprettes af fanen Kæder) ·"/>
<text name="spu_board_supply" text="· Ind til bygning ·"/>
<text name="spu_board_outbound" text="· Ud fra bygning ·"/>
@@ -577,6 +579,8 @@
<text name="spu_item_balanced" text="i balance"/>
<text name="spu_item_share" text="%d %% af varen"/>
<text name="spu_tab_chains" text="Kæder"/>
<text name="spu_chain_finalSell" text="Sælg"/>
<text name="spu_chain_finalKeep" text="Behold"/>
<text name="spu_chain_suggestHeader" text="Forslag til ny afgrøde"/>
<text name="spu_chain_empty" text="Ingen kæder: slå et produkt til i en fabrik"/>
<text name="spu_chain_col_item" text="Vare"/>
+4
View File
@@ -377,6 +377,8 @@
<text name="spu_col_to" text="To"/>
<text name="spu_col_tractors" text="Running"/>
<text name="spu_col_status" text="Status"/>
<text name="spu_board_chainIdleShow" text="%d chain flows are waiting for a free tractor/trailer — click to show"/>
<text name="spu_board_chainIdleHide" text="%d chain flows without a tractor — click to hide"/>
<text name="spu_board_chain" text="· Flows created by chains (Chains tab) ·"/>
<text name="spu_board_supply" text="· Into building ·"/>
<text name="spu_board_outbound" text="· Out of building ·"/>
@@ -577,6 +579,8 @@
<text name="spu_item_balanced" text="balanced"/>
<text name="spu_item_share" text="%d %% of the good"/>
<text name="spu_tab_chains" text="Chains"/>
<text name="spu_chain_finalSell" text="Sell"/>
<text name="spu_chain_finalKeep" text="Keep"/>
<text name="spu_chain_suggestHeader" text="Crop change suggestions"/>
<text name="spu_chain_empty" text="No chains: enable a product in a factory"/>
<text name="spu_chain_col_item" text="Goods"/>
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<modDesc descVersion="107">
<author>masterdraco</author>
<version>1.61.3.0</version>
<version>1.61.4.0</version>
<title>
<en>AD Smart Pickup</en>
</title>