feat(kæde): læs i køretøjer tæller som lager, ingen skift under høst, varelinje åbner flow (v1.61.8.0)

This commit is contained in:
2026-09-30 18:03:56 +02:00
parent 2290bdde95
commit 47afdebf64
9 changed files with 70 additions and 6 deletions
+1
View File
@@ -8,6 +8,7 @@ står i [docs/BRUGERMANUAL.md](docs/BRUGERMANUAL.md) (dansk) og [docs/USER_MANUA
## 1.61 — Produktionskæder
- **1.61.8** Kæderne regner **læs i vogne og høstertanke** med som lager (afgrøder og markvarer), og der kommer ingen forslag eller automatiske markskift for en afgrøde, mens den høstes. Klik på en varelinje i fanen **Kæder** åbner flowet, der kører varen, i redigeringsvinduet (så en stoppet kæde kan få traktor og vogn).
- **1.61.7** Kædelinjen viser *Gård* (uden klik) ved slutprodukter, gården selv bruger (fx ensilage, TMR, roesnitter), og *Sælg*/*Behold* kun ved varer, der faktisk kan sælges straks.
- **1.61.6** Slutprodukter sælges kun straks, hvis gården ikke selv bruger varen (stald, tændt produktion, forbrug eller flow). Varer kæden allerede havde sat til salg, sættes automatisk tilbage til *Behold*, når de ikke længere må sælges (in-game: TMR og roesnitter blev sat til salg).
- **1.61.5** Redskaber bredere end Courseplays 50 m (Unrealistic kan sættes til op til 120 m) afvises ikke længere: de bruges, og kursusbredden sættes til 50 m, før Courseplay laver kurset. Markflåden viser *kursus 50 m* ved dem.
+1 -1
View File
@@ -306,7 +306,7 @@ local function plan(data, state)
if not data.fieldsIncomplete then
shortages = ADCropPlan.shortages(chain.fieldDemand, data.stock, data.fields, data.yields)
state.dismissed = ADChain.pruneDismissed(state.dismissed, shortages)
actions = ADCropPlan.choose(shortages, data.fields, used, data.yields, data.priceOf or data.prices, ADChain.dismissedFruits(state.dismissed))
actions = ADCropPlan.choose(shortages, data.fields, used, data.yields, data.priceOf or data.prices, ADChain.dismissedFruits(state.dismissed), data.harvesting)
end
-- de ægte opskrifter (ikke kopien): en pauset rod er stadig tændt og må ikke "tændes" og overtages af kæden
local prod = ADChainActions.productions(data.recipes, chain.needRecipes, protected)
+36 -1
View File
@@ -267,6 +267,38 @@ local function readFieldGoods(data, nameOf)
end
end
-- Læs i gårdens køretøjer (vogne, høstertanke, marksilo-vogne) tæller som lager: kun afgrøder og markvarer, ikke
-- diesel o.l. loads = {{name, liters}} (in-game 30/9: høst på vej hjem gav falsk mangel og forslag).
function ADChainData.addVehicleLoads(data, loads)
for _, load in ipairs(loads or {}) do
local name, liters = load.name, load.liters or 0
local fruit = name ~= nil and (data.crops or {})[name] or nil
if liters > 0 and (fruit ~= nil or (data.fieldGoods or {})[name]) then
data.stockByFill[name] = (data.stockByFill[name] or 0) + liters
if fruit ~= nil then data.stock[fruit] = (data.stock[fruit] or 0) + liters end
end
end
end
local function readVehicleLoads(data, farmId, nameOf)
local ok, err = pcall(function()
local loads = {}
for _, vehicle in pairs(g_currentMission.vehicleSystem.vehicles or {}) do
local spec = vehicle.spec_fillUnit
if spec ~= nil and vehicle.getOwnerFarmId ~= nil and vehicle:getOwnerFarmId() == farmId then
for index, unit in ipairs(spec.fillUnits or {}) do
local level = vehicle:getFillUnitFillLevel(index) or 0
if level > 0 then
table.insert(loads, {name = nameOf(vehicle:getFillUnitFillType(index) or unit.fillType), liters = level})
end
end
end
end
ADChainData.addVehicleLoads(data, loads)
end)
if not ok then ADChainData.warnOnce("vehicleLoads", "læs i køretøjer kunne ikke læses: %s", tostring(err)) 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
@@ -324,6 +356,8 @@ local function readFields(data)
local field = ADFieldJobs.getField(info.id)
local plan = field.plan or {}
local fruit = info.growing and info.fruitName or field.lastFruit
-- høst i gang: ingen forslag/skift for afgrøden, mens læssene er på vej hjem
if field.stage == "harvesting" and info.fruitName ~= nil then data.harvesting[info.fruitName] = true end
return {id = info.id, areaHa = ADFields.polygonAreaHa(info.verts), fruit = fruit,
targetFruit = plan.targetFruit, targetSource = plan.targetSource, chainFree = plan.chainFree == true,
permanent = ADChainData.isPermanent(info.fruitName or field.lastFruit), excluded = info.kind == "mowable"}
@@ -439,13 +473,14 @@ 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 = {}, fieldGoods = {}}
usedFruits = {}, points = {}, fillTitles = {}, fillIndex = {}, placeables = {}, barnFood = {}, stockByFill = {}, rates = {}, fieldGoods = {}, harvesting = {}}
local nameOf = nameResolver(data)
data.priceOf = priceResolver(data)
readPlaceables(data, farmId, byChain, nameOf)
readCrops(data, nameOf)
readFieldGoods(data, nameOf)
readStock(data, adEnv, farmId, nameOf)
readVehicleLoads(data, farmId, nameOf)
readRates(data, adEnv, farmId, nameOf)
readUsedFruits(data, adEnv, farmId, nameOf)
readFields(data)
+2
View File
@@ -343,6 +343,8 @@ local function rootRows(rows, root, last, ctx, isExpanded)
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
-- klik åbner flowet der kører varen til modtageren (in-game 30/9: "flow stopped" kunne ikke redigeres)
if node.consumer ~= nil then row.openConsumer, row.openFillType = node.consumer, node.fillType end
table.insert(rows, row)
problemRows(node.fillType, depth + 1)
if node.source == "field" and node.fruit ~= nil and not shown[node.fruit] then
+3 -2
View File
@@ -46,7 +46,8 @@ local function isCandidate(field, used, dismissedFruit, fruit)
end
-- prices: {[afgrøde] = pris} eller en funktion afgrøde -> pris (slås kun op for kandidaternes afgrøder).
function ADCropPlan.choose(shortages, fields, usedFruits, yields, prices, dismissed)
-- harvesting = {[afgrøde] = true}: afgrøder der høstes lige nu — ingen forslag/skift for dem (læssene er på vej).
function ADCropPlan.choose(shortages, fields, usedFruits, yields, prices, dismissed, harvesting)
local actions, taken = {}, {}
local used = usedFruits or {}
local priceOf = type(prices) == "function" and prices or function(fruit) return (prices or {})[fruit] or 0 end
@@ -57,7 +58,7 @@ function ADCropPlan.choose(shortages, fields, usedFruits, yields, prices, dismis
return priceCache[fruit]
end
for _, shortage in ipairs(shortages or {}) do
local remaining = shortage.missingHa
local remaining = (harvesting or {})[shortage.fruit] and 0 or shortage.missingHa
local candidates = {}
for _, field in ipairs(fields or {}) do
if not taken[field.id] and isCandidate(field, used, (dismissed or {})[field.id], shortage.fruit) then
+24 -1
View File
@@ -42,6 +42,27 @@ function SmartPickupFrame:onChainAction(action)
self:requestRebuild()
end
-- Klik på en varelinje: åbn flowet der kører varen til modtageren (kædens eget flow til bygningen, ellers et flow
-- der dækker varen) i Flows-fanens redigeringsvindue; uden flow en besked.
function SmartPickupFrame:openChainFlow(consumer, fillType)
local runs = ADChain.runsView(ADRunsController.store.runs)
local runId = ADChainActions.chainRunFor(runs, consumer)
if runId == nil then
for _, run in ipairs(runs) do
if runId == nil and ADChainActions.isCovered({consumer = consumer, fillType = fillType}, {run}) then runId = run.id end
end
end
if runId == nil then
self.statusOverride = self.i18n:getText("spu_chain_noFlow")
self.statusOverrideTicks = SmartPickupFrame.STATUS_OVERRIDE_TICKS
self:requestRebuild()
return
end
self.selectedRunId = runId
self:switchTab(SmartPickupFrame.TAB_FLOWS)
self:openEditPanel("flow", runId)
end
-- Klik på rodens linje uden for knappen: fold ud/sammen (huskes kun mens spillet kører).
function SmartPickupFrame:toggleChainRoot(rootKey)
self.chainExpanded = self.chainExpanded or {}
@@ -76,7 +97,9 @@ function SmartPickupFrame:addChainRow(entry)
cells[4]:setTextColor(unpackFn(color))
end
local actions, toggle = entry.actions or {}, entry.toggle
if next(actions) ~= nil or toggle ~= nil then
if next(actions) == nil and toggle == nil and entry.openConsumer ~= nil then
self:makeRowClickable(row, function(frame) frame:openChainFlow(entry.openConsumer, entry.openFillType) end)
elseif next(actions) ~= nil or toggle ~= nil then
self:makeRowClickable(row, function(frame)
local action = actions[clickedCell(frame, cells) or 0]
if action ~= nil then
+1
View File
@@ -579,6 +579,7 @@
<text name="spu_item_balanced" text="i balance"/>
<text name="spu_item_share" text="%d %% af varen"/>
<text name="spu_tab_chains" text="Kæder"/>
<text name="spu_chain_noFlow" text="Intet flow kører den vare endnu — kæden opretter det, når der er en ledig traktor/vogn"/>
<text name="spu_chain_finalFarm" text="Gård"/>
<text name="spu_chain_finalSell" text="Sælg"/>
<text name="spu_chain_finalKeep" text="Behold"/>
+1
View File
@@ -579,6 +579,7 @@
<text name="spu_item_balanced" text="balanced"/>
<text name="spu_item_share" text="%d %% of the good"/>
<text name="spu_tab_chains" text="Chains"/>
<text name="spu_chain_noFlow" text="No flow carries that good yet — the chain creates one when a tractor/trailer is free"/>
<text name="spu_chain_finalFarm" text="Farm"/>
<text name="spu_chain_finalSell" text="Sell"/>
<text name="spu_chain_finalKeep" text="Keep"/>
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<modDesc descVersion="107">
<author>masterdraco</author>
<version>1.61.7.0</version>
<version>1.61.8.0</version>
<title>
<en>AD Smart Pickup</en>
</title>