feat: kæder respekterer fabrikkens udgang (Fordel = intet flow, Sælg på mellemvare -> Behold)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -606,6 +606,7 @@
|
||||
<text name="spu_chain_flowOk" text="flow kører"/>
|
||||
<text name="spu_chain_flowMissing" text="mangler traktor/vogn"/>
|
||||
<text name="spu_chain_flowNone" text="intet flow"/>
|
||||
<text name="spu_chain_flowAuto" text="fordeles af spillet"/>
|
||||
<text name="spu_chain_flowPallet" text="palle: vælg UAL-vogn"/>
|
||||
<text name="spu_chain_flowStopped" text="flow stoppet"/>
|
||||
<text name="spu_chain_bottleneck" text="flaskehals: -%s/md"/>
|
||||
|
||||
@@ -606,6 +606,7 @@
|
||||
<text name="spu_chain_flowOk" text="flow running"/>
|
||||
<text name="spu_chain_flowMissing" text="no tractor/trailer"/>
|
||||
<text name="spu_chain_flowNone" text="no flow"/>
|
||||
<text name="spu_chain_flowAuto" text="distributed by game"/>
|
||||
<text name="spu_chain_flowPallet" text="pallet: UAL trailer"/>
|
||||
<text name="spu_chain_flowStopped" text="flow stopped"/>
|
||||
<text name="spu_chain_bottleneck" text="bottleneck: -%s/mo"/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<modDesc descVersion="107">
|
||||
<author>masterdraco</author>
|
||||
<version>1.62.1.0</version>
|
||||
<version>1.62.2.0</version>
|
||||
<title>
|
||||
<en>AD Smart Pickup</en>
|
||||
</title>
|
||||
|
||||
Reference in New Issue
Block a user