feat(kæde): advarsel når en fabrik gården er afhængig af slukkes
Slukker spilleren en produktion, hvis vare et tændt flow kører (manuel vare eller målet tager den i auto), en stald æder eller en aktiv kæde bruger, kommer én besked pr. slukning med brugere og lagerdage, og en rød post 'Slukket men nødvendig' i Kræver handling og statusvinduet så længe den er slukket og nødvendig. Ingen advarsel når en anden tændt produktion dækker, lageret rækker over 60 dage, kæden selv slukkede den eller den var slukket ved indlæsning. Tjekket kører i kædens tick også uden kæder; ren logik i adDependency.lua.
This commit is contained in:
@@ -32,8 +32,9 @@ end
|
||||
|
||||
-- En læsefejl i dette kald: data.partial = true (også når advarslen allerede er logget), så kæden ikke
|
||||
-- slukker eller stopper noget på et ufuldstændigt billede af gården.
|
||||
-- data.readFailed skelner en ægte læsefejl fra en ufærdig markscanning (afhængighedstjekket bruger ikke markerne).
|
||||
local function failed(data, key, fmt, ...)
|
||||
data.partial = true
|
||||
data.partial, data.readFailed = true, true
|
||||
ADChainData.warnOnce(key, fmt, ...)
|
||||
end
|
||||
|
||||
@@ -118,11 +119,27 @@ local function readHerd(placeable, nameOf)
|
||||
return {building = uid, buildingName = placeableName(placeable, uid), outputs = outputs}
|
||||
end
|
||||
|
||||
-- Stald-foder: {[vare] = {staldnavn...}} for det truget æder nu (forbrug > 0) — til afhængighedstjekket.
|
||||
local function readBarnFood(data, placeable, nameOf)
|
||||
if ADSources.getHusbandryConsumption == nil then return end
|
||||
local barnName
|
||||
for index, perDay in pairs(ADSources.getHusbandryConsumption(placeable) or {}) do
|
||||
local name = nameOf(index)
|
||||
if name ~= nil and (perDay or 0) > 0 then
|
||||
barnName = barnName or placeableName(placeable, placeableId(placeable) or "?")
|
||||
data.barnFood[name] = data.barnFood[name] or {}
|
||||
table.insert(data.barnFood[name], barnName)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function readPlaceables(data, farmId, byChain, nameOf)
|
||||
local placeables = g_currentMission ~= nil and g_currentMission.placeableSystem ~= nil and g_currentMission.placeableSystem.placeables or {}
|
||||
for index, placeable in pairs(placeables) do
|
||||
local ok, err = pcall(function()
|
||||
if not ADPrices.isOwnedBy(placeable, farmId) then return end
|
||||
local uid = placeableId(placeable)
|
||||
if uid ~= nil then data.placeables[uid] = placeable end
|
||||
local point = placeable.spec_productionPoint ~= nil and placeable.spec_productionPoint.productionPoint or nil
|
||||
if point ~= nil then
|
||||
local recipes, points = readFactory(placeable, point, byChain, nameOf)
|
||||
@@ -131,6 +148,7 @@ local function readPlaceables(data, farmId, byChain, nameOf)
|
||||
elseif placeable.spec_husbandry ~= nil or placeable.getHusbandryFillLevel ~= nil then
|
||||
local herd = readHerd(placeable, nameOf)
|
||||
if herd ~= nil then table.insert(data.husbandry, herd) end
|
||||
readBarnFood(data, placeable, nameOf)
|
||||
end
|
||||
end)
|
||||
if not ok then
|
||||
@@ -182,13 +200,30 @@ end
|
||||
local function readStock(data, adEnv, farmId, nameOf)
|
||||
local ok, err = pcall(function()
|
||||
for index, liters in pairs(ADStorageStats.farmTotals(ADSources.getFarmInventory(adEnv, farmId))) do
|
||||
local fruit = data.crops[nameOf(index) or ""]
|
||||
local name = nameOf(index)
|
||||
if name ~= nil then data.stockByFill[name] = (data.stockByFill[name] or 0) + liters end
|
||||
local fruit = data.crops[name or ""]
|
||||
if fruit ~= nil then data.stock[fruit] = (data.stock[fruit] or 0) + liters end
|
||||
end
|
||||
end)
|
||||
if not ok then failed(data, "stock", "lager kunne ikke læses: %s", tostring(err)) end
|
||||
end
|
||||
|
||||
-- Gårdens forbrug og produktion pr. døgn (fabrikker der kører + stalde) pr. varenavn og spillets tidsfaktor
|
||||
-- (rå rater pr. måned × faktor = pr. døgn) — til lagerdage og dækning i afhængighedstjekket.
|
||||
local function readRates(data, adEnv, farmId, nameOf)
|
||||
local environment = g_currentMission ~= nil and g_currentMission.environment or nil
|
||||
local adjustment = environment ~= nil and tonumber(environment.timeAdjustment) or nil
|
||||
data.timeAdjustment = adjustment ~= nil and adjustment > 0 and adjustment or 1
|
||||
local ok, err = pcall(function()
|
||||
for index, rate in pairs(ADSources.getFarmRates(adEnv, farmId, false) or {}) do
|
||||
local name = nameOf(index)
|
||||
if name ~= nil then data.rates[name] = {consumption = rate.consumption or 0, production = rate.production or 0} end
|
||||
end
|
||||
end)
|
||||
if not ok then failed(data, "allRates", "gårdens forbrug kunne ikke læses: %s", tostring(err)) end
|
||||
end
|
||||
|
||||
-- Afgrøder gården selv bruger: input i en tændt produktion eller foder/halm i en stald (kun staldenes forbrug
|
||||
-- læses: feedOnly springer fabrikkernes rater over — dem dækker opskrifterne allerede).
|
||||
local function readUsedFruits(data, adEnv, farmId, nameOf)
|
||||
@@ -231,19 +266,107 @@ local function readFields(data)
|
||||
end
|
||||
end
|
||||
|
||||
-- ---- afhængighedstjek (spec afsnit 8) ----
|
||||
|
||||
local function unloadStationsOf(placeable)
|
||||
local storage = g_currentMission ~= nil and g_currentMission.storageSystem or nil
|
||||
local result = {}
|
||||
if storage == nil or storage.getUnloadingStations == nil then return result end
|
||||
for _, station in pairs(storage:getUnloadingStations() or {}) do
|
||||
if station.owningPlaceable == placeable then table.insert(result, station) end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
-- Hvad auto-flowenes mål tager af varerne `goods` (navne): {[bygnings-id] = {[vare] = true}}.
|
||||
-- Kun tændte forsyningsflows i auto-tilstand; hvert mål slås op én gang.
|
||||
function ADChainData.acceptsMap(data, flows, goods)
|
||||
local result = {}
|
||||
for _, flow in ipairs(flows or {}) do
|
||||
if flow.kind ~= "outbound" and not flow.stopped and flow.ingredientMode ~= "manual" then
|
||||
for _, id in ipairs(flow.targetBuildingIds or {}) do
|
||||
local placeable = data.placeables[id]
|
||||
if result[id] == nil and placeable ~= nil then
|
||||
result[id] = {}
|
||||
local ok, err = pcall(function()
|
||||
local stations = unloadStationsOf(placeable)
|
||||
for _, name in ipairs(goods) do
|
||||
local index = data.fillIndex[name]
|
||||
for _, station in ipairs(stations) do
|
||||
if index ~= nil and ADSources.stationAccepts(station, index) then result[id][name] = true; break end
|
||||
end
|
||||
end
|
||||
end)
|
||||
if not ok then ADChainData.warnOnce("accepts:" .. tostring(id), "mål %s kunne ikke læses: %s", tostring(id), tostring(err)) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
-- Kædens input-varer (alle varer i de aktive kæders træer) -> {[vare] = {kædenavn...}}
|
||||
function ADChainData.chainInputs(chain, titles)
|
||||
local result = {}
|
||||
local function walk(nodes, rootTitle)
|
||||
for _, node in ipairs(nodes or {}) do
|
||||
result[node.fillType] = result[node.fillType] or {}
|
||||
local list = result[node.fillType]
|
||||
if list[#list] ~= rootTitle then table.insert(list, rootTitle) end
|
||||
walk(node.children, rootTitle)
|
||||
end
|
||||
end
|
||||
for _, root in ipairs(chain ~= nil and chain.roots or {}) do
|
||||
walk(root.children, tostring((titles or {})[root.name] or root.name))
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
-- Alt afhængighedstjekket regner på, i liter pr. døgn: {needed, stockDays, capacity, demand}.
|
||||
-- flows = ADChain.runsView(...); chain = kædemodellen (kan være uden rødder).
|
||||
function ADChainData.dependencyInputs(data, chain, flows)
|
||||
local goods, seen = {}, {}
|
||||
for _, recipe in ipairs(data.recipes or {}) do
|
||||
if not recipe.enabled and not recipe.byChain then
|
||||
for name in pairs(recipe.outputs or {}) do
|
||||
if not seen[name] then seen[name] = true; table.insert(goods, name) end
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(goods)
|
||||
local needed = ADDependency.neededOutputs(flows, data.barnFood, ADChainData.chainInputs(chain, data.fillTitles),
|
||||
#goods > 0 and ADChainData.acceptsMap(data, flows, goods) or {})
|
||||
local adjustment = data.timeAdjustment or 1
|
||||
local capacity, demand, stockDays = {}, {}, {}
|
||||
for name, perMonth in pairs(ADDependency.capacity(data.recipes)) do capacity[name] = perMonth * adjustment end
|
||||
for _, name in ipairs(goods) do
|
||||
local rate = (data.rates or {})[name] or {}
|
||||
local consumption, production = rate.consumption or 0, rate.production or 0
|
||||
local chainDemand = ((chain ~= nil and chain.demand or {})[name] or 0) * adjustment
|
||||
demand[name] = math.max(consumption, chainDemand)
|
||||
if consumption > production then
|
||||
stockDays[name] = ((data.stockByFill or {})[name] or 0) / (consumption - production)
|
||||
elseif consumption > 0 then
|
||||
stockDays[name] = math.huge
|
||||
end
|
||||
end
|
||||
return {needed = needed, stockDays = stockDays, capacity = capacity, demand = demand}
|
||||
end
|
||||
|
||||
-- ---- samlet ----
|
||||
|
||||
-- byChain = {[recipeKey] = true} for produktioner kæden selv har tændt (ikke rødder).
|
||||
-- data.partial = true når noget ikke kunne læses i dette kald; data.fieldsIncomplete = true mens markscanningen
|
||||
-- data.partial = true når noget ikke kunne læses i dette kald (data.readFailed) eller markscanningen kører; data.fieldsIncomplete = true mens markscanningen
|
||||
-- ikke har nået alle marker.
|
||||
function ADChainData.collect(adEnv, farmId, byChain)
|
||||
local data = {recipes = {}, husbandry = {}, crops = {}, stock = {}, fields = {}, yields = {}, fruitFillIndex = {},
|
||||
usedFruits = {}, points = {}, fillTitles = {}, fillIndex = {}}
|
||||
usedFruits = {}, points = {}, fillTitles = {}, fillIndex = {}, placeables = {}, barnFood = {}, stockByFill = {}, rates = {}}
|
||||
local nameOf = nameResolver(data)
|
||||
data.priceOf = priceResolver(data)
|
||||
readPlaceables(data, farmId, byChain, nameOf)
|
||||
readCrops(data, nameOf)
|
||||
readStock(data, adEnv, farmId, nameOf)
|
||||
readRates(data, adEnv, farmId, nameOf)
|
||||
readUsedFruits(data, adEnv, farmId, nameOf)
|
||||
readFields(data)
|
||||
return data
|
||||
|
||||
Reference in New Issue
Block a user