diff --git a/CHANGELOG.md b/CHANGELOG.md
index ac81926..bd2b806 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ står i [docs/BRUGERMANUAL.md](docs/BRUGERMANUAL.md) (dansk) og [docs/USER_MANUA
## 1.62 — Kæder slås selv til
+- **1.62.2** Kæderne læser fabrikkernes udgange (Sælg / Fordel / Behold). **Fordel:** spillet leverer selv til gårdens fabrikker, så kæden opretter intet flow, og fanen Kæder viser *fordeles af spillet*. **Behold** (spawn eller lager): et flow henter varen ved fabrikken som hidtil. **Sælg på en mellemvare** betyder, at næste led i kæden intet får, så kæden sætter den til Behold og henter den.
- **1.62.1** Rettet: udkørsel fra objektlager (fx Sell Egg fra Æglageret) stoppede halvfuld med "spawner ikke … (30 s uden fremdrift)". Lageret deler samme vare i flere grupper (paller med forskellig fyldning), og moden talte kun den første gruppe — når en lille gruppe blev tom, så lageret ud til at vokse. Nu tælles alle grupper. Advarslen viser lager- og vogntal, hvis den alligevel opstår.
- **1.62.0** **En kæde er noget du selv slår til:** tændte produktioner er ikke længere automatisk kæder (FS25 tænder nye produktioner som standard — 7 ensilagetårne gav 56 kæder og fik AIO-fabrikkens høproduktion tændt). Fanen **Kæder** har nederst *N tændte produktioner uden kæde* (foldet sammen); **[Kæde til]** gør en produktion til en kæde. En pauset kæde har **[Fjern]**. Ikke-valgte produktioner bruges stadig som kilde. Når en kæde fjernes, slettes dens tomme kædeflows (uden traktor), og mellemled kæden selv tændte slukkes igen. Rettet: kædens flowoversigt manglede traktorerne, så sammenlægning/oprydning kunne ramme et kædeflow med traktor. Udkørsel fra palleskur holder AutoDrive, mens moden tager paller ud.
diff --git a/FS25_ADSmartPickup/adChain.lua b/FS25_ADSmartPickup/adChain.lua
index e9eaa62..e2a2ea2 100644
--- a/FS25_ADSmartPickup/adChain.lua
+++ b/FS25_ADSmartPickup/adChain.lua
@@ -244,12 +244,13 @@ function ADChain.stillNeeded(runView, edges)
return nil
end
--- {[forbruger|vare] = "ok"|"missing"|"stopped"|"noVehicle"|"pallet"} for hver kant.
+-- {[forbruger|vare] = "ok"|"auto"|"missing"|"stopped"|"noVehicle"|"pallet"} for hver kant.
function ADChain.flowStatus(edges, runs, noVehicle)
local result = {}
for _, edge in ipairs(edges or {}) do
- local status = "missing"
- for _, run in ipairs(runs or {}) do
+ -- udgangen står på Fordel: spillet leverer selv, intet flow
+ local status = edge.delivery == "auto" and "auto" or "missing"
+ for _, run in ipairs(edge.delivery == "auto" and {} or runs or {}) do
if run.kind == "supply" and covers(run, edge) then
if not run.stopped then status = "ok"; break end
local reason = (noVehicle or {})[run.id]
@@ -318,7 +319,7 @@ local function plan(data, state)
end
-- de ægte opskrifter (ikke kopien): en pauset rod er stadig tændt og må ikke "tændes" og overtages af kæden
local prod = ADChainActions.productions(data.recipes, chain.needRecipes, protected)
- return {chain = chain, shortages = shortages, actions = actions, prod = prod, edges = ADChainActions.edges(chain)}
+ return {chain = chain, shortages = shortages, actions = actions, prod = prod, edges = ADChainActions.edges(chain, data.recipes)}
end
local function logInfo(fmt, ...) Logging.info(ADChain.LOG_PREFIX .. fmt, ...) end
diff --git a/FS25_ADSmartPickup/adChainActions.lua b/FS25_ADSmartPickup/adChainActions.lua
index 92d4063..1469a5a 100644
--- a/FS25_ADSmartPickup/adChainActions.lua
+++ b/FS25_ADSmartPickup/adChainActions.lua
@@ -17,14 +17,53 @@ end
local SUPPLIED = {production = true, husbandry = true, field = true}
-function ADChainActions.edges(chain)
- local list, seen = {}, {}
+-- Hvordan varen når frem, ud fra producenternes udgang: "auto" = alle fordeler (spillet leverer selv til gårdens
+-- fabrikker; kædens forbrugere er altid fabrikker) -> intet flow; "sold" = en producent sælger mellemvaren væk;
+-- ellers "haul" (Behold: spawn/lager ved fabrikken, eller stald/mark) -> et flow henter. recipesByKey = {[nøgle] = opskrift}.
+function ADChainActions.delivery(edge, recipesByKey)
+ local producers = edge.producers or {}
+ if #producers == 0 or recipesByKey == nil then return "haul" end
+ local all, sold = true, false
+ for _, key in ipairs(producers) do
+ local mode = ADChainActions.producerMode(recipesByKey, key, edge.fillType)
+ if mode == "sell" then sold = true end
+ if mode ~= "distribute" then all = false end
+ end
+ if sold then return "sold" end
+ return all and "auto" or "haul"
+end
+
+function ADChainActions.producerMode(recipesByKey, key, fillType)
+ local recipe = (recipesByKey or {})[key]
+ return recipe ~= nil and (recipe.outputModes or {})[fillType] or nil
+end
+
+local function producerKeys(node)
+ local keys = {}
+ for _, producer in ipairs(node.producers or {}) do table.insert(keys, producer.key) end
+ return keys
+end
+
+-- recipes (valgfri): gårdens opskrifter med outputModes -> kanten får delivery (se ADChainActions.delivery).
+function ADChainActions.edges(chain, recipes)
+ local list, seen, byKey = {}, {}, nil
+ if recipes ~= nil then
+ byKey = {}
+ for _, recipe in ipairs(recipes) do byKey[recipe.key] = recipe end
+ end
local function walk(nodes, rootKey)
for _, node in ipairs(nodes or {}) do
local id = tostring(node.consumer) .. "|" .. tostring(node.fillType)
if SUPPLIED[node.source] and node.consumer ~= nil and not seen[id] then
seen[id] = true
- table.insert(list, {fillType = node.fillType, consumer = node.consumer, consumerName = node.consumerName, rootKey = rootKey})
+ local edge = {fillType = node.fillType, consumer = node.consumer, consumerName = node.consumerName, rootKey = rootKey,
+ producers = producerKeys(node)}
+ edge.delivery = ADChainActions.delivery(edge, byKey)
+ edge.soldBy = {}
+ for _, key in ipairs(edge.producers) do
+ if ADChainActions.producerMode(byKey, key, edge.fillType) == "sell" then table.insert(edge.soldBy, key) end
+ end
+ table.insert(list, edge)
end
walk(node.children, rootKey)
end
@@ -51,7 +90,24 @@ end
function ADChainActions.missingFlows(edges, runs)
local list = {}
for _, edge in ipairs(edges or {}) do
- if not ADChainActions.isCovered(edge, runs) then table.insert(list, edge) end
+ if edge.delivery ~= "auto" and not ADChainActions.isCovered(edge, runs) then table.insert(list, edge) end
+ end
+ return list
+end
+
+-- Mellemvarer en producent sælger væk (kanten er "sold") -> {{key, fillType}} én gang hver; kæden sætter dem til Behold.
+function ADChainActions.soldIntermediates(edges)
+ local list, seen = {}, {}
+ for _, edge in ipairs(edges or {}) do
+ if edge.delivery == "sold" then
+ for _, key in ipairs(edge.soldBy or {}) do
+ local id = key .. "|" .. edge.fillType
+ if not seen[id] then
+ seen[id] = true
+ table.insert(list, {key = key, fillType = edge.fillType})
+ end
+ end
+ end
end
return list
end
diff --git a/FS25_ADSmartPickup/adChainData.lua b/FS25_ADSmartPickup/adChainData.lua
index 4e2485b..52c5175 100644
--- a/FS25_ADSmartPickup/adChainData.lua
+++ b/FS25_ADSmartPickup/adChainData.lua
@@ -92,6 +92,28 @@ end
-- ---- læsning pr. del ----
-- En fabriks produktioner -> opskrifter + punkter (samles først, så en fejl midtvejs ikke efterlader halve).
+-- Udgangens fordeling (spillets Sælg/Fordel/Behold) -> "sell"|"distribute"|"keep". Behold dækker både spawn (paller
+-- ved fabrikken) og lager (STORE fra ProductionStorageControl): begge dele skal hentes af et flow.
+function ADChainData.outputModeName(mode)
+ local modes = ProductionPoint ~= nil and ProductionPoint.OUTPUT_MODE or {}
+ if mode ~= nil and mode == modes.DIRECT_SELL then return "sell" end
+ if mode ~= nil and mode == modes.AUTO_DELIVER then return "distribute" end
+ return "keep"
+end
+
+local function outputModes(point, outputs, nameOf)
+ local modes = {}
+ if point.getOutputDistributionMode == nil then return modes end
+ for _, output in ipairs(outputs or {}) do
+ local name = output.type ~= nil and nameOf(output.type) or nil
+ if name ~= nil then
+ local ok, mode = pcall(point.getOutputDistributionMode, point, output.type)
+ modes[name] = ADChainData.outputModeName(ok and mode or nil)
+ end
+ end
+ return modes
+end
+
local function readFactory(placeable, point, byChain, nameOf)
local uid = placeableId(placeable)
if uid == nil then return {}, {} end
@@ -102,8 +124,9 @@ local function readFactory(placeable, point, byChain, nameOf)
table.insert(recipes, {key = key, name = tostring(production.name or production.id), building = uid,
buildingName = buildingName, enabled = isEnabled(point, production), byChain = (byChain or {})[key] == true,
cyclesPerMonth = ADChainData.cyclesPerMonth(production),
- inputs = amountsByName(production.inputs, nameOf), outputs = amountsByName(production.outputs, nameOf)})
- points[key] = {point = point, productionId = production.id, placeable = placeable}
+ inputs = amountsByName(production.inputs, nameOf), outputs = amountsByName(production.outputs, nameOf),
+ outputModes = outputModes(point, production.outputs, nameOf)})
+ points[key] = {point = point, productionId = production.id, placeable = placeable, buildingName = buildingName}
end
return recipes, points
end
diff --git a/FS25_ADSmartPickup/adChainRun.lua b/FS25_ADSmartPickup/adChainRun.lua
index 07464c4..034bd75 100644
--- a/FS25_ADSmartPickup/adChainRun.lua
+++ b/FS25_ADSmartPickup/adChainRun.lua
@@ -281,6 +281,25 @@ function ADChainRun.applyFinalSales(data, chain, edges)
return changed
end
+-- Mellemvarer der står på Sælg: næste led i kæden fik intet. Sættes til Behold (spawn/lager), så kædens flow henter
+-- dem ved fabrikken; tages ud af soldByChain, så slutprodukt-logikken ikke sætter dem til salg igen.
+function ADChainRun.keepIntermediates(data, edges)
+ if ProductionPoint == nil or ProductionPoint.OUTPUT_MODE == nil then return 0 end
+ local changed = 0
+ for _, item in ipairs(ADChainActions.soldIntermediates(edges)) do
+ local entry, fillIndex = (data.points or {})[item.key], data.fillIndex[item.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.KEEP)
+ ADChain.state.soldByChain[item.key .. "|" .. item.fillType] = nil
+ changed = changed + 1
+ log("mellemvare %s i %s sælges ikke længere (næste led i kæden bruger den) -> Behold", tostring(item.fillType),
+ tostring(entry.buildingName or item.key))
+ 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)
@@ -326,6 +345,8 @@ function ADChainRun.apply(adEnv, data, result)
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
+ local okKeep, keepErr = pcall(ADChainRun.keepIntermediates, data, result.edges)
+ if not okKeep then warn("mellemvarer fejlede: %s", tostring(keepErr)) 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
diff --git a/FS25_ADSmartPickup/adChainView.lua b/FS25_ADSmartPickup/adChainView.lua
index b2039ab..43c0ec4 100644
--- a/FS25_ADSmartPickup/adChainView.lua
+++ b/FS25_ADSmartPickup/adChainView.lua
@@ -26,10 +26,10 @@ ADChainView.TEXT_KEYS = {"spu_chain_optIn", "spu_chain_optOut", "spu_chain_notCh
"spu_chain_pause", "spu_chain_resume", "spu_chain_scanning", "spu_chain_rootOk", "spu_chain_rootError", "spu_chain_rootWaiting",
"spu_chain_rootPaused", "spu_chain_rootSummary", "spu_chain_free", "spu_chain_switches", "spu_chain_gone", "spu_chain_col_item",
"spu_chain_col_source", "spu_chain_col_perMonth", "spu_chain_col_status", "spu_chain_col_stock",
- "spu_chain_problem_buy", "spu_chain_problem_bottleneck", "spu_chain_problem_off", "spu_chain_problem_cycle", "spu_chain_problem_barn"}
+ "spu_chain_flowAuto", "spu_chain_problem_buy", "spu_chain_problem_bottleneck", "spu_chain_problem_off", "spu_chain_problem_cycle", "spu_chain_problem_barn"}
local FLOW_KEYS = {ok = "spu_chain_flowOk", missing = "spu_chain_flowNone", stopped = "spu_chain_flowStopped", noVehicle = "spu_chain_flowMissing",
- pallet = "spu_chain_flowPallet"}
+ pallet = "spu_chain_flowPallet", auto = "spu_chain_flowAuto"}
local ROOT_STATUS = {running = "spu_chain_rootOk", error = "spu_chain_rootError", waiting = "spu_chain_rootWaiting", stopped = "spu_chain_rootPaused"}
local function identity(value) return tostring(value) end
@@ -129,7 +129,7 @@ function ADChainView.rootLevel(root, last)
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") or isTight(node, last, short) then hasWaiting = true end
+ if (status ~= nil and status ~= "ok" and status ~= "auto") or isTight(node, last, short) then hasWaiting = true end
end)
if hasShortage then return "error" end
if hasWaiting then return "waiting" end
@@ -218,6 +218,8 @@ local function nodeStatus(node, last, fmt, textOf, short)
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
+ -- udgangen står på Fordel: spillet leverer selv
+ if status == "auto" then return textOf("spu_chain_flowAuto"), "running" end
if status ~= nil and status ~= "ok" then return textOf(FLOW_KEYS[status] or "spu_chain_flowNone"), "waiting" end
if isTight(node, last, short) then return textOf("spu_chain_tight"), "waiting" end
if status == "ok" then return textOf("spu_chain_flowOk"), "running" end
diff --git a/FS25_ADSmartPickup/l10n/l10n_da.xml b/FS25_ADSmartPickup/l10n/l10n_da.xml
index 66a2834..16d718d 100644
--- a/FS25_ADSmartPickup/l10n/l10n_da.xml
+++ b/FS25_ADSmartPickup/l10n/l10n_da.xml
@@ -606,6 +606,7 @@
+
diff --git a/FS25_ADSmartPickup/l10n/l10n_en.xml b/FS25_ADSmartPickup/l10n/l10n_en.xml
index 42f5876..eb670c6 100644
--- a/FS25_ADSmartPickup/l10n/l10n_en.xml
+++ b/FS25_ADSmartPickup/l10n/l10n_en.xml
@@ -606,6 +606,7 @@
+
diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml
index 3628bcf..59d2baf 100644
--- a/FS25_ADSmartPickup/modDesc.xml
+++ b/FS25_ADSmartPickup/modDesc.xml
@@ -1,7 +1,7 @@
masterdraco
- 1.62.1.0
+ 1.62.2.0
AD Smart Pickup