feat: run-indstilling 'Kør hjem fra Wait under X %' (10–90, standard 50) i model, XML, Wait-regel og fane (v1.10.2.0)

This commit is contained in:
2026-09-23 01:47:42 +02:00
parent ef69347c55
commit 231d6a41d6
12 changed files with 98 additions and 6 deletions
+13
View File
@@ -6,6 +6,9 @@
ADRuns = {}
ADRuns.MAX_LOOPS = 99
-- "Kør hjem fra Wait når målet er under X %": 10..90, standard 50 (= den gamle 50 %-regel).
ADRuns.DEFAULT_RETURN_BELOW_PERCENT = 50
ADRuns.MIN_RETURN_BELOW_PERCENT, ADRuns.MAX_RETURN_BELOW_PERCENT = 10, 90
ADRuns.XML_VERSION = 1
ADRuns.INGREDIENT_MODES = {auto = true, manual = true}
@@ -26,6 +29,7 @@ local function copyRun(run)
ingredients = copyList(run.ingredients),
waitPoolGroup = run.waitPoolGroup,
loops = run.loops,
returnBelowPercent = run.returnBelowPercent,
vehicleIds = copyList(run.vehicleIds),
}
end
@@ -38,6 +42,11 @@ local function copyStore(store)
return {nextId = store.nextId, runs = runs}
end
local function clampPercent(percent)
local value = tonumber(percent) or ADRuns.DEFAULT_RETURN_BELOW_PERCENT
return math.max(ADRuns.MIN_RETURN_BELOW_PERCENT, math.min(ADRuns.MAX_RETURN_BELOW_PERCENT, math.floor(value)))
end
local function clampLoops(loops)
local value = tonumber(loops) or 0
return math.max(0, math.min(ADRuns.MAX_LOOPS, math.floor(value)))
@@ -57,6 +66,7 @@ function ADRuns.createRun(store, name)
ingredients = {},
waitPoolGroup = "",
loops = 0,
returnBelowPercent = ADRuns.DEFAULT_RETURN_BELOW_PERCENT,
vehicleIds = {},
}
new.nextId = new.nextId + 1
@@ -102,6 +112,7 @@ function ADRuns.updateRun(store, runId, changes)
if changes.ingredients ~= nil then run.ingredients = copyList(changes.ingredients) end
if changes.waitPoolGroup ~= nil then run.waitPoolGroup = changes.waitPoolGroup end
if changes.loops ~= nil then run.loops = clampLoops(changes.loops) end
if changes.returnBelowPercent ~= nil then run.returnBelowPercent = clampPercent(changes.returnBelowPercent) end
return new
end
@@ -170,6 +181,7 @@ function ADRuns.toRecords(store)
ingredients = ADRuns.serializeIngredients(run.ingredients),
waitPool = run.waitPoolGroup,
loops = run.loops,
returnBelow = run.returnBelowPercent,
vehicleIds = copyList(run.vehicleIds),
}
end
@@ -192,6 +204,7 @@ function ADRuns.fromRecords(records)
ingredients = ADRuns.parseIngredients(record.ingredients),
waitPoolGroup = record.waitPool or "",
loops = clampLoops(record.loops),
returnBelowPercent = clampPercent(record.returnBelow),
vehicleIds = copyList(record.vehicleIds),
})
store.nextId = math.max(store.nextId, id + 1)
+2
View File
@@ -34,6 +34,7 @@ function ADRunsStorage.writeRecords(xmlFile, records)
xmlFile:setString(key .. "#ingredients", record.ingredients)
xmlFile:setString(key .. "#waitPool", record.waitPool)
xmlFile:setInt(key .. "#loops", record.loops)
xmlFile:setInt(key .. "#returnBelow", record.returnBelow or ADRuns.DEFAULT_RETURN_BELOW_PERCENT)
for vehicleIndex, vehicleId in ipairs(record.vehicleIds) do
xmlFile:setString(string.format("%s.vehicle(%d)#id", key, vehicleIndex - 1), vehicleId)
end
@@ -51,6 +52,7 @@ function ADRunsStorage.readRecords(xmlFile)
ingredients = xmlFile:getString(key .. "#ingredients"),
waitPool = xmlFile:getString(key .. "#waitPool"),
loops = xmlFile:getInt(key .. "#loops"),
returnBelow = xmlFile:getInt(key .. "#returnBelow"),
vehicleIds = {},
}
xmlFile:iterate(key .. ".vehicle", function(_, vehicleKey)
+4 -3
View File
@@ -158,11 +158,12 @@ end
ADSupplyPlanner.WAIT_RETURN_FREE_FRACTION = 0.5
-- Kan den ventende køre hjem? Ja når hele resten passer, eller når målet er mindst
-- WAIT_RETURN_FREE_FRACTION tomt. capacity nil = kun hele resten tæller.
function ADSupplyPlanner.canUnloadRest(freeSpace, rest, capacity)
-- freeFraction tomt (standard WAIT_RETURN_FREE_FRACTION; run'ets "kør hjem under X %" =
-- freeFraction 1 - X/100). capacity nil = kun hele resten tæller.
function ADSupplyPlanner.canUnloadRest(freeSpace, rest, capacity, freeFraction)
local needed = rest
if capacity ~= nil and capacity > 0 then
needed = math.min(rest, capacity * ADSupplyPlanner.WAIT_RETURN_FREE_FRACTION)
needed = math.min(rest, capacity * (freeFraction or ADSupplyPlanner.WAIT_RETURN_FREE_FRACTION))
end
return freeSpace >= needed
end
+4 -1
View File
@@ -169,7 +169,10 @@ local function updateWaiting(adEnv, task, wait, dt)
-- Friske tal til evt. bytte og til tilbage-loggen — ikke dem fra da ventningen startede.
wait.fillType, wait.rest = fillType, rest
local free, capacity = ADSmartPickup.getTargetFreeCapacity(adEnv, vehicle, fillType)
if free == nil or not ADSupplyPlanner.canUnloadRest(free, rest, capacity) then
-- run'ets "kør hjem under X %" (fri ≥ (100-X) % af kapaciteten), ellers standardreglen
local run = ADRunsController ~= nil and ADRunsController.getRunForVehicle(vehicle) or nil
local freeFraction = run ~= nil and run.returnBelowPercent ~= nil and (1 - run.returnBelowPercent / 100) or nil
if free == nil or not ADSupplyPlanner.canUnloadRest(free, rest, capacity, freeFraction) then
if ADLoadSwap ~= nil then
local isReachable = function(markerId)
return hasRouteBothWays(adEnv, vehicle, markerId, wait.homeDestinationID)
+28 -1
View File
@@ -61,6 +61,23 @@ function SmartPickupFrame:initialize()
table.insert(loopTexts, tostring(loops))
end
self.loopsOption:setTexts(loopTexts)
self.returnBelowSteps = {}
local percentTexts = {}
for percent = ADRuns.MIN_RETURN_BELOW_PERCENT, ADRuns.MAX_RETURN_BELOW_PERCENT, 10 do
table.insert(self.returnBelowSteps, percent)
table.insert(percentTexts, string.format("%d %%", percent))
end
self.returnBelowOption:setTexts(percentTexts)
end
function SmartPickupFrame:getReturnBelowIndex(run)
local index = 1
for position, percent in ipairs(self.returnBelowSteps or {}) do
if percent <= (run.returnBelowPercent or ADRuns.DEFAULT_RETURN_BELOW_PERCENT) then
index = position
end
end
return index
end
-- FocusManager har skiftet API mellem FS-versioner; her hentes det fokuserede element
@@ -228,7 +245,7 @@ function SmartPickupFrame:rebuild()
self:fillRunOption(adEnv)
local run = self:getRun()
local hasRun = run ~= nil and adEnv ~= nil
for _, element in ipairs({self.targetOption, self.ingredientModeOption, self.waitPoolOption, self.loopsOption}) do
for _, element in ipairs({self.targetOption, self.ingredientModeOption, self.waitPoolOption, self.loopsOption, self.returnBelowOption}) do
element:setDisabled(not hasRun)
end
if hasRun then
@@ -238,6 +255,7 @@ function SmartPickupFrame:rebuild()
self.needsRow = self:addTextRow(self:describeNeeds(adEnv, run))
self:fillWaitPoolOption(adEnv, run)
self.loopsOption:setState(run.loops + 1, false)
self.returnBelowOption:setState(self:getReturnBelowIndex(run), false)
self:addTextRow(self.i18n:getText("spu_vehicles"))
self:fillVehicleRows(adEnv, run)
end
@@ -398,6 +416,7 @@ function SmartPickupFrame:syncOptionsFromModel(adEnv, run)
end
sync(self.waitPoolOption, poolIndex)
sync(self.loopsOption, run.loops + 1)
sync(self.returnBelowOption, self:getReturnBelowIndex(run))
end
-- "Silage 0/33.750 l · Straw 21.375/21.375 l" — mest trængende først.
@@ -631,6 +650,14 @@ function SmartPickupFrame:onClickLoops(state)
end)
end
function SmartPickupFrame:onClickReturnBelow(state)
self:safe(function(frame)
local run = frame:getRun()
if run == nil then return end
ADRunsController.updateRun(run.id, {returnBelowPercent = frame.returnBelowSteps[state] or ADRuns.DEFAULT_RETURN_BELOW_PERCENT})
end)
end
function SmartPickupFrame:onClickVehicle(state, element)
self:safe(function(frame)
local run, data = frame:getRun(), frame.rowData[element]
@@ -41,6 +41,12 @@
</MultiTextOption>
<Text profile="fs25_settingsMultiTextOptionTitle" text="$l10n_spu_loops"/>
</Bitmap>
<Bitmap profile="fs25_multiTextOptionContainer">
<MultiTextOption profile="fs25_settingsMultiTextOption" onClick="onClickReturnBelow" id="returnBelowOption">
<Text profile="fs25_multiTextOptionTooltip" name="ignore" text="$l10n_spu_returnBelow_tooltip"/>
</MultiTextOption>
<Text profile="fs25_settingsMultiTextOptionTitle" text="$l10n_spu_returnBelow"/>
</Bitmap>
</ScrollingLayout>
<!-- prefabs: klones pr. vare / traktor / tekstlinje og fjernes fra layoutet ved initialize -->
+2
View File
@@ -42,6 +42,8 @@
<text name="spu_renameTitle" text="Navn på run"/>
<text name="spu_deleteConfirm" text="Slet run '%s'? Traktorerne stoppes ikke."/>
<text name="spu_capacity" text="%s l"/>
<text name="spu_returnBelow" text="Kør hjem fra Wait under"/>
<text name="spu_returnBelow_tooltip" text="Ventende traktorer kører først hjem, når målet er faldet under denne andel (eller hele resten passer)."/>
<text name="spu_overview" text="Overblik"/>
<text name="spu_activity_stopped" text="stoppet"/>
<text name="spu_activity_waiting" text="venter ved %s med %s l %s"/>
+2
View File
@@ -42,6 +42,8 @@
<text name="spu_renameTitle" text="Run name"/>
<text name="spu_deleteConfirm" text="Delete run '%s'? Tractors are not stopped."/>
<text name="spu_capacity" text="%s l"/>
<text name="spu_returnBelow" text="Return from Wait below"/>
<text name="spu_returnBelow_tooltip" text="Waiting tractors return only once the target has dropped below this share (or the whole rest fits)."/>
<text name="spu_overview" text="Overview"/>
<text name="spu_activity_stopped" text="stopped"/>
<text name="spu_activity_waiting" text="waiting at %s with %s l %s"/>
+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.10.1.0</version>
<version>1.10.2.0</version>
<title>
<en>AD Smart Pickup</en>
</title>