diff --git a/FS25_ADSmartPickup/adRuns.lua b/FS25_ADSmartPickup/adRuns.lua
index d1a3a1e..1049b75 100644
--- a/FS25_ADSmartPickup/adRuns.lua
+++ b/FS25_ADSmartPickup/adRuns.lua
@@ -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)
diff --git a/FS25_ADSmartPickup/adRunsStorage.lua b/FS25_ADSmartPickup/adRunsStorage.lua
index 012a6a2..b5bb783 100644
--- a/FS25_ADSmartPickup/adRunsStorage.lua
+++ b/FS25_ADSmartPickup/adRunsStorage.lua
@@ -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)
diff --git a/FS25_ADSmartPickup/adSupplyPlanner.lua b/FS25_ADSmartPickup/adSupplyPlanner.lua
index c8ce7ad..784e5b4 100644
--- a/FS25_ADSmartPickup/adSupplyPlanner.lua
+++ b/FS25_ADSmartPickup/adSupplyPlanner.lua
@@ -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
diff --git a/FS25_ADSmartPickup/adUnloadWait.lua b/FS25_ADSmartPickup/adUnloadWait.lua
index 8331eb8..1cace98 100644
--- a/FS25_ADSmartPickup/adUnloadWait.lua
+++ b/FS25_ADSmartPickup/adUnloadWait.lua
@@ -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)
diff --git a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua
index 81b6791..0d269bc 100644
--- a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua
+++ b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua
@@ -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]
diff --git a/FS25_ADSmartPickup/gui/SmartPickupFrame.xml b/FS25_ADSmartPickup/gui/SmartPickupFrame.xml
index a6b5c31..2e714b9 100644
--- a/FS25_ADSmartPickup/gui/SmartPickupFrame.xml
+++ b/FS25_ADSmartPickup/gui/SmartPickupFrame.xml
@@ -41,6 +41,12 @@
+
+
+
+
+
+
diff --git a/FS25_ADSmartPickup/l10n/l10n_da.xml b/FS25_ADSmartPickup/l10n/l10n_da.xml
index 9680790..18239bd 100644
--- a/FS25_ADSmartPickup/l10n/l10n_da.xml
+++ b/FS25_ADSmartPickup/l10n/l10n_da.xml
@@ -42,6 +42,8 @@
+
+
diff --git a/FS25_ADSmartPickup/l10n/l10n_en.xml b/FS25_ADSmartPickup/l10n/l10n_en.xml
index 3e25725..8314503 100644
--- a/FS25_ADSmartPickup/l10n/l10n_en.xml
+++ b/FS25_ADSmartPickup/l10n/l10n_en.xml
@@ -42,6 +42,8 @@
+
+
diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml
index a689e0d..6bec9fb 100644
--- a/FS25_ADSmartPickup/modDesc.xml
+++ b/FS25_ADSmartPickup/modDesc.xml
@@ -1,7 +1,7 @@
masterdraco
- 1.10.1.0
+ 1.10.2.0
AD Smart Pickup
diff --git a/tests/test_adRuns.lua b/tests/test_adRuns.lua
index cf57836..dd7e175 100644
--- a/tests/test_adRuns.lua
+++ b/tests/test_adRuns.lua
@@ -96,6 +96,17 @@ check("R6 nextId", dirty.nextId, 8)
check("R6 tom liste", #ADRuns.fromRecords(nil).runs, 0)
check("R6 parseIngredients trimmer", #ADRuns.parseIngredients(" STRAW FORAGE "), 2)
+-- R7: "kør hjem under X %" — standard 50, klippes til 10..90, overlever records
+local r7, r7run = ADRuns.createRun(ADRuns.newStore(), "Tærskel")
+check("R7 standard 50 %", r7run.returnBelowPercent, 50)
+check("R7 sættes", ADRuns.getRun(ADRuns.updateRun(r7, r7run.id, {returnBelowPercent = 30}), r7run.id).returnBelowPercent, 30)
+check("R7 klippes til 90", ADRuns.getRun(ADRuns.updateRun(r7, r7run.id, {returnBelowPercent = 150}), r7run.id).returnBelowPercent, 90)
+check("R7 klippes til 10", ADRuns.getRun(ADRuns.updateRun(r7, r7run.id, {returnBelowPercent = 0}), r7run.id).returnBelowPercent, 10)
+local r7rec = ADRuns.toRecords(ADRuns.updateRun(r7, r7run.id, {returnBelowPercent = 25}))
+check("R7 i records", r7rec[1].returnBelow, 25)
+check("R7 tilbage fra records", ADRuns.fromRecords(r7rec).runs[1].returnBelowPercent, 25)
+check("R7 manglende i gammel fil -> 50", ADRuns.fromRecords({{id = 1, name = "Gammel"}}).runs[1].returnBelowPercent, 50)
+
-- ==== XML-laget mod en flad XMLFile-mock (nøgle -> værdi) ====
dofile("FS25_ADSmartPickup/adRunsStorage.lua")
diff --git a/tests/test_adSmartPickup.lua b/tests/test_adSmartPickup.lua
index ec28d88..0473423 100644
--- a/tests/test_adSmartPickup.lua
+++ b/tests/test_adSmartPickup.lua
@@ -842,6 +842,25 @@ UnloadTask.update(st6, 16)
check("S6 bliver ved siloen efter bytte", st6.destinationID, 21)
check("S6 ingen ny Wait-omdirigering", countLogsSince(mark6, "venter ved"), 0)
+-- I5: run'ets "kør hjem under X %" styrer hvornår den ventende kører hjem
+buildWaitWorld()
+local vt, tt = makeUnloadingVehicle("Fastrac Tærskel", 551, 5000, 60000) -- rest > stald: kun tærsklen kan sende hjem
+vt.uniqueId = "vehicleThreshold"
+testVehicles = {vt}
+ADRunsController.store = ADRuns.newStore()
+local thrRun = ADRunsController.createRun("Tærskel")
+ADRunsController.updateRun(thrRun.id, {targetWayPointId = 30, returnBelowPercent = 20})
+ADRunsController.assignVehicle(thrRun.id, vt)
+UnloadTask.update(tt, 16)
+check("I5 venter først", tt.destinationID, 40)
+waitBarnLevels[STRAW] = 15000 -- 70 % fri: 50 %-reglen ville sende hjem, men tærsklen er "under 20 %" (= 80 % fri)
+UnloadTask.update(tt, 16)
+check("I5 70 % fri er ikke nok ved 'under 20 %'", tt.destinationID, 40)
+waitBarnLevels[STRAW] = 9000 -- 82 % fri >= 80 %
+UnloadTask.update(tt, 16)
+check("I5 hjem når målet er under 20 %", tt.destinationID, 30)
+ADRunsController.store = ADRuns.newStore()
+
-- ==== E: eksponerede opslag (bruges af runs-controller og læs-bytte) ====
local eDriver = buildCowWorld()
check("E1 aflæsningsstation ved waypoint", ADSmartPickup.getUnloadStationAtWayPointId(FS25_AutoDrive, 30, 1), unloadStations[1])
diff --git a/tests/test_adSupplyPlanner.lua b/tests/test_adSupplyPlanner.lua
index 037be1b..13d55e6 100644
--- a/tests/test_adSupplyPlanner.lua
+++ b/tests/test_adSupplyPlanner.lua
@@ -172,5 +172,11 @@ check("kæmpe rest: halv stald fri -> hjem", ADSupplyPlanner.canUnloadRest(15000
check("kæmpe rest: under halv stald fri -> vent", ADSupplyPlanner.canUnloadRest(100000, 700000, 300000), false)
check("ukendt kapacitet -> kun hele resten tæller", ADSupplyPlanner.canUnloadRest(100000, 700000, nil), false)
+-- canUnloadRest med run-tærskel: "under 30 %" = fri >= 70 % af kapaciteten
+check("tærskel 30 %: 60 % fri er ikke nok", ADSupplyPlanner.canUnloadRest(60000, 90000, 100000, 0.7), false)
+check("tærskel 30 %: 70 % fri er nok", ADSupplyPlanner.canUnloadRest(70000, 90000, 100000, 0.7), true)
+check("hele resten passer vinder altid", ADSupplyPlanner.canUnloadRest(5000, 5000, 100000, 0.7), true)
+check("uden tærskel = 50 %-reglen", ADSupplyPlanner.canUnloadRest(50000, 90000, 100000), true)
+
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
os.exit(failures == 0 and 0 or 1)