-- AD Chain Actions (ren logik): mellemled der skal tændes/slukkes, flows der mangler eller skal stoppes. ADChainActions = {} -- protected = {[nøgle] = cyklusser}: behov fra pausede rødder — slukkes ikke, men tændes heller ikke. function ADChainActions.productions(recipes, needRecipes, protected) local result = {enable = {}, disable = {}} for _, recipe in ipairs(recipes or {}) do local needed = ((needRecipes or {})[recipe.key] or 0) > 0 local kept = ((protected or {})[recipe.key] or 0) > 0 if needed and not recipe.enabled then table.insert(result.enable, recipe.key) end if not needed and not kept and recipe.enabled and recipe.byChain then table.insert(result.disable, recipe.key) end end table.sort(result.enable); table.sort(result.disable) return result end local SUPPLIED = {production = true, husbandry = true, field = true} -- 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 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 end for _, root in ipairs((chain or {}).roots or {}) do walk(root.children, root.key) end return list end local function contains(list, value) for _, item in ipairs(list or {}) do if item == value then return true end end return false end function ADChainActions.isCovered(edge, runs) for _, run in ipairs(runs or {}) do if run.kind == "supply" and contains(run.targetBuildingIds, edge.consumer) and (run.ingredientMode ~= "manual" or contains(run.ingredients, edge.fillType)) then return true end end return false end function ADChainActions.missingFlows(edges, runs) local list = {} for _, edge in ipairs(edges or {}) do 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 function ADChainActions.flowsToStop(runs, activeRootKeys) local list = {} for _, run in ipairs(runs or {}) do -- også stoppede kædeflows uden traktor (de ryddes op); stoppede med traktor er allerede sluppet/spillerens local idle = #(run.vehicleIds or {}) == 0 if run.chain ~= nil and run.chain ~= "" and (not run.stopped or idle) and not (activeRootKeys or {})[run.chain] then table.insert(list, run) end 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 -- Varer gården selv bruger: stalde æder dem, en tændt produktion bruger dem, gården har forbrug af dem, eller et -- flow (ikke udkørsel) kører dem. -> {[vare] = true}. Den slags sælges aldrig straks (in-game 30/9: TMR og roesnitter). function ADChainActions.farmUses(data, runs) local uses = {} for name in pairs((data or {}).barnFood or {}) do uses[name] = true end for name, rate in pairs((data or {}).rates or {}) do if (rate.consumption or 0) > 0 then uses[name] = true end end for _, recipe in ipairs((data or {}).recipes or {}) do if recipe.enabled then for name in pairs(recipe.inputs or {}) do uses[name] = true end end end for _, run in ipairs(runs or {}) do if run.kind ~= "outbound" and run.ingredientMode == "manual" then for _, name in ipairs(run.ingredients or {}) do uses[name] = true end end end return uses end -- Slutprodukter der skal sælges straks: aktive rødders udgange, som ikke selv er en kant i kæden (mellemvare) og -- som gården ikke selv bruger (farmUses). keep = {[rodnøgle] = true} for rødder sat til "Behold". -> {{key, fillType}} function ADChainActions.finalSales(chain, recipes, edges, keep, farmUses) 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] and not (farmUses or {})[name] then table.insert(list, {key = root.key, fillType = name}) end end end end return list end -- Kædeflow for en kæde der er slået fra: uden traktor slettes det (auto-oprettet og tomt), ellers stoppes og slippes -- det (bliver spillerens eget). (in-game 30/9: ellers 12 tomme "Kæde: …"-flows blandt spillerens egne) function ADChainActions.releaseAction(run) if #(run.vehicleIds or {}) == 0 then return "delete" end return "release" end