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 <en>AD Smart Pickup</en>