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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,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>
|
||||
|
||||
@@ -82,6 +82,7 @@ local function addMarker(index, name, group, x, z, fromX, fromZ)
|
||||
end
|
||||
|
||||
local function resetWorld()
|
||||
if ADSmartPickup ~= nil and ADSmartPickup.resetEnRoute ~= nil then ADSmartPickup.resetEnRoute() end
|
||||
stations, unloadStations, markers, wayPoints, nodePositions, logLines = {}, {}, {}, {}, {}, {}
|
||||
FS25_AutoDrive.ADMultipleTargetsManager.pickups = {}
|
||||
end
|
||||
@@ -1140,6 +1141,20 @@ check("N1 strengt opslag finder intet", ADSmartPickup.getLoadStationAtWayPointId
|
||||
check("N1 løst opslag finder tanken", ADSmartPickup.getLoadStationNearWayPointId(FS25_AutoDrive, 1, 1, 40), stations[1])
|
||||
check("N1 uden for afstanden -> nil", ADSmartPickup.getLoadStationNearWayPointId(FS25_AutoDrive, 1, 1, 20), nil)
|
||||
|
||||
-- V: flere traktorer til samme mål fordeler sig — læs på vej tælles med (in-game 24/9: alle 4 Fastrac'er
|
||||
-- hentede det samme). Græs mangler mest (20000/100000), ensilage næst (50000/100000).
|
||||
buildFeedWorld()
|
||||
local feederA = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS, SILAGE})
|
||||
feederA.trailers = {level = 0, capacity = 70000}
|
||||
local feederB = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS, SILAGE})
|
||||
feederB.trailers = {level = 0, capacity = 70000}
|
||||
check("V1 A henter græs (mangler mest)", manager:getNextPickup(feederA, false), 3)
|
||||
check("V1 B henter ensilage (græs er på vej med A)", manager:getNextPickup(feederB, false), 4)
|
||||
check("V1 A's læs registreret på vej", ADSmartPickup.getPending(10, feederB)[GRASS], 40000)
|
||||
ADSmartPickup.clearEnRoute(feederA)
|
||||
check("V2 A læsset af -> intet på vej", ADSmartPickup.getPending(10, feederB)[GRASS], nil)
|
||||
check("V2 egne læs tæller ikke", ADSmartPickup.getPending(10, feederB)[SILAGE], nil)
|
||||
|
||||
-- X: udkørsel koblet på getNextPickup-wrap'en og Wait-logikken
|
||||
resetWorld()
|
||||
addMarker(1, "Suger Load", "Pickup points", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[WHEAT] = 1000}))
|
||||
|
||||
@@ -206,5 +206,22 @@ check("NF1 ikke-foder (halm) påvirkes ikke", ADSupplyPlanner.isFoodAllowed(stra
|
||||
check("NF1 intet bedre foder på listen -> tilladt", ADSupplyPlanner.isFoodAllowed({fillType = 7, priority = 0.8, score = 0.1, trough = true}, 0.8), true)
|
||||
check("NF1 bedste foderprioritet", ADSupplyPlanner.bestFoodPriority({tmrNeed, silageNeed, strawNeed}), 1.0)
|
||||
|
||||
-- Læs på vej (andre traktorer): lægges oven i målets niveau; foder deler trug -> hele trugets fri plads falder
|
||||
local pendingNeeds = {
|
||||
{fillType = 31, level = 5000, capacity = 20000}, -- halm
|
||||
{fillType = 116, level = 0, capacity = 30000, scoreOverride = 1.0, troughCapacity = 30000}, -- TMR, tomt trug
|
||||
{fillType = 7, level = 0, capacity = 30000, scoreOverride = 1.0, troughCapacity = 30000}, -- ensilage
|
||||
}
|
||||
local adjusted = ADSupplyPlanner.applyPending(pendingNeeds, {[116] = 20000, [31] = 20000})
|
||||
check("AP halm: niveau + på vej, højst kapacitet", adjusted[1].level, 20000)
|
||||
check("AP TMR: niveau + på vej", adjusted[2].level, 20000)
|
||||
check("AP TMR: kapacitet = niveau + resterende fri", adjusted[2].capacity, 30000)
|
||||
check("AP ensilage: truget har kun 10000 fri tilbage", adjusted[3].capacity - adjusted[3].level, 10000)
|
||||
check("AP trug-score falder", math.floor(adjusted[3].scoreOverride * 100 + 0.5), 33)
|
||||
check("AP input urørt", pendingNeeds[2].level, 0)
|
||||
check("AP intet på vej -> samme værdier", ADSupplyPlanner.applyPending(pendingNeeds, {})[1].level, 5000)
|
||||
local ranked = ADSupplyPlanner.rankNeeds(adjusted, 8000)
|
||||
check("AP halm fuld (på vej) -> kun trug-varer tilbage", #ranked, 2)
|
||||
|
||||
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
||||
os.exit(failures == 0 and 0 or 1)
|
||||
|
||||
Reference in New Issue
Block a user