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:
2026-09-30 15:08:28 +02:00
parent ba2a3e94f1
commit 979b7f72d5
14 changed files with 488 additions and 14 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ står i [docs/BRUGERMANUAL.md](docs/BRUGERMANUAL.md) (dansk) og [docs/USER_MANUA
## 1.61 — Produktionskæder ## 1.61 — Produktionskæder
- **1.61.0** Produktionskæder: slå et produkt til i en fabrik, og moden holder det kørende helt tilbage til marken. Kæden regner baglæns gennem dine egne fabrikker og stalde til afgrøderne og ser 12 måneder frem. Ny fane **Kæder** med træ pr. rod-produktion, farvet status, fold ud/ind og **[Pause]**/**[Start]**. Marker markeret **Fri til kæden** skifter afgrøde automatisk (højst 2 i minuttet); andre marker får forslag med **[Godkend]**/**[Afvis]** (højst 3 pr. afgrøde) i fanen og som ét samlet punkt i **Kræver handling**. En afgrøde, du selv har valgt, ændres aldrig. Mellemled tændes og slukkes kun igen, hvis kæden tændte dem. Flows **Kæde: <vare> → <bygning>** oprettes med en ledig pool-traktor og tipvogn (ellers stoppet: *mangler traktor/vogn*; paller altid stoppet) og stoppes, ikke slettes, når kæden slås fra. Dine egne tændte produktioner bruges som kilde først; slukker du en produktion, tænder kæden den ikke igen, før du tænder en ny. Stoppede kædeflows slippes af kæden. Ingen handlinger de første 2 minutter, ingen markskift før alle marker er scannet, og intet slukkes ved delvise læsefejl. Begrænsninger: intet køb, ingen dyrekøb, flerårige afgrøder og græsmarker skiftes aldrig, grundudbytte uden gødningsbonus. **Vigtigt:** hver produktion, du tænder i en fabrik, prøver moden at forsyne — tænd kun det, du vil have produceret; sluk produktionen eller tryk [Pause] for at stoppe kæden. Manglende led forklares i klart sprog med råd (*Ingen af dine fabrikker laver Mel — byg en eller køb*, *Mølle kan kun lave 800 af 1.0k/md*, *Dine stalde leverer ikke nok Mælk …*, *… er slukket af dig*, *Kredsløb omkring …*): som rød tekst under varen i fanen Kæder, som én rød linje pr. kæde i **Kræver handling** (højst 2; klik åbner kæden foldet ud) og som engangsbesked med kædens navn foran, når et nyt led mangler (ikke i opstartsfristen eller mens markerne scannes). Nyt **statusvindue** på skærmen (i stil med Simple Inspector), som slås til/fra med **venstre Alt + S** (kan ændres under Styring; virker til fods og i køretøjer): øverst til højre under info-boksen vises flow-traktorernes handling og fyld, markarbejde i gang, kæder med manglende led og advarsler (køretøjer der har stået stille over 2 min, ventende kædeforslag) — højst 18 linjer, opdateres hvert sekund, skjules ved menu/dialog, skjult HUD og søvn; tændt/slukket gemmes pr. bruger i `modSettings/FS25_ADSmartPickup/status.xml`. Manual og ordliste opdateret. - **1.61.0** Produktionskæder: slå et produkt til i en fabrik, og moden holder det kørende helt tilbage til marken. Kæden regner baglæns gennem dine egne fabrikker og stalde til afgrøderne og ser 12 måneder frem. Ny fane **Kæder** med træ pr. rod-produktion, farvet status, fold ud/ind og **[Pause]**/**[Start]**. Marker markeret **Fri til kæden** skifter afgrøde automatisk (højst 2 i minuttet); andre marker får forslag med **[Godkend]**/**[Afvis]** (højst 3 pr. afgrøde) i fanen og som ét samlet punkt i **Kræver handling**. En afgrøde, du selv har valgt, ændres aldrig. Mellemled tændes og slukkes kun igen, hvis kæden tændte dem. Flows **Kæde: <vare> → <bygning>** oprettes med en ledig pool-traktor og tipvogn (ellers stoppet: *mangler traktor/vogn*; paller altid stoppet) og stoppes, ikke slettes, når kæden slås fra. Dine egne tændte produktioner bruges som kilde først; slukker du en produktion, tænder kæden den ikke igen, før du tænder en ny. Stoppede kædeflows slippes af kæden. Ingen handlinger de første 2 minutter, ingen markskift før alle marker er scannet, og intet slukkes ved delvise læsefejl. Begrænsninger: intet køb, ingen dyrekøb, flerårige afgrøder og græsmarker skiftes aldrig, grundudbytte uden gødningsbonus. **Vigtigt:** hver produktion, du tænder i en fabrik, prøver moden at forsyne — tænd kun det, du vil have produceret; sluk produktionen eller tryk [Pause] for at stoppe kæden. Manglende led forklares i klart sprog med råd (*Ingen af dine fabrikker laver Mel — byg en eller køb*, *Mølle kan kun lave 800 af 1.0k/md*, *Dine stalde leverer ikke nok Mælk …*, *… er slukket af dig*, *Kredsløb omkring …*): som rød tekst under varen i fanen Kæder, som én rød linje pr. kæde i **Kræver handling** (højst 2; klik åbner kæden foldet ud) og som engangsbesked med kædens navn foran, når et nyt led mangler (ikke i opstartsfristen eller mens markerne scannes). Nyt **statusvindue** på skærmen (i stil med Simple Inspector), som slås til/fra med **venstre Alt + S** (kan ændres under Styring; virker til fods og i køretøjer): øverst til højre under info-boksen vises flow-traktorernes handling og fyld, markarbejde i gang, kæder med manglende led og advarsler (slukkede nødvendige fabrikker, køretøjer der har stået stille over 2 min, ventende kædeforslag) — højst 18 linjer, opdateres hvert sekund, skjules ved menu/dialog, skjult HUD og søvn; tændt/slukket gemmes pr. bruger i `modSettings/FS25_ADSmartPickup/status.xml`. **Advarsel når en nødvendig fabrik slukkes** (også uden kæder): slukker du en produktion, hvis vare et tændt flow kører (valgt vare eller målet tager den i auto), en stald æder eller en aktiv kæde bruger, kommer én besked pr. slukning (*Advarsel: du slukkede Fermenteringssilo 3 (Ensilage) — Cow Food, Goat Food +1 bruger det. Lageret rækker ca. 12 dage.*) og en rød linje *Slukket men nødvendig: …* i **Kræver handling** (højst 2; klik åbner fanen Fabrikker, eller Kæder hvis kun en kæde bruger varen) og i statusvinduet, så længe den er slukket og nødvendig. Ingen advarsel når en anden tændt produktion dækker med nok kapacitet, lageret rækker over 60 dage, kæden selv slukkede den, eller den allerede var slukket ved indlæsning; produktioner ingen bruger, advarer aldrig. Manual og ordliste opdateret.
## 1.60 — "Kræver handling" → flow, menuplacering, flow-vogne i guiden ## 1.60 — "Kræver handling" → flow, menuplacering, flow-vogne i guiden
+55 -1
View File
@@ -1,6 +1,7 @@
-- AD Chain (kæde-kørsel) -- AD Chain (kæde-kørsel)
-- Hvert minut (efter opstartsfristen): læs gården (ADChainData), byg kæden (ADChainModel), find manglende -- Hvert minut (efter opstartsfristen): læs gården (ADChainData), byg kæden (ADChainModel), find manglende
-- afgrøder (ADCropPlan) og lad ADChainRun skifte frie marker, tænde/slukke mellemled og oprette/stoppe flows. -- afgrøder (ADCropPlan) og lad ADChainRun skifte frie marker, tænde/slukke mellemled og oprette/stoppe flows.
-- Samme tick advarer (ADDependency), når spilleren slukker en produktion gården er afhængig af — også uden kæder.
-- Resultatet gemmes i ADChain.last til Kæder-fanen. Tilstanden (byChain, pausede rødder, afviste forslag, -- Resultatet gemmes i ADChain.last til Kæder-fanen. Tilstanden (byChain, pausede rødder, afviste forslag,
-- userOff og det sidst kendte sæt af rødder) gemmes i ADSmartPickup_chain.xml under spillets gem. -- userOff og det sidst kendte sæt af rødder) gemmes i ADSmartPickup_chain.xml under spillets gem.
-- Spiladgang kun inde i funktioner (filen dofile's i test). -- Spiladgang kun inde i funktioner (filen dofile's i test).
@@ -31,6 +32,9 @@ ADChain.timer = 0
ADChain.collectFailed = false ADChain.collectFailed = false
-- {[rodnøgle|vare|art] = true}: manglende led der allerede er meldt som besked (kun i hukommelsen; nulstilles ved indlæsning) -- {[rodnøgle|vare|art] = true}: manglende led der allerede er meldt som besked (kun i hukommelsen; nulstilles ved indlæsning)
ADChain.problemKeys = {} ADChain.problemKeys = {}
-- {[nøgle] = true}: produktioner (ikke kædens) der var tændt ved sidste afhængighedstjek (kun kædens tick, kun på
-- hele data). nil efter indlæsning: det der allerede er slukket ved første tjek, advares der ikke om.
ADChain.depPrevEnabled = nil
local function contains(list, value) local function contains(list, value)
for _, item in ipairs(list or {}) do if item == value then return true end end for _, item in ipairs(list or {}) do if item == value then return true end end
@@ -320,6 +324,30 @@ local function applyReconcile(state, data)
ADChain.afterLoad = false ADChain.afterLoad = false
end end
-- Afhængighedstjek (spec afsnit 8): nye slukninger (kun act) og "Slukket men nødvendig"-posterne. Uafhængigt af
-- kædens rødder og af markscanningen; springes over ved læsefejl (så beholdes forrige poster, og slukninger i
-- mellemtiden meldes ved næste hele tjek).
-- Produktioner kæden selv er ved at tænde (plan.prod.enable), er ikke "slukket men nødvendig".
local function dependencyCheck(data, plan, runs, act, previous)
if ADDependency == nil or ADChainData.dependencyInputs == nil then return nil end
if data.readFailed then
return previous ~= nil and {warnings = {}, offNeeded = previous.offNeeded or {}} or nil
end
local inputs = ADChainData.dependencyInputs(data, plan.chain, runs)
local enabling, recipes = {}, {}
for _, key in ipairs(plan.prod ~= nil and plan.prod.enable or {}) do enabling[key] = true end
for _, recipe in ipairs(data.recipes or {}) do
if not enabling[recipe.key] then table.insert(recipes, recipe) end
end
local prev = act and ADChain.depPrevEnabled or nil
local result = {
warnings = ADDependency.warnings(prev, recipes, inputs.needed, inputs.stockDays, inputs.capacity, inputs.demand),
offNeeded = ADDependency.offNeeded(recipes, inputs.needed, inputs.stockDays, inputs.capacity, inputs.demand),
}
if act then ADChain.depPrevEnabled = ADDependency.enabledKeys(data.recipes) end
return result
end
-- act = true: udfør handlingerne (tick). Uden act kun en frisk visning (GUI). -- act = true: udfør handlingerne (tick). Uden act kun en frisk visning (GUI).
function ADChain.evaluate(adEnv, act) function ADChain.evaluate(adEnv, act)
local state = ADChain.state local state = ADChain.state
@@ -341,11 +369,17 @@ function ADChain.evaluate(adEnv, act)
end end
if act and ADChainRun ~= nil then ADChainRun.apply(adEnv, data, result) end if act and ADChainRun ~= nil then ADChainRun.apply(adEnv, data, result) end
local runs = ADChain.runsView(ADRunsController.store.runs) local runs = ADChain.runsView(ADRunsController.store.runs)
local okDep, dependency = pcall(dependencyCheck, data, result, runs, act, ADChain.last ~= nil and ADChain.last.dependency or nil)
if not okDep then
Logging.warning(ADChain.LOG_PREFIX .. "afhængighedstjek fejlede: %s", tostring(dependency))
dependency = nil
end
local paused = {} local paused = {}
for key in pairs(state.paused) do paused[key] = true end for key in pairs(state.paused) do paused[key] = true end
ADChain.last = {chain = result.chain, shortages = result.shortages, actions = result.actions, edges = result.edges, ADChain.last = {chain = result.chain, shortages = result.shortages, actions = result.actions, edges = result.edges,
missingFlows = ADChainActions.missingFlows(result.edges, runs), flowStatus = ADChain.flowStatus(result.edges, runs, ADChain.noVehicle), missingFlows = ADChainActions.missingFlows(result.edges, runs), flowStatus = ADChain.flowStatus(result.edges, runs, ADChain.noVehicle),
paused = paused, userOff = copySet(state.userOff), data = data, fields = data.fields, fieldsIncomplete = data.fieldsIncomplete == true, titles = data.fillTitles, at = ADChain.now, switchCount = ADChain.switchCount} paused = paused, userOff = copySet(state.userOff), data = data, fields = data.fields, fieldsIncomplete = data.fieldsIncomplete == true, titles = data.fillTitles, at = ADChain.now, switchCount = ADChain.switchCount,
dependency = dependency}
return ADChain.last return ADChain.last
end end
@@ -378,6 +412,23 @@ function ADChain.notifyProblems(last)
return #fresh return #fresh
end end
-- Én besked pr. slukning af en nødvendig produktion (kun fra tick; advarslerne tømmes, når de er meldt).
function ADChain.notifyDependencies(last)
local dependency = last ~= nil and last.dependency or nil
if dependency == nil or #(dependency.warnings or {}) == 0 or ADChain.now < ADChain.STARTUP_GRACE_MS then return 0 end
local warnings = dependency.warnings
dependency.warnings = {}
if ADFieldJobs == nil or ADFieldJobs.notify == nil then return 0 end
local textOf = function(key) return g_i18n ~= nil and g_i18n:getText(key) or key end
local titleOf = function(name) return tostring((last.titles or {})[name] or name) end
for _, warning in ipairs(warnings) do
local text = ADDependency.notifyText(warning, textOf, titleOf)
Logging.info(ADChain.LOG_PREFIX .. "nødvendig produktion slukket: %s", text)
ADFieldJobs.notify(text, true)
end
return #warnings
end
function ADChain.tick(adEnv, dt) function ADChain.tick(adEnv, dt)
if adEnv == nil then return end if adEnv == nil then return end
ADChain.now = ADChain.now + (dt or 0) ADChain.now = ADChain.now + (dt or 0)
@@ -387,6 +438,8 @@ function ADChain.tick(adEnv, dt)
local last = ADChain.evaluate(adEnv, true) local last = ADChain.evaluate(adEnv, true)
local ok, err = pcall(ADChain.notifyProblems, last) local ok, err = pcall(ADChain.notifyProblems, last)
if not ok then Logging.warning(ADChain.LOG_PREFIX .. "besked om manglende led fejlede: %s", tostring(err)) end if not ok then Logging.warning(ADChain.LOG_PREFIX .. "besked om manglende led fejlede: %s", tostring(err)) end
local okDep, errDep = pcall(ADChain.notifyDependencies, last)
if not okDep then Logging.warning(ADChain.LOG_PREFIX .. "advarsel om slukket produktion fejlede: %s", tostring(errDep)) end
end end
-- ---- GUI-handlinger ---- -- ---- GUI-handlinger ----
@@ -482,6 +535,7 @@ function ADChain.load()
ADChain.noVehicle = {} ADChain.noVehicle = {}
ADChain.prevEnabled, ADChain.afterLoad = nil, true ADChain.prevEnabled, ADChain.afterLoad = nil, true
ADChain.problemKeys = {} ADChain.problemKeys = {}
ADChain.depPrevEnabled = nil
local path = getPath() local path = getPath()
if path == nil or not fileExists(path) then return end if path == nil or not fileExists(path) then return end
local xmlFile = XMLFile.load("ADSmartPickupChain", path) local xmlFile = XMLFile.load("ADSmartPickupChain", path)
+127 -4
View File
@@ -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 -- 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. -- 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, ...) local function failed(data, key, fmt, ...)
data.partial = true data.partial, data.readFailed = true, true
ADChainData.warnOnce(key, fmt, ...) ADChainData.warnOnce(key, fmt, ...)
end end
@@ -118,11 +119,27 @@ local function readHerd(placeable, nameOf)
return {building = uid, buildingName = placeableName(placeable, uid), outputs = outputs} return {building = uid, buildingName = placeableName(placeable, uid), outputs = outputs}
end 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 function readPlaceables(data, farmId, byChain, nameOf)
local placeables = g_currentMission ~= nil and g_currentMission.placeableSystem ~= nil and g_currentMission.placeableSystem.placeables or {} local placeables = g_currentMission ~= nil and g_currentMission.placeableSystem ~= nil and g_currentMission.placeableSystem.placeables or {}
for index, placeable in pairs(placeables) do for index, placeable in pairs(placeables) do
local ok, err = pcall(function() local ok, err = pcall(function()
if not ADPrices.isOwnedBy(placeable, farmId) then return end 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 local point = placeable.spec_productionPoint ~= nil and placeable.spec_productionPoint.productionPoint or nil
if point ~= nil then if point ~= nil then
local recipes, points = readFactory(placeable, point, byChain, nameOf) 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 elseif placeable.spec_husbandry ~= nil or placeable.getHusbandryFillLevel ~= nil then
local herd = readHerd(placeable, nameOf) local herd = readHerd(placeable, nameOf)
if herd ~= nil then table.insert(data.husbandry, herd) end if herd ~= nil then table.insert(data.husbandry, herd) end
readBarnFood(data, placeable, nameOf)
end end
end) end)
if not ok then if not ok then
@@ -182,13 +200,30 @@ end
local function readStock(data, adEnv, farmId, nameOf) local function readStock(data, adEnv, farmId, nameOf)
local ok, err = pcall(function() local ok, err = pcall(function()
for index, liters in pairs(ADStorageStats.farmTotals(ADSources.getFarmInventory(adEnv, farmId))) do 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 if fruit ~= nil then data.stock[fruit] = (data.stock[fruit] or 0) + liters end
end end
end) end)
if not ok then failed(data, "stock", "lager kunne ikke læses: %s", tostring(err)) end if not ok then failed(data, "stock", "lager kunne ikke læses: %s", tostring(err)) end
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 -- 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). -- læses: feedOnly springer fabrikkernes rater over — dem dækker opskrifterne allerede).
local function readUsedFruits(data, adEnv, farmId, nameOf) local function readUsedFruits(data, adEnv, farmId, nameOf)
@@ -231,19 +266,107 @@ local function readFields(data)
end end
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 ---- -- ---- samlet ----
-- byChain = {[recipeKey] = true} for produktioner kæden selv har tændt (ikke rødder). -- 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. -- ikke har nået alle marker.
function ADChainData.collect(adEnv, farmId, byChain) function ADChainData.collect(adEnv, farmId, byChain)
local data = {recipes = {}, husbandry = {}, crops = {}, stock = {}, fields = {}, yields = {}, fruitFillIndex = {}, 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) local nameOf = nameResolver(data)
data.priceOf = priceResolver(data) data.priceOf = priceResolver(data)
readPlaceables(data, farmId, byChain, nameOf) readPlaceables(data, farmId, byChain, nameOf)
readCrops(data, nameOf) readCrops(data, nameOf)
readStock(data, adEnv, farmId, nameOf) readStock(data, adEnv, farmId, nameOf)
readRates(data, adEnv, farmId, nameOf)
readUsedFruits(data, adEnv, farmId, nameOf) readUsedFruits(data, adEnv, farmId, nameOf)
readFields(data) readFields(data)
return data return data
+160
View File
@@ -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
+12
View File
@@ -963,6 +963,18 @@ function ADSources.getFarmRates(adEnv, farmId, feedOnly)
return rates return rates
end end
-- Én stalds forbrug (foder efter trugets indhold, halm, vand): {[fillType] = liter} kun for consumption > 0.
-- Rå rate uden timeAdjustment (som getHusbandryOutputs).
function ADSources.getHusbandryConsumption(placeable)
local rates, result = {}, {}
if placeable == nil then return result end
pcall(addHusbandryRates, rates, placeable)
for fillType, rate in pairs(rates) do
if (rate.consumption or 0) > 0 then result[fillType] = rate.consumption end
end
return result
end
-- Én stalds produktion (mælk, gødning, ...): {[fillType] = liter} kun for production > 0. Spillets rå rate -- Én stalds produktion (mælk, gødning, ...): {[fillType] = liter} kun for production > 0. Spillets rå rate
-- (pr. måned = én 24-timers dag) UDEN timeAdjustment — samme enhed som fabrikkernes cyklusser pr. måned i kæden. -- (pr. måned = én 24-timers dag) UDEN timeAdjustment — samme enhed som fabrikkernes cyklusser pr. måned i kæden.
function ADSources.getHusbandryOutputs(placeable) function ADSources.getHusbandryOutputs(placeable)
+6 -2
View File
@@ -168,10 +168,11 @@ function ADStatusHud.fieldEntries(proxy)
return entries return entries
end end
-- Kæder med manglende led + ventende forslag (samme punkter og tekster som "Kræver handling"). -- Kæder med manglende led + ventende forslag + slukkede nødvendige fabrikker (samme punkter og tekster som
-- "Kræver handling"; de to sidste står under Advarsler).
function ADStatusHud.chainEntries(proxy) function ADStatusHud.chainEntries(proxy)
local chains, suggestions = {}, {} local chains, suggestions = {}, {}
local result = {chains = chains, suggestions = suggestions} local result = {chains = chains, suggestions = suggestions, dependencies = {}}
if ADChain == nil or ADChain.last == nil or ADStorageStats == nil or ADStorageStats.chainEntries == nil or proxy == nil then if ADChain == nil or ADChain.last == nil or ADStorageStats == nil or ADStorageStats.chainEntries == nil or proxy == nil then
return result return result
end end
@@ -180,6 +181,8 @@ function ADStatusHud.chainEntries(proxy)
table.insert(chains, {text = proxy:alertText(entry), level = "error"}) table.insert(chains, {text = proxy:alertText(entry), level = "error"})
elseif entry.kind == "chainSuggest" then elseif entry.kind == "chainSuggest" then
table.insert(suggestions, {text = proxy:alertText(entry), level = "waiting"}) table.insert(suggestions, {text = proxy:alertText(entry), level = "waiting"})
elseif entry.kind == "dependencyOff" then
table.insert(result.dependencies, {text = proxy:alertText(entry), level = "error"})
end end
end end
return result return result
@@ -212,6 +215,7 @@ function ADStatusHud.snapshot(adEnv)
snapshot.fields = guarded("fields", ADStatusHud.fieldEntries, proxy) or {} snapshot.fields = guarded("fields", ADStatusHud.fieldEntries, proxy) or {}
local chainResult = guarded("chains", ADStatusHud.chainEntries, proxy) or {} local chainResult = guarded("chains", ADStatusHud.chainEntries, proxy) or {}
snapshot.chains = chainResult.chains or {} snapshot.chains = chainResult.chains or {}
for _, entry in ipairs(chainResult.dependencies or {}) do table.insert(snapshot.warnings, entry) end
for _, entry in ipairs(guarded("stuck", ADStatusHud.stuckEntries) or {}) do table.insert(snapshot.warnings, entry) end for _, entry in ipairs(guarded("stuck", ADStatusHud.stuckEntries) or {}) do table.insert(snapshot.warnings, entry) end
for _, entry in ipairs(chainResult.suggestions or {}) do table.insert(snapshot.warnings, entry) end for _, entry in ipairs(chainResult.suggestions or {}) do table.insert(snapshot.warnings, entry) end
return snapshot return snapshot
+27
View File
@@ -336,9 +336,36 @@ local function chainProblemEntries(last)
return result return result
end end
-- "Slukket men nødvendig" (ADChain.last.dependency.offNeeded): én rød post pr. fabrik, højst MAX_DEPENDENCY_ENTRIES
-- (den sidste får more = antal skjulte). Score DEPENDENCY_SCORE + hast (0-99, færre lagerdage = højere; ukendt = 50):
-- over manglende led (3500-3599), under "rød tom" (3900-4000).
ADStorageStats.DEPENDENCY_SCORE = 3700
ADStorageStats.MAX_DEPENDENCY_ENTRIES = 2
local function urgency(days)
if days == nil then return 50 end
if days == math.huge then return 0 end
return math.max(0, 99 - math.min(99, math.floor(days)))
end
function ADStorageStats.dependencyEntries(last)
local entries = {}
for _, item in ipairs(last ~= nil and last.dependency ~= nil and last.dependency.offNeeded or {}) do
local name = tostring(item.buildingName or item.building or "?")
table.insert(entries, {kind = "dependencyOff", level = "red", score = ADStorageStats.DEPENDENCY_SCORE + urgency(item.days),
building = item.building, buildingName = name, fillType = item.fillType, onlyChain = item.onlyChain == true, text = name})
end
entries = ADStorageStats.rankAttention(entries)
local result = {}
for index = 1, math.min(#entries, ADStorageStats.MAX_DEPENDENCY_ENTRIES) do result[index] = entries[index] end
if #entries > #result then result[#result].more = #entries - #result end
return result
end
function ADStorageStats.chainEntries(last) function ADStorageStats.chainEntries(last)
if last == nil then return {} end if last == nil then return {} end
local result = chainProblemEntries(last) local result = chainProblemEntries(last)
for _, entry in ipairs(ADStorageStats.dependencyEntries(last)) do table.insert(result, entry) end
local count = 0 local count = 0
for _, action in ipairs(last.actions or {}) do for _, action in ipairs(last.actions or {}) do
if action.mode == "suggest" then count = count + 1 end if action.mode == "suggest" then count = count + 1 end
@@ -4,6 +4,14 @@
-- siges det at et flow ikke hjælper (producér/køb). Afgørelsen er ADStorageStats.flowAdvice (testet). -- siges det at et flow ikke hjælper (producér/køb). Afgørelsen er ADStorageStats.flowAdvice (testet).
-- Funktionerne lægges på SmartPickupFrame (indlæses efter SmartPickupFrame.lua). -- Funktionerne lægges på SmartPickupFrame (indlæses efter SmartPickupFrame.lua).
-- Lagerfanen for en bygningstype (TAB_KINDS), fx "factory" -> fanen Fabrikker.
function SmartPickupFrame.tabOfKind(kind)
for tab, tabKind in pairs(SmartPickupFrame.TAB_KINDS or {}) do
if tabKind == kind then return tab end
end
return SmartPickupFrame.TAB_OVERVIEW
end
local function fillTypeName(fillType) local function fillTypeName(fillType)
return g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(fillType) or tostring(fillType) return g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(fillType) or tostring(fillType)
end end
@@ -64,6 +72,8 @@ end
function SmartPickupFrame:attentionAdvice(entry) function SmartPickupFrame:attentionAdvice(entry)
-- kæde-forslag og manglende led har ingen vare (stat): klik fører til fanen Kæder -- kæde-forslag og manglende led har ingen vare (stat): klik fører til fanen Kæder
if entry ~= nil and (entry.kind == "chainSuggest" or entry.kind == "chainProblem") then return "chain", nil end if entry ~= nil and (entry.kind == "chainSuggest" or entry.kind == "chainProblem") then return "chain", nil end
-- slukket men nødvendig: fanen Fabrikker (Kæder når kun en kæde bruger varen)
if entry ~= nil and entry.kind == "dependencyOff" then return entry.onlyChain and "chain" or "factories", nil end
if entry ~= nil and entry.kind == "sell" and entry.stat ~= nil then return sellAdviceFor(entry.stat) end if entry ~= nil and entry.kind == "sell" and entry.stat ~= nil then return sellAdviceFor(entry.stat) end
if entry == nil or entry.kind ~= "empty" or entry.stat == nil then return "none", nil end if entry == nil or entry.kind ~= "empty" or entry.stat == nil then return "none", nil end
local stat = entry.stat local stat = entry.stat
@@ -86,6 +96,7 @@ end
function SmartPickupFrame:attentionHint(advice, info) function SmartPickupFrame:attentionHint(advice, info)
if advice == "chain" then return self.i18n:getText("spu_attention_hintChain") end if advice == "chain" then return self.i18n:getText("spu_attention_hintChain") end
if advice == "factories" then return self.i18n:getText("spu_attention_hintFactories") end
if advice == "open" then return string.format(self.i18n:getText("spu_attention_hintOpen"), tostring(info.run.name)) end if advice == "open" then return string.format(self.i18n:getText("spu_attention_hintOpen"), tostring(info.run.name)) end
if advice == "flow" then return self.i18n:getText(info.sell and "spu_attention_hintSellFlow" or "spu_attention_hintFlow") end if advice == "flow" then return self.i18n:getText(info.sell and "spu_attention_hintSellFlow" or "spu_attention_hintFlow") end
if advice == "produce" then return self.i18n:getText("spu_attention_hintProduce") end if advice == "produce" then return self.i18n:getText("spu_attention_hintProduce") end
@@ -103,6 +114,10 @@ function SmartPickupFrame:onAttentionClick(entry)
frame:switchTab(SmartPickupFrame.TAB_CHAINS) frame:switchTab(SmartPickupFrame.TAB_CHAINS)
return return
end end
if entry ~= nil and entry.kind == "dependencyOff" then
frame:switchTab(entry.onlyChain and SmartPickupFrame.TAB_CHAINS or SmartPickupFrame.tabOfKind("factory"))
return
end
local adEnv = frame.getAdEnv() local adEnv = frame.getAdEnv()
local advice, info = frame:attentionAdvice(entry) local advice, info = frame:attentionAdvice(entry)
if adEnv == nil or advice == "none" then return end if adEnv == nil or advice == "none" then return end
@@ -183,10 +183,15 @@ function SmartPickupFrame:addKpiRow(stats)
return row return row
end end
-- Varepunkterne + kædens punkter (røde manglende led, højst 2, og ét samlet punkt for afgrødeforslag; ADChain.last, -- Varepunkterne + kædens punkter (røde manglende led, højst 2, røde "Slukket men nødvendig", højst 2, og ét samlet
-- ingen evaluering her), samme loft MAX_ATTENTION. -- punkt for afgrødeforslag), samme loft MAX_ATTENTION. ADChain.last genbruges; er det ældre end CHAIN_MAX_AGE_MS,
-- evalueres der (visning, handler ikke), så en slukket nødvendig fabrik ses straks.
function SmartPickupFrame:attentionFor(stats) function SmartPickupFrame:attentionFor(stats)
local attention = ADStorageStats.attentionList(stats, SmartPickupFrame.MAX_ATTENTION) local attention = ADStorageStats.attentionList(stats, SmartPickupFrame.MAX_ATTENTION)
if ADChain ~= nil and ADChain.evaluate ~= nil and self.chainLast ~= nil and self.getAdEnv ~= nil then
local okEnv, adEnv = pcall(self.getAdEnv)
if okEnv and adEnv ~= nil then pcall(self.chainLast, self, adEnv) end
end
if ADChain == nil or ADChain.last == nil then return attention end if ADChain == nil or ADChain.last == nil then return attention end
return ADStorageStats.mergeAttention(attention, ADStorageStats.chainEntries(ADChain.last), SmartPickupFrame.MAX_ATTENTION) return ADStorageStats.mergeAttention(attention, ADStorageStats.chainEntries(ADChain.last), SmartPickupFrame.MAX_ATTENTION)
end end
@@ -201,6 +206,11 @@ function SmartPickupFrame:alertText(entry)
if (entry.more or 0) > 0 then text = text .. string.format(" (+%d)", entry.more) end if (entry.more or 0) > 0 then text = text .. string.format(" (+%d)", entry.more) end
return text return text
end end
if entry.kind == "dependencyOff" then
local text = string.format(i18n:getText("spu_attention_depOff"), tostring(entry.buildingName))
if (entry.more or 0) > 0 then text = text .. string.format(" (+%d)", entry.more) end
return text
end
if entry.kind == "empty" then if entry.kind == "empty" then
return string.format(i18n:getText("spu_alert_empty"), stat.title, self:formatDays(stat.days), fc(stat.farmLevel or 0), fc(math.abs(stat.net or 0))) return string.format(i18n:getText("spu_alert_empty"), stat.title, self:formatDays(stat.days), fc(stat.farmLevel or 0), fc(math.abs(stat.net or 0)))
elseif entry.kind == "full" then elseif entry.kind == "full" then
+4
View File
@@ -632,5 +632,9 @@
<text name="spu_status_act_emptying" text="tømmer vognen"/> <text name="spu_status_act_emptying" text="tømmer vognen"/>
<text name="spu_status_act_homing" text="på vej hjem"/> <text name="spu_status_act_homing" text="på vej hjem"/>
<text name="spu_status_stuck" text="%s står stille (%d min)"/> <text name="spu_status_stuck" text="%s står stille (%d min)"/>
<text name="spu_dep_warn" text="Advarsel: du slukkede %s (%s) — %s bruger det. Lageret rækker ca. %d dage."/>
<text name="spu_dep_warnNoDays" text="Advarsel: du slukkede %s (%s) — %s bruger det."/>
<text name="spu_attention_depOff" text="Slukket men nødvendig: %s"/>
<text name="spu_attention_hintFactories" text=" → klik: se Fabrikker"/>
</texts> </texts>
</l10n> </l10n>
+4
View File
@@ -632,5 +632,9 @@
<text name="spu_status_act_emptying" text="emptying the wagon"/> <text name="spu_status_act_emptying" text="emptying the wagon"/>
<text name="spu_status_act_homing" text="heading home"/> <text name="spu_status_act_homing" text="heading home"/>
<text name="spu_status_stuck" text="%s standing still (%d min)"/> <text name="spu_status_stuck" text="%s standing still (%d min)"/>
<text name="spu_dep_warn" text="Warning: you switched off %s (%s) — %s uses it. Stock lasts about %d days."/>
<text name="spu_dep_warnNoDays" text="Warning: you switched off %s (%s) — %s uses it."/>
<text name="spu_attention_depOff" text="Switched off but needed: %s"/>
<text name="spu_attention_hintFactories" text=" → click: see Factories"/>
</texts> </texts>
</l10n> </l10n>
+1
View File
@@ -58,6 +58,7 @@
<sourceFile filename="adChainModel.lua"/> <sourceFile filename="adChainModel.lua"/>
<sourceFile filename="adCropPlan.lua"/> <sourceFile filename="adCropPlan.lua"/>
<sourceFile filename="adChainActions.lua"/> <sourceFile filename="adChainActions.lua"/>
<sourceFile filename="adDependency.lua"/>
<sourceFile filename="adChainData.lua"/> <sourceFile filename="adChainData.lua"/>
<sourceFile filename="adChain.lua"/> <sourceFile filename="adChain.lua"/>
<sourceFile filename="adChainRun.lua"/> <sourceFile filename="adChainRun.lua"/>
+32 -2
View File
@@ -344,8 +344,9 @@ spillets info-boks. Tasten virker både til fods og i et køretøj og kan ændre
grå = stoppet. grå = stoppet.
- **Marker** — én linje pr. mark med markarbejde i gang: *F92 Høstes · Roeoptager · 45 %* (farve som i Marker-fanen). - **Marker** — én linje pr. mark med markarbejde i gang: *F92 Høstes · Roeoptager · 45 %* (farve som i Marker-fanen).
- **Kæder** — kun kæder med et manglende led: *Brød mangler et led: Mel*. - **Kæder** — kun kæder med et manglende led: *Brød mangler et led: Mel*.
- **Advarsler** — køretøjer der har stået stille i over 2 minutter (uden at Smart Pickup selv holder dem ved - **Advarsler** — fabrikker gården er afhængig af, men som er slukket (*Slukket men nødvendig: Fermenteringssilo 3*,
Wait/udkørsel) og ventende kædeforslag (*Kæde: 2 forslag*). se [Advarsel når en nødvendig fabrik slukkes](#advarsel-når-en-nødvendig-fabrik-slukkes)), køretøjer der har
stået stille i over 2 minutter (uden at Smart Pickup selv holder dem ved Wait/udkørsel) og ventende kædeforslag (*Kæde: 2 forslag*).
Højst 18 linjer; resten samles i *+k mere*. Vinduet skjules, mens en menu eller dialog er åben, når HUD'en er Højst 18 linjer; resten samles i *+k mere*. Vinduet skjules, mens en menu eller dialog er åben, når HUD'en er
skjult, og når du sover. Om vinduet er tændt, gemmes pr. bruger i skjult, og når du sover. Om vinduet er tændt, gemmes pr. bruger i
@@ -687,6 +688,33 @@ Beskederne:
- *Mølle (Mel) er slukket af dig — tænd den eller sæt kæden på pause*: du har selv slukket produktionen. - *Mølle (Mel) er slukket af dig — tænd den eller sæt kæden på pause*: du har selv slukket produktionen.
- *Kredsløb omkring Mel — kæden kan ikke beregnes*: varen bruges til at lave sig selv. - *Kredsløb omkring Mel — kæden kan ikke beregnes*: varen bruges til at lave sig selv.
#### Advarsel når en nødvendig fabrik slukkes
Slukker du en produktion, som gården er afhængig af, advarer moden dig — også når ingen kæde er i gang. En
produktion er nødvendig, når en af dens varer bruges af gården:
- et tændt forsyningsflow kører varen (valgt som vare, eller flowets mål tager varen i auto),
- en stald æder varen lige nu, eller
- en aktiv kæde har varen som input.
Eksempel: du slukker fermenteringssiloerne, men flowene *Cow Food*, *Goat Food* og *Biogas Silage* henter
ensilage. Så kommer beskeden *Advarsel: du slukkede Fermenteringssilo 3 (Ensilage) — Cow Food, Goat Food +1
bruger det. Lageret rækker ca. 12 dage.* (dagene ud fra gårdens lager og forbrug; kendes de ikke, udelades
de). Beskeden kommer én gang pr. slukning, højst et minut efter.
Så længe produktionen er slukket og stadig nødvendig, står der en rød linje i **Kræver handling**:
*Slukket men nødvendig: Fermenteringssilo 3* (højst 2; også i statusvinduet under Advarsler). Et klik åbner
lagerfanen **Fabrikker** — eller **Kæder**, hvis kun en kæde bruger varen.
Der advares ikke, når:
- en anden tændt produktion laver varen med nok kapacitet,
- gården har lager til mere end 60 dage,
- kæden selv slukkede produktionen, eller
- produktionen allerede var slukket, da spillet blev indlæst (så vises kun linjen i Kræver handling).
Produktioner, som ingen flows, stalde eller kæder bruger (fx et bageri uden flow), giver aldrig en advarsel.
#### Markér en mark fri til kæden #### Markér en mark fri til kæden
Åbn marken i markguiden og slå **Fri til kæden (moden må skifte afgrøde)** til. Kun sådan en mark skifter Åbn marken i markguiden og slå **Fri til kæden (moden må skifte afgrøde)** til. Kun sådan en mark skifter
@@ -794,6 +822,7 @@ ens. Øverst vælger du **Vis: Varer | Bygninger**. Fanen husker valget. Tallene
| *Hvede løber tør om … — gården har …, bruger …/døgn* | Gården bruger mere, end den producerer, og varen rækker under 3 døgn (rød under 1 døgn) | | *Hvede løber tør om … — gården har …, bruger …/døgn* | Gården bruger mere, end den producerer, og varen rækker under 3 døgn (rød under 1 døgn) |
| *Mælk er 92 % fuld — fuld om …* | En produceret vare er mindst 85 % fuld (rød fra 95 %) | | *Mælk er 92 % fuld — fuld om …* | En produceret vare er mindst 85 % fuld (rød fra 95 %) |
| *Sælg Raps nu: prisen er 94 % af årets top — …* | Prisen er mindst 90 % af årets top, beholdningen er værd mindst 10.000, og gården bruger den ikke op | | *Sælg Raps nu: prisen er 94 % af årets top — …* | Prisen er mindst 90 % af årets top, beholdningen er værd mindst 10.000, og gården bruger den ikke op |
| *Slukket men nødvendig: Fermenteringssilo 3* | Du har slukket en produktion, som et flow, en stald eller en kæde bruger (se [Advarsel når en nødvendig fabrik slukkes](#advarsel-når-en-nødvendig-fabrik-slukkes)); klik åbner fanen Fabrikker |
**Klik på en linje** for at handle: **Klik på en linje** for at handle:
@@ -1280,4 +1309,5 @@ Moden testes løbende, og fejlrapporter er meget velkomne.
| **Marksilo** | En silo ved en mark, der bruges som mellemlager | | **Marksilo** | En silo ved en mark, der bruges som mellemlager |
| **Kæde** | Den samlede vej fra en produktion tilbage gennem fabrikker og stalde til afgrøderne på markerne | | **Kæde** | Den samlede vej fra en produktion tilbage gennem fabrikker og stalde til afgrøderne på markerne |
| **Rod-produktion** | Den produktion, du selv slår til, og som kæden holder kørende | | **Rod-produktion** | Den produktion, du selv slår til, og som kæden holder kørende |
| **Slukket men nødvendig** | En slukket produktion, hvis vare et flow, en stald eller en kæde bruger — moden advarer én gang og viser en rød linje i Kræver handling |
| **Fri til kæden** | Markering på en mark, der lader moden skifte afgrøde automatisk | | **Fri til kæden** | Markering på en mark, der lader moden skifte afgrøde automatisk |
+32 -2
View File
@@ -349,8 +349,9 @@ that have something in them:
- **Fields** — one line per field with field work in progress: *F92 Harvesting · Beet harvester · 45 %* - **Fields** — one line per field with field work in progress: *F92 Harvesting · Beet harvester · 45 %*
(colours as in the Fields tab). (colours as in the Fields tab).
- **Chains** — only chains with a missing link: *Bread is missing a link: Flour*. - **Chains** — only chains with a missing link: *Bread is missing a link: Flour*.
- **Warnings** — vehicles that have stood still for more than 2 minutes (without Smart Pickup holding them at - **Warnings** — factories the farm depends on that are switched off (*Switched off but needed: Fermenting
Wait/outbound) and pending chain suggestions (*Chain: 2 suggestions*). silo 3*, see [Warning when a needed factory is switched off](#warning-when-a-needed-factory-is-switched-off)),
vehicles that have stood still for more than 2 minutes (without Smart Pickup holding them at Wait/outbound) and pending chain suggestions (*Chain: 2 suggestions*).
At most 18 lines; the rest is summed up as *+k more*. The window is hidden while a menu or dialog is open, At most 18 lines; the rest is summed up as *+k more*. The window is hidden while a menu or dialog is open,
when the HUD is hidden and while you sleep. Whether it is on is saved per user in when the HUD is hidden and while you sleep. Whether it is on is saved per user in
@@ -703,6 +704,33 @@ The messages:
- *Mill (Flour) was turned off by you — turn it on or pause the chain*: you switched the production off yourself. - *Mill (Flour) was turned off by you — turn it on or pause the chain*: you switched the production off yourself.
- *Loop around Flour — the chain cannot be calculated*: the goods are needed to make themselves. - *Loop around Flour — the chain cannot be calculated*: the goods are needed to make themselves.
#### Warning when a needed factory is switched off
If you switch off a production the farm depends on, the mod warns you — also when no chain is running. A
production is needed when one of its goods is used by the farm:
- a running supply flow carries the goods (chosen as goods, or the flow's target takes the goods in auto),
- a barn is eating the goods right now, or
- an active chain has the goods as input.
Example: you switch off the fermenting silos, but the flows *Cow Food*, *Goat Food* and *Biogas Silage* fetch
silage. You then get the message *Warning: you switched off Fermenting silo 3 (Silage) — Cow Food, Goat Food
+1 uses it. Stock lasts about 12 days.* (days from the farm's stock and consumption; left out when unknown).
The message comes once per switch-off, at most a minute later.
As long as the production is off and still needed, **Needs attention** shows a red line: *Switched off but
needed: Fermenting silo 3* (at most 2; also in the status window under Warnings). Clicking opens the storage
tab **Factories** — or **Chains** if only a chain uses the goods.
No warning when:
- another running production makes the goods with enough capacity,
- the farm has stock for more than 60 days,
- the chain switched the production off itself, or
- the production was already off when the game was loaded (then only the Needs attention line is shown).
Productions that no flow, barn or chain uses (for example a bakery without a flow) never give a warning.
#### Mark a field free for chains #### Mark a field free for chains
Open the field in the field guide and switch on **Free for chains (mod may change the crop)**. Only such a Open the field in the field guide and switch on **Free for chains (mod may change the crop)**. Only such a
@@ -814,6 +842,7 @@ every second.
| *Wheat runs out in … — farm has …, uses …/day* | The farm uses more than it produces, and the good lasts less than 3 days (red below 1 day) | | *Wheat runs out in … — farm has …, uses …/day* | The farm uses more than it produces, and the good lasts less than 3 days (red below 1 day) |
| *Milk is 92 % full — full in …* | A produced good is at least 85 % full (red from 95 %) | | *Milk is 92 % full — full in …* | A produced good is at least 85 % full (red from 95 %) |
| *Sell Canola now: price is 94 % of the yearly top — …* | The price is at least 90 % of the yearly top, the stock is worth at least 10,000, and the farm does not use it up | | *Sell Canola now: price is 94 % of the yearly top — …* | The price is at least 90 % of the yearly top, the stock is worth at least 10,000, and the farm does not use it up |
| *Switched off but needed: Fermenting silo 3* | You switched off a production that a flow, a barn or a chain uses (see [Warning when a needed factory is switched off](#warning-when-a-needed-factory-is-switched-off)); clicking opens the Factories tab |
**Click a line** to act on it: **Click a line** to act on it:
@@ -1317,4 +1346,5 @@ The mod is being tested continuously, and bug reports are very welcome.
| **Field silo** | A silo near a field used as intermediate storage | | **Field silo** | A silo near a field used as intermediate storage |
| **Chain** | The whole path from a production back through factories and barns to the crops in the fields | | **Chain** | The whole path from a production back through factories and barns to the crops in the fields |
| **Root production** | The production you switch on yourself, which the chain keeps running | | **Root production** | The production you switch on yourself, which the chain keeps running |
| **Switched off but needed** | A switched-off production whose goods a flow, a barn or a chain uses — the mod warns once and shows a red line in Needs attention |
| **Free for chains** | A field marking that lets the mod change the crop automatically | | **Free for chains** | A field marking that lets the mod change the crop automatically |