diff --git a/FS25_ADSmartPickup/adChain.lua b/FS25_ADSmartPickup/adChain.lua index 2fdbaec..e91f581 100644 --- a/FS25_ADSmartPickup/adChain.lua +++ b/FS25_ADSmartPickup/adChain.lua @@ -290,11 +290,11 @@ end local function plan(data, state) local recipes = ADChain.modelRecipes(data.recipes, state.paused, state.userOff) - local chain = ADChainModel.build(recipes, data.husbandry, data.crops) + local chain = ADChainModel.build(recipes, data.husbandry, data.crops, data.fieldGoods) -- pausede rødder: det de bruger, er beskyttet mod slukning (modellen regnet som om de ikke var pauset) local protected = {} if next(state.paused) ~= nil then - protected = ADChainModel.build(ADChain.modelRecipes(data.recipes, {}, state.userOff), data.husbandry, data.crops).needRecipes + protected = ADChainModel.build(ADChain.modelRecipes(data.recipes, {}, state.userOff), data.husbandry, data.crops, data.fieldGoods).needRecipes end local used = ADChain.unionFruits(data.usedFruits, chain.fieldDemand) -- ufuldstændig markliste: udbuddet ville være undervurderet -> ingen mangler, forslag eller markskift endnu diff --git a/FS25_ADSmartPickup/adChainData.lua b/FS25_ADSmartPickup/adChainData.lua index 981aa1f..bba9b5e 100644 --- a/FS25_ADSmartPickup/adChainData.lua +++ b/FS25_ADSmartPickup/adChainData.lua @@ -197,6 +197,76 @@ local function readCrops(data, nameOf) end end +-- ---- markvarer (hø, hakkelse, halm …) ---- +-- Varer der høstes/laves på marken uden at være en afgrødes hovedvare. Spillets konvertere hedder forskelligt +-- mellem versioner/kort (FORAGEHARVESTER/FORAGE_HARVESTER), så alle navne prøves; et ukendt navn giver nil. +ADChainData.FIELD_CONVERTERS = {"FORAGEHARVESTER", "FORAGE_HARVESTER", "MOWER", "COMBINE", "CUTTER"} +-- Kendte markvarer (basisspil, FermentingSiloPack, Dakota); kun dem der findes som fill type bruges. +ADChainData.KNOWN_FIELD_GOODS = {"STRAW", "CHAFF", "CROP_WINDROW", "GRASS_WINDROW", "ALFALFA_WINDROW", "CLOVER_WINDROW", + "DRYGRASS_WINDROW", "DRYALFALFA_WINDROW", "DRYCLOVER_WINDROW", "CCM", "CCMRAW", "WCS"} + +-- fruit.windrowFillType kan være et indeks eller en fill type-beskrivelse +local function windrowIndex(fruit) + local windrow = fruit.windrowFillType + if type(windrow) == "number" then return windrow end + if type(windrow) == "table" then + if windrow.index ~= nil then return windrow.index end + if windrow.name ~= nil and g_fillTypeManager.getFillTypeIndexByName ~= nil then + return g_fillTypeManager:getFillTypeIndexByName(windrow.name) + end + end + return nil +end + +-- Én konverters mål-indekser ({[fra] = {targetFillTypeIndex|fillTypeIndex}}, samme form som adFields.buildMowable) +local function converterTargets(converter, add) + for _, entry in pairs(converter or {}) do + if type(entry) == "table" then add(entry.targetFillTypeIndex or entry.fillTypeIndex) end + end +end + +-- data.fieldGoods = {[navn] = true}. Kun eksisterende fill types; fejl logges én gang og gør ikke læsningen delvis +-- (en ændret spil-API må ikke blokere kæden for altid) — værst tænkeligt meldes en markvare som "køb". +local function readFieldGoods(data, nameOf) + local indices = {} + local function add(index) if type(index) == "number" then indices[index] = true end end + local function guarded(key, fn) + local ok, err = pcall(fn) + if not ok then ADChainData.warnOnce("fieldGoods:" .. key, "markvarer (%s) kunne ikke læses: %s", key, tostring(err)) end + end + if g_fillTypeManager == nil then return end + local fruitManager = g_fruitTypeManager + for _, fruit in ipairs(fruitManager ~= nil and fruitManager.fruitTypes or {}) do + guarded("windrow:" .. tostring(fruit.name), function() add(windrowIndex(fruit)) end) + end + if fruitManager ~= nil and fruitManager.getConverterDataByName ~= nil then + for _, name in ipairs(ADChainData.FIELD_CONVERTERS) do + guarded(name, function() converterTargets(fruitManager:getConverterDataByName(name), add) end) + end + end + if g_fillTypeManager.getFillTypeIndexByName ~= nil then + for _, name in ipairs(ADChainData.KNOWN_FIELD_GOODS) do + guarded(name, function() add(g_fillTypeManager:getFillTypeIndexByName(name)) end) + end + end + -- tørrede varianter (vender: græs-vindue -> hø) + if g_fillTypeManager.getConverterDataByName ~= nil then + guarded("TEDDER", function() + local tedder = g_fillTypeManager:getConverterDataByName("TEDDER") or {} + local windrows = {} + for index in pairs(indices) do table.insert(windrows, index) end + for _, index in ipairs(windrows) do + local entry = tedder[index] + if type(entry) == "table" then add(entry.targetFillTypeIndex or entry.fillTypeIndex) end + end + end) + end + for index in pairs(indices) do + local ok, name = pcall(nameOf, index) + if ok and name ~= nil and data.crops[name] == nil then data.fieldGoods[name] = true end + end +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 @@ -369,11 +439,12 @@ end -- 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 = {}, placeables = {}, barnFood = {}, stockByFill = {}, rates = {}} + usedFruits = {}, points = {}, fillTitles = {}, fillIndex = {}, placeables = {}, barnFood = {}, stockByFill = {}, rates = {}, fieldGoods = {}} local nameOf = nameResolver(data) data.priceOf = priceResolver(data) readPlaceables(data, farmId, byChain, nameOf) readCrops(data, nameOf) + readFieldGoods(data, nameOf) readStock(data, adEnv, farmId, nameOf) readRates(data, adEnv, farmId, nameOf) readUsedFruits(data, adEnv, farmId, nameOf) diff --git a/FS25_ADSmartPickup/adChainModel.lua b/FS25_ADSmartPickup/adChainModel.lua index cb9c0c3..9a07c9e 100644 --- a/FS25_ADSmartPickup/adChainModel.lua +++ b/FS25_ADSmartPickup/adChainModel.lua @@ -47,7 +47,9 @@ local function sortedNames(map) return names end -function ADChainModel.build(recipes, husbandry, crops) +-- fieldGoods = {[vare] = true}: varer der høstes/laves på marken uden at være en afgrødes hovedvare (hø, hakkelse, +-- halm …). Uden producent/stald er kilden "field" uden afgrøde: intet markbehov og intet problem. +function ADChainModel.build(recipes, husbandry, crops, fieldGoods) local chain = {roots = {}, demand = {}, fieldDemand = {}, needRecipes = {}, problems = {}} local producers, herds = indexProducers(recipes), indexHerds(husbandry) -- Rødderne kører for sig selv med fuld fart: deres egne input kræves af deres eget træ. Som leverandør til en @@ -136,6 +138,11 @@ function ADChainModel.build(recipes, husbandry, crops) chain.fieldDemand[node.fruit] = (chain.fieldDemand[node.fruit] or 0) + rest rest = 0 end + -- c2) markvare uden egen afgrøde (fx hø, hakkelse): kommer fra markerne, ingen afgrødeplan + if rest > 1e-9 and #node.producers == 0 and #node.herds == 0 and fieldGoods ~= nil and fieldGoods[name] then + node.source, node.fruit = "field", nil + rest = 0 + end -- d) ingen kilde (eller rest efter stald), eller exhausted producers if rest > 1e-9 and makers ~= nil and #node.producers == 0 and #node.herds == 0 then node.source = "production" diff --git a/FS25_ADSmartPickup/adChainView.lua b/FS25_ADSmartPickup/adChainView.lua index 3c7d1b2..cc99382 100644 --- a/FS25_ADSmartPickup/adChainView.lua +++ b/FS25_ADSmartPickup/adChainView.lua @@ -22,7 +22,7 @@ ADChainView.TIGHT_MONTHS = 3 -- nøgler rækkerne bruger (til l10n-testen) ADChainView.TEXT_KEYS = {"spu_chain_empty", "spu_chain_approve", "spu_chain_dismiss", "spu_chain_auto", "spu_chain_flowOk", "spu_chain_flowMissing", "spu_chain_flowPallet", "spu_chain_flowNone", "spu_chain_flowStopped", "spu_chain_bottleneck", - "spu_chain_buy", "spu_chain_cycle", "spu_chain_fieldShort", "spu_chain_tight", "spu_chain_noField", "spu_chain_buySource", + "spu_chain_buy", "spu_chain_cycle", "spu_chain_fieldShort", "spu_chain_tight", "spu_chain_noField", "spu_chain_buySource", "spu_chain_fromFields", "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", @@ -94,7 +94,14 @@ end local function stockOf(last, fruit) local stock = last.data ~= nil and last.data.stock or nil - return stock ~= nil and stock[fruit] or nil + return stock ~= nil and fruit ~= nil and stock[fruit] or nil +end + +-- Lager for en mark-node: afgrødens lager, eller varens eget lager for en markvare uden afgrøde (hø, hakkelse …). +local function fieldStockOf(last, node) + if node.fruit ~= nil then return stockOf(last, node.fruit) end + local byFill = last.data ~= nil and last.data.stockByFill or nil + return byFill ~= nil and byFill[node.fillType] or nil end -- Mark-afgrøde uden mangel men med lager under TIGHT_MONTHS måneders behov. @@ -187,6 +194,8 @@ local function sourceText(node, last, textOf) elseif node.source == "husbandry" then for _, herd in ipairs(node.herds or {}) do table.insert(names, tostring(herd.buildingName)) end elseif node.source == "field" then + -- markvare uden egen afgrøde (hø, hakkelse …): ingen afgrødeplan, bare "fra marker" + if node.fruit == nil then return textOf("spu_chain_fromFields") end for _, field in ipairs(last.fields or {}) do if plannedFruit(field) == node.fruit then table.insert(names, "F" .. tostring(field.id)) end end @@ -294,7 +303,7 @@ local function rootRows(rows, root, last, ctx, isExpanded) end eachNode(root.children, function(node, depth) local text, nodeLevel = nodeStatus(node, last, ctx.fmt, ctx.textOf, ctx.short) - local stock = node.source == "field" and stockOf(last, node.fruit) or nil + local stock = node.source == "field" and fieldStockOf(last, node) or nil local row = makeRow("node", depth, nodeLevel, {string.rep(ADChainView.INDENT, depth) .. ctx.titleOf(node.fillType), sourceText(node, last, ctx.textOf), ctx.fmt(node.perMonth or 0), text, stock ~= nil and ctx.fmt(stock) or ""}) row.fillType = node.fillType diff --git a/FS25_ADSmartPickup/l10n/l10n_da.xml b/FS25_ADSmartPickup/l10n/l10n_da.xml index c0af30f..7304767 100644 --- a/FS25_ADSmartPickup/l10n/l10n_da.xml +++ b/FS25_ADSmartPickup/l10n/l10n_da.xml @@ -598,6 +598,7 @@ + diff --git a/FS25_ADSmartPickup/l10n/l10n_en.xml b/FS25_ADSmartPickup/l10n/l10n_en.xml index 9b0e6f9..0963905 100644 --- a/FS25_ADSmartPickup/l10n/l10n_en.xml +++ b/FS25_ADSmartPickup/l10n/l10n_en.xml @@ -598,6 +598,7 @@ +