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:
@@ -0,0 +1,160 @@
|
||||
-- AD Dependency (ren logik)
|
||||
-- Advarsel når spilleren slukker en produktion gården er afhængig af (spec 2026-09-30-produktionskaeder afsnit 8).
|
||||
-- En vare er "nødvendig" når et tændt forsyningsflow kører den (manuel vare, eller flowets mål tager den i
|
||||
-- auto-tilstand), en stald æder den, eller en aktiv kæde har den som input. Varer er NAVNE; kapacitet og forbrug
|
||||
-- i samme enhed (adapteren i adChain.lua vælger), lagerdage i spillets døgn. Ingen spiladgang.
|
||||
|
||||
ADDependency = {}
|
||||
ADDependency.STOCK_DAYS_OK = 60 -- lager til mere end så mange døgn: ingen advarsel
|
||||
ADDependency.MAX_USERS = 2 -- brugere nævnt i beskeden; resten "+k"
|
||||
|
||||
local function contains(list, value)
|
||||
for _, item in ipairs(list or {}) do if item == value then return true end end
|
||||
return false
|
||||
end
|
||||
|
||||
local function sortedKeys(map)
|
||||
local keys = {}
|
||||
for key in pairs(map or {}) do table.insert(keys, key) end
|
||||
table.sort(keys)
|
||||
return keys
|
||||
end
|
||||
|
||||
-- flows = {{name, kind, stopped, targetBuildingIds, ingredientMode, ingredients}}
|
||||
-- barnFood = {[vare] = {staldnavn...}}, chainInputs = {[vare] = {kædenavn...}}
|
||||
-- accepts = {[bygnings-id] = {[vare] = true}}: hvad auto-flowenes mål tager (adapteren slår op)
|
||||
-- -> {[vare] = {users = {navn...}, onlyChain}} (brugere i rækkefølgen flows, stalde, kæder; hver kun én gang)
|
||||
function ADDependency.neededOutputs(flows, barnFood, chainInputs, accepts)
|
||||
local result = {}
|
||||
local function add(name, user, isChain)
|
||||
if name == nil or user == nil then return end
|
||||
local entry = result[name]
|
||||
if entry == nil then
|
||||
entry = {users = {}, onlyChain = true}
|
||||
result[name] = entry
|
||||
end
|
||||
if not isChain then entry.onlyChain = false end
|
||||
if not contains(entry.users, user) then table.insert(entry.users, user) end
|
||||
end
|
||||
for _, flow in ipairs(flows or {}) do
|
||||
if flow.kind ~= "outbound" and not flow.stopped then
|
||||
local user = tostring(flow.name or "?")
|
||||
if flow.ingredientMode == "manual" then
|
||||
for _, name in ipairs(flow.ingredients or {}) do add(name, user, false) end
|
||||
else
|
||||
for _, building in ipairs(flow.targetBuildingIds or {}) do
|
||||
for _, name in ipairs(sortedKeys((accepts or {})[building])) do add(name, user, false) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for _, name in ipairs(sortedKeys(barnFood)) do
|
||||
for _, barn in ipairs(barnFood[name]) do add(name, tostring(barn), false) end
|
||||
end
|
||||
for _, name in ipairs(sortedKeys(chainInputs)) do
|
||||
for _, root in ipairs(chainInputs[name]) do add(name, tostring(root), true) end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
-- Tændte produktioner der ikke er kædens (samme regel som ADChain.enabledKeys) -> {[nøgle] = true}
|
||||
function ADDependency.enabledKeys(recipes)
|
||||
local result = {}
|
||||
for _, recipe in ipairs(recipes or {}) do
|
||||
if recipe.enabled and not recipe.byChain then result[recipe.key] = true end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
-- Tændte produktioners kapacitet pr. vare (outputs × cyklusser pr. måned) -> {[vare] = mængde}
|
||||
function ADDependency.capacity(recipes)
|
||||
local result = {}
|
||||
for _, recipe in ipairs(recipes or {}) do
|
||||
if recipe.enabled then
|
||||
for name, amount in pairs(recipe.outputs or {}) do
|
||||
result[name] = (result[name] or 0) + (amount or 0) * (recipe.cyclesPerMonth or 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
-- Dækker andre tændte produktioner varen? Ukendt/intet forbrug: enhver kapacitet dækker.
|
||||
local function isCovered(name, otherCapacity, demand)
|
||||
local capacity = (otherCapacity or {})[name] or 0
|
||||
return capacity > 0 and capacity >= ((demand or {})[name] or 0)
|
||||
end
|
||||
|
||||
-- Rækker lageret længe nok? nil = ukendt (advar, uden dage).
|
||||
local function stockLasts(name, stockDays)
|
||||
local days = (stockDays or {})[name]
|
||||
return days ~= nil and days > ADDependency.STOCK_DAYS_OK
|
||||
end
|
||||
|
||||
-- Første nødvendige, udækkede vare (efter navn) for en slukket produktion (ikke kædens) -> advarsel eller nil.
|
||||
local function check(recipe, needed, stockDays, otherCapacity, demand)
|
||||
if recipe.enabled or recipe.byChain then return nil end
|
||||
for _, name in ipairs(sortedKeys(recipe.outputs)) do
|
||||
local need = (needed or {})[name]
|
||||
if need ~= nil and (recipe.outputs[name] or 0) > 0 and not isCovered(name, otherCapacity, demand)
|
||||
and not stockLasts(name, stockDays) then
|
||||
local days = (stockDays or {})[name]
|
||||
return {recipeKey = recipe.key, building = recipe.building, buildingName = recipe.buildingName, fillType = name,
|
||||
users = need.users, onlyChain = need.onlyChain == true, days = days}
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Alle slukkede, nødvendige produktioner (én pr. fabrik) — "Slukket men nødvendig" i Kræver handling.
|
||||
function ADDependency.offNeeded(recipes, needed, stockDays, otherCapacity, demand)
|
||||
local result, seen = {}, {}
|
||||
for _, recipe in ipairs(recipes or {}) do
|
||||
local warning = check(recipe, needed, stockDays, otherCapacity, demand)
|
||||
if warning ~= nil and not seen[tostring(warning.building)] then
|
||||
seen[tostring(warning.building)] = true
|
||||
table.insert(result, warning)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
-- Nye slukninger siden sidste evaluering: tændt i prevEnabled, slukket nu, nødvendig og ikke dækket.
|
||||
-- prevEnabled = nil (første evaluering efter indlæsning) -> ingen. Én pr. fabrik og vare.
|
||||
function ADDependency.warnings(prevEnabled, recipes, needed, stockDays, otherCapacity, demand)
|
||||
local result, seen = {}, {}
|
||||
if prevEnabled == nil then return result end
|
||||
for _, recipe in ipairs(recipes or {}) do
|
||||
if prevEnabled[recipe.key] then
|
||||
local warning = check(recipe, needed, stockDays, otherCapacity, demand)
|
||||
local id = warning ~= nil and (tostring(warning.building) .. "|" .. warning.fillType) or nil
|
||||
if warning ~= nil and not seen[id] then
|
||||
seen[id] = true
|
||||
table.insert(result, warning)
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
-- "A, B +2"
|
||||
function ADDependency.usersText(users)
|
||||
local shown = {}
|
||||
for index = 1, math.min(#(users or {}), ADDependency.MAX_USERS) do shown[index] = tostring(users[index]) end
|
||||
if #shown == 0 then return "?" end
|
||||
local text = table.concat(shown, ", ")
|
||||
if #users > #shown then text = text .. " +" .. tostring(#users - #shown) end
|
||||
return text
|
||||
end
|
||||
|
||||
-- Beskedtekst (spu_dep_warn med dage, spu_dep_warnNoDays uden).
|
||||
function ADDependency.notifyText(warning, textOf, titleOf)
|
||||
local title = tostring(titleOf ~= nil and titleOf(warning.fillType) or warning.fillType)
|
||||
local users = ADDependency.usersText(warning.users)
|
||||
local building = tostring(warning.buildingName or warning.building or "?")
|
||||
local days = warning.days
|
||||
if days ~= nil and days < math.huge then
|
||||
return string.format(textOf("spu_dep_warn"), building, title, users, math.max(0, math.floor(days)))
|
||||
end
|
||||
return string.format(textOf("spu_dep_warnNoDays"), building, title, users)
|
||||
end
|
||||
Reference in New Issue
Block a user