feat: læs på vej tælles med — flere traktorer til samme stald fordeler sig på varer og stalde (v1.14.1.0)

This commit is contained in:
2026-09-24 01:36:29 +02:00
parent 1b7b74ad2c
commit 8ec0853fae
7 changed files with 124 additions and 7 deletions
+3 -1
View File
@@ -252,7 +252,9 @@ function ADRunsController.chooseTarget(adEnv, run, vehicle)
local ingredients = ADRunsController.getRunIngredients(adEnv, run, vehicle)
local bestId, bestNeed = nil, nil
for _, entry in ipairs(getTargetStations(adEnv, run, farmId)) do
local ranked = ADSmartPickup.getTargetNeedsRanked(entry.station, ingredients, farmId)
-- læs andre traktorer er på vej med til stalden, tæller med — så fordeler de sig på staldene
local pending = ADSmartPickup.getPending ~= nil and ADSmartPickup.getPending(entry.wayPointId, vehicle) or nil
local ranked = ADSmartPickup.getTargetNeedsRanked(entry.station, ingredients, farmId, pending)
local need = ranked[1]
if need ~= nil and need.freeSpace >= (ADSupplyPlanner.MIN_FREE_LITERS or 1000) then
local isBetter = bestNeed == nil
+53 -5
View File
@@ -49,6 +49,9 @@ local supplyState = setmetatable({}, {__mode = "k"})
-- køretøj -> {station, fillTypes}: siloen moden sendte køretøjet til på denne tur.
local activePickups = setmetatable({}, {__mode = "k"})
-- køretøj -> senest loggede valg (AutoDrive spørger flere gange pr. start).
-- køretøj -> {wayPointId, fillType, liters}: læs traktoren er på vej med (fordeler flere traktorer til
-- samme mål; ryddes ved aflæsning, stop eller ny tur)
local enRoute = setmetatable({}, {__mode = "k"})
local lastLogged = setmetatable({}, {__mode = "k"})
-- Cross-mod adgang: FS25-mods er sandboxede; AutoDrives globals ligger
@@ -436,7 +439,7 @@ local function getFoodPriority(station, fillType)
end
local capacity = foodSpec.capacity or 0
local troughScore = capacity > 0 and math.max(0, capacity - total) / capacity or 0
return troughScore, foodFillTypes[fillType]
return troughScore, foodFillTypes[fillType], capacity
end
-- En stald kan MODTAGE foder, halm og vand — ikke sine egne outputs (mælk, gødning, gylle),
@@ -508,16 +511,49 @@ local function getTargetNeeds(station, ingredients, farmId, demand)
for _, fillType in ipairs(ingredients) do
local level, capacity = getTargetLevelAndCapacity(station, fillType, farmId)
if level ~= nil then
local troughScore, priority = getFoodPriority(station, fillType)
table.insert(needs, {fillType = fillType, level = level, capacity = capacity, litersPerMinute = demand[fillType], scoreOverride = troughScore, priority = priority})
local troughScore, priority, troughCapacity = getFoodPriority(station, fillType)
table.insert(needs, {fillType = fillType, level = level, capacity = capacity, litersPerMinute = demand[fillType], scoreOverride = troughScore, priority = priority, troughCapacity = troughCapacity})
end
end
return needs
end
-- Til fanen: samme rangering som valget bruger (uden vogn-størrelse). Fulde varer sidst.
function ADSmartPickup.getTargetNeedsRanked(station, fillTypes, farmId)
-- ---- læs på vej ----
function ADSmartPickup.setEnRoute(vehicle, wayPointId, fillType, liters)
if wayPointId == nil or fillType == nil or liters == nil or liters <= 0 then
enRoute[vehicle] = nil
return
end
enRoute[vehicle] = {wayPointId = wayPointId, fillType = fillType, liters = liters}
end
function ADSmartPickup.clearEnRoute(vehicle)
enRoute[vehicle] = nil
end
function ADSmartPickup.resetEnRoute()
enRoute = setmetatable({}, {__mode = "k"})
end
-- {[fillType] = liter} som ANDRE (aktive) traktorer er på vej med til målet.
function ADSmartPickup.getPending(wayPointId, excludeVehicle)
local pending = {}
for vehicle, entry in pairs(enRoute) do
if vehicle ~= excludeVehicle and entry.wayPointId == wayPointId then
pending[entry.fillType] = (pending[entry.fillType] or 0) + entry.liters
end
end
return pending
end
-- pending (valgfri): læs på vej til målet, lægges oven i (fanen og mål-valget mellem stalde).
function ADSmartPickup.getTargetNeedsRanked(station, fillTypes, farmId, pending)
local needs = getTargetNeeds(station, fillTypes, farmId, {})
if pending ~= nil and next(pending) ~= nil then
needs = ADSupplyPlanner.applyPending(needs, pending)
end
local ranked = ADSupplyPlanner.rankNeeds(needs)
local seen = {}
for _, need in ipairs(ranked) do seen[need.fillType] = true end
@@ -593,7 +629,8 @@ function ADSmartPickup.findSupplyPickup(adEnv, driver, ingredients, targetWayPoi
local allMarkers = adEnv.ADGraphManager:getMapMarkers()
local markerStations = collectMarkerStations(adEnv, allMarkers, farmId, target.owningPlaceable)
local loadCapacity = getLoadCapacity(adEnv, driver)
local ranked = ADSupplyPlanner.rankNeeds(getTargetNeeds(target, ingredients, farmId, demand), loadCapacity)
local needs = ADSupplyPlanner.applyPending(getTargetNeeds(target, ingredients, farmId, demand), ADSmartPickup.getPending(wayPointId, driver))
local ranked = ADSupplyPlanner.rankNeeds(needs, loadCapacity)
-- bedste foder først: lavere foder kun når truget er nødlidende (ADSupplyPlanner.isFoodAllowed)
local bestFood = ADSupplyPlanner.bestFoodPriority ~= nil and ADSupplyPlanner.bestFoodPriority(ranked) or nil
for _, need in ipairs(ranked) do
@@ -602,6 +639,9 @@ function ADSmartPickup.findSupplyPickup(adEnv, driver, ingredients, targetWayPoi
return {
markerIndex = choice.markerIndex,
markerId = choice.markerId,
targetWayPointId = wayPointId,
-- det der reelt kommer af: vognen, kildens beholdning og målets fri plads sætter grænsen
pendingLiters = math.min(loadCapacity or math.huge, choice.level or math.huge, need.freeSpace or math.huge),
markerName = choice.markerName,
group = choice.group,
level = choice.level,
@@ -619,6 +659,7 @@ end
local function applySupplyChoice(driver, ingredients, choice)
local stateModule = driver.ad.stateModule
ADSmartPickup.setEnRoute(driver, choice.targetWayPointId, choice.fillType, choice.pendingLiters)
stateModule:setFillType(choice.fillType)
stateModule.selectedFillTypes = {choice.fillType}
stateModule:raiseDirtyFlag()
@@ -687,6 +728,7 @@ end
-- Samlet valg: behovsstyret med ingrediensliste, ellers mappe-logik. nil = AutoDrives original.
function ADSmartPickup.choosePickup(adEnv, driver)
enRoute[driver] = nil -- ny tur: forrige læs er leveret (eller opgivet)
chooseRunTarget(adEnv, driver)
local ingredients = ADSmartPickup.getIngredientsFor(adEnv, driver)
if ingredients == nil then
@@ -913,6 +955,12 @@ local function restoreStoppedVehicles()
activePickups[vehicle] = nil
end
end
for vehicle in pairs(enRoute) do
local stateModule = vehicle.ad ~= nil and vehicle.ad.stateModule or nil
if stateModule == nil or stateModule.isActive == nil or not stateModule:isActive() then
enRoute[vehicle] = nil
end
end
for vehicle, state in pairs(supplyState) do
local stateModule = vehicle.ad ~= nil and vehicle.ad.stateModule or nil
if stateModule == nil or not stateModule:isActive() then
+31
View File
@@ -210,6 +210,37 @@ function ADSupplyPlanner.rankNeeds(needs, loadCapacity)
return ranked
end
-- ---- læs på vej ----
-- pending = {[fillType] = liter} som ANDRE traktorer allerede er på vej med til samme mål.
-- Almindelige lagre: niveau + på vej (højst kapaciteten). Foder i et fælles trug (scoreOverride +
-- troughCapacity): varens niveau stiger, og trugets fri plads falder med ALT foder på vej.
-- Returnerer NY liste; input røres ikke.
function ADSupplyPlanner.applyPending(needs, pending)
local troughPending = 0
for _, need in ipairs(needs) do
if need.scoreOverride ~= nil and pending[need.fillType] ~= nil then
troughPending = troughPending + pending[need.fillType]
end
end
local result = {}
for index, need in ipairs(needs) do
local copy = {}
for key, value in pairs(need) do copy[key] = value end
local extra = pending[need.fillType] or 0
if need.scoreOverride ~= nil and troughPending > 0 then
local free = math.max(0, (need.capacity - need.level) - troughPending)
copy.level = need.level + extra
copy.capacity = copy.level + free
local troughCapacity = need.troughCapacity or need.capacity
copy.scoreOverride = troughCapacity > 0 and math.max(0, need.scoreOverride - troughPending / troughCapacity) or 0
elseif extra > 0 then
copy.level = math.min(need.capacity, need.level + extra)
end
result[index] = copy
end
return result
end
-- ---- bedste foder først ----
-- Foder i et stald-trug deler plads. Lavere foder (ensilage/hø, vægt 0,8) må kun hentes når der er
-- bedre foder (TMR, 1,0) på listen, hvis truget er nødlidende: mindst så stor en andel fri.
+4
View File
@@ -392,6 +392,10 @@ function ADUnloadWait.install(adEnv)
task.finished = function(self)
release(self.vehicle)
restoreTarget(self.vehicle)
-- læsset er afleveret: tæller ikke længere som "på vej" for de andre traktorer
if ADSmartPickup ~= nil and ADSmartPickup.clearEnRoute ~= nil then
ADSmartPickup.clearEnRoute(self.vehicle)
end
return originalFinished(self)
end
end
+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.14.0.0</version>
<version>1.14.1.0</version>
<title>
<en>AD Smart Pickup</en>
</title>