fix(forsyning): rest ved fuldt mål køres til run'ets andet mål med mest plads før Wait; staldes læssestationer er aldrig kilde for halm/foder (kun output) (v1.18.6.0)

This commit is contained in:
2026-09-24 18:07:24 +02:00
parent 2d6d2c31a5
commit a281600760
6 changed files with 97 additions and 9 deletions
+20
View File
@@ -272,7 +272,27 @@ end
-- En fabriks læssestation udleverer også dens INPUTS (fx halm i en TMR-fabrik). -- En fabriks læssestation udleverer også dens INPUTS (fx halm i en TMR-fabrik).
-- Dem stjæler vi aldrig — fabrikken ville løbe tør for det, den skal bruge. -- Dem stjæler vi aldrig — fabrikken ville løbe tør for det, den skal bruge.
-- Outputs (og varer der både er input og output i en kæde) må gerne hentes. -- Outputs (og varer der både er input og output i en kæde) må gerne hentes.
-- En STALDS læssestation udleverer kun staldens produktion. Den kan dog "have" halm/foder/vand
-- (strøelse, trug, udvidelseslagre inden for storageRadius) — dem henter vi aldrig dér.
local HUSBANDRY_OUTPUTS = {MILK = true, GOATMILK = true, BUFFALOMILK = true, SHEEPMILK = true, LIQUIDMANURE = true, MANURE = true, DIGESTATE = true, EGG = true, WOOL = true}
local function isProtectedHusbandryInput(station, fillTypes)
local placeable = station.owningPlaceable
if placeable == nil or placeable.getHusbandryFillLevel == nil or g_fillTypeManager == nil then
return false
end
for _, fillType in ipairs(fillTypes) do
if not HUSBANDRY_OUTPUTS[tostring(g_fillTypeManager:getFillTypeNameByIndex(fillType))] then
return true
end
end
return false
end
local function isProtectedProductionInput(station, fillTypes) local function isProtectedProductionInput(station, fillTypes)
if isProtectedHusbandryInput(station, fillTypes) then
return true
end
local productionPoint = getProductionPoint(station) local productionPoint = getProductionPoint(station)
if productionPoint == nil or productionPoint.productions == nil then if productionPoint == nil or productionPoint.productions == nil then
return false return false
+32
View File
@@ -119,6 +119,27 @@ local function isAtUnloadTarget(adEnv, vehicle)
end end
-- Står køretøjet ved målet med rest-læs, som målet ikke kan tage? Så find et Wait-punkt. -- Står køretøjet ved målet med rest-læs, som målet ikke kan tage? Så find et Wait-punkt.
ADUnloadWait.MIN_FREE_FOR_OTHER_TARGET = 1000
-- Det af run'ets ANDRE mål der tager varen og har mest fri plads (≥ MIN_FREE_FOR_OTHER_TARGET) og vej.
local function findOtherTargetForRest(adEnv, vehicle, fillType, currentId)
local run = ADRunsController ~= nil and ADRunsController.getRunForVehicle ~= nil and ADRunsController.getRunForVehicle(vehicle) or nil
if run == nil or (run.kind or "supply") ~= "supply" or vehicle.ad.stateModule.setSecondMarkerByWayPointId == nil then
return nil
end
local best, bestFree = nil, ADUnloadWait.MIN_FREE_FOR_OTHER_TARGET - 1
for _, targetId in ipairs(ADRunsController.getRunTargets(run)) do
if targetId ~= currentId then
local free = ADSmartPickup.getTargetFreeCapacity(adEnv, vehicle, fillType, false, targetId)
if free ~= nil and free > bestFree and hasRouteTo(adEnv, vehicle, targetId) then
local marker = adEnv.ADGraphManager.getMapMarkerByWayPointId ~= nil and adEnv.ADGraphManager:getMapMarkerByWayPointId(targetId) or nil
best, bestFree = {id = targetId, name = marker ~= nil and marker.name or tostring(targetId)}, free
end
end
end
return best, bestFree
end
local function startWaiting(adEnv, task) local function startWaiting(adEnv, task)
local vehicle = task.vehicle local vehicle = task.vehicle
-- Kun "fremme ved målet". At AD stadig regner vognen som aflæssende (isActiveAtTrigger) -- Kun "fremme ved målet". At AD stadig regner vognen som aflæssende (isActiveAtTrigger)
@@ -157,6 +178,17 @@ local function startWaiting(adEnv, task)
return return
end end
-- Run med flere mål: resten køres til det af run'ets andre mål der har mest plads til varen,
-- før der ventes (en 2,4M l-vogn kan ellers aldrig komme af med resten ved én svinesti)
local other, otherFree = findOtherTargetForRest(adEnv, vehicle, fillType, target.id)
if other ~= nil then
vehicle.ad.stateModule:setSecondMarkerByWayPointId(other.id)
redirect(task, other.id)
Logging.info(ADUnloadWait.LOG_PREFIX .. "'%s': %s er fuld — resten (%.0f l %s) køres til %s (%.0f l fri)", tostring(ADRuns.vehicleLabel(vehicle)), tostring(target.name), rest,
g_fillTypeManager ~= nil and tostring(g_fillTypeManager:getFillTypeNameByIndex(fillType)) or tostring(fillType), tostring(other.name), otherFree)
return
end
local isReachable = function(candidate) local isReachable = function(candidate)
return hasRouteBothWays(adEnv, vehicle, candidate.marker.id, task.destinationID) return hasRouteBothWays(adEnv, vehicle, candidate.marker.id, task.destinationID)
end end
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?> <?xml version="1.0" encoding="utf-8" standalone="no"?>
<modDesc descVersion="107"> <modDesc descVersion="107">
<author>masterdraco</author> <author>masterdraco</author>
<version>1.18.5.0</version> <version>1.18.6.0</version>
<title> <title>
<en>AD Smart Pickup</en> <en>AD Smart Pickup</en>
</title> </title>
+7 -4
View File
@@ -1,6 +1,6 @@
# AD Smart Pickup — brugermanual # AD Smart Pickup — brugermanual
Version 1.18.5 · Farming Simulator 25 · tillæg til AutoDrive Version 1.18.6 · Farming Simulator 25 · tillæg til AutoDrive
*English version: [USER_MANUAL.md](USER_MANUAL.md)* *English version: [USER_MANUAL.md](USER_MANUAL.md)*
@@ -210,7 +210,8 @@ Før hver pålæsning ser moden på målene og vælger:
ryddes. Mælk, gylle og digestat hentes fra den *fyldeste* kilde, så produktionen ikke stopper. ryddes. Mælk, gylle og digestat hentes fra den *fyldeste* kilde, så produktionen ikke stopper.
En fabriks egne **inputs** hentes aldrig fra fabrikken selv. Halm i en TMR-fabrik bliver altså, hvor En fabriks egne **inputs** hentes aldrig fra fabrikken selv. Halm i en TMR-fabrik bliver altså, hvor
den er. den er. En stalds læssepunkter bruges kun til staldens produktion, altså mælk, gylle, gødning, æg og
uld. Halm og foder hentes aldrig ved en stald.
**Fabrikker følger produktionerne.** Tænder du TMR i en fabrik, begynder traktoren at hente **Fabrikker følger produktionerne.** Tænder du TMR i en fabrik, begynder traktoren at hente
ensilage, hø, halm og mineralfoder af sig selv. Slukker du den, holder traktoren op. Produktioner, ensilage, hø, halm og mineralfoder af sig selv. Slukker du den, holder traktoren op. Produktioner,
@@ -234,8 +235,10 @@ at alle henter det samme.
Kan målet ikke tage hele læsset, sker følgende: Kan målet ikke tage hele læsset, sker følgende:
1. Traktoren læsser af, hvad der er plads til. 1. Traktoren læsser af, hvad der er plads til.
2. Resten køres til det **nærmeste ledige Wait-punkt**, så triggeren ikke blokeres for de næste. 2. Har flowet flere mål, køres resten til det af de andre mål, der har mest plads til varen. Det
3. Den kører tilbage, når målet kan tage **hele resten**, eller er faldet under **Kør hjem fra Wait kræver mindst 1.000 l fri plads. Store vogne fordeler sig altså på alle staldene.
3. Har ingen af målene plads, køres resten til det **nærmeste ledige Wait-punkt**, så triggeren ikke blokeres for de næste.
4. Den kører tilbage, når målet kan tage **hele resten**, eller er faldet under **Kør hjem fra Wait
under**. under**.
En rest på under 100 l, som målet ikke kan tage, tæller som tom. Traktoren kører videre i stedet for En rest på under 100 l, som målet ikke kan tage, tæller som tom. Traktoren kører videre i stedet for
+7 -4
View File
@@ -1,6 +1,6 @@
# AD Smart Pickup — user manual # AD Smart Pickup — user manual
Version 1.18.5 · Farming Simulator 25 · add-on for AutoDrive Version 1.18.6 · Farming Simulator 25 · add-on for AutoDrive
*Dansk version: [BRUGERMANUAL.md](BRUGERMANUAL.md)* *Dansk version: [BRUGERMANUAL.md](BRUGERMANUAL.md)*
@@ -212,7 +212,8 @@ Before each loading, the mod looks at the targets and picks:
*fullest* source, so production never stops. *fullest* source, so production never stops.
A factory's own **inputs** are never taken from the factory itself. Straw in a TMR factory stays A factory's own **inputs** are never taken from the factory itself. Straw in a TMR factory stays
where it is. where it is. A barn's loading points are only used for the barn's own products: milk, slurry,
manure, eggs and wool. Straw and feed are never collected at a barn.
**Factories follow their productions.** Switch TMR on in a factory, and the tractor starts fetching **Factories follow their productions.** Switch TMR on in a factory, and the tractor starts fetching
silage, hay, straw and mineral feed by itself. Switch it off, and the tractor stops. Productions silage, hay, straw and mineral feed by itself. Switch it off, and the tractor stops. Productions
@@ -236,9 +237,11 @@ of them fetching the same thing.
When the target cannot take the whole load, this happens: When the target cannot take the whole load, this happens:
1. The tractor unloads what fits. 1. The tractor unloads what fits.
2. The rest is taken to the **nearest free Wait point**, so the trigger is not blocked for the next 2. If the flow has several targets, the rest goes to the other target with the most room for the
good, at least 1,000 l free. Large trailers therefore spread over all the barns.
3. If none of the targets has room, the rest is taken to the **nearest free Wait point**, so the trigger is not blocked for the next
tractors. tractors.
3. It drives back when the target can take **the whole rest**, or has dropped below **Return from 4. It drives back when the target can take **the whole rest**, or has dropped below **Return from
Wait below**. Wait below**.
A rest of less than 100 l that the target cannot take counts as empty. The tractor drives on instead A rest of less than 100 l that the target cannot take counts as empty. The tractor drives on instead
+30
View File
@@ -409,6 +409,16 @@ local function buildCowWorld()
return driver return driver
end end
-- P0: en STALDS læssestation (gylle/mælk) er aldrig kilde for halm/foder, selv om den "har" halm
-- (in-game 24/9: TMR- og Cow Food-traktorer skulle hente halm ved 'Pig Slurry 1')
do
local cowDriverP0 = buildCowWorld()
addMarker(22, "Pig Slurry 1", "Pig Slurry", 400, 0)
table.insert(stations, makeStation(122, 400, 15, {[STRAW] = 1000, [LIQUIDMANURE or 99] = 5000}))
stations[#stations].owningPlaceable = {getHusbandryFillLevel = function() return 0 end, getName = function() return "Svinesti" end}
check("P0 halm hentes ikke fra svinestiens gyllestation", manager:getNextPickup(cowDriverP0, false), 21)
end
-- P1: halm mangler mest -> hentes fra halmladen, IKKE fra fabrikken (selv om den er mindst fyldt) -- P1: halm mangler mest -> hentes fra halmladen, IKKE fra fabrikken (selv om den er mindst fyldt)
local cowDriver = buildCowWorld() local cowDriver = buildCowWorld()
check("P1 halm hentes fra siloen, ikke fabrikkens input", manager:getNextPickup(cowDriver, false), 21) check("P1 halm hentes fra siloen, ikke fabrikkens input", manager:getNextPickup(cowDriver, false), 21)
@@ -863,6 +873,26 @@ UnloadTask.update(tp, 16)
check("I4 kun run'ets pulje", tp.destinationID, 40) check("I4 kun run'ets pulje", tp.destinationID, 40)
ADRunsController.store = ADRuns.newStore() ADRunsController.store = ADRuns.newStore()
-- I8: målet er fuldt med rest på vognen, men run'et har FLERE mål -> resten køres til det mål med plads
-- (in-game 24/9: 'Pig Food' med 2,4M l halm holdt ved den første svinesti; de fire andre fik intet)
buildWaitWorld()
addMarker(31, "Cow 2 Food", "Cow Food", 6000, 0)
local pen2Levels = {[STRAW] = 10000}
table.insert(unloadStations, makeUnloadStation(131, 6000, 10, pen2Levels, {[STRAW] = 100000}, {name = "sti 2"}))
local v8, t8 = makeUnloadingVehicle("Pig Food", 531, 5000, 35000)
v8.uniqueId = "vehicleI8"
testVehicles = {v8}
ADRunsController.store = ADRuns.newStore()
local pens = ADRunsController.createRun("Pig Food")
ADRunsController.updateRun(pens.id, {targetWayPointIds = {30, 31}, waitPoolGroup = "Cow Wait"})
ADRunsController.assignVehicle(pens.id, v8)
UnloadTask.update(t8, 16)
check("I8 resten køres til det andet mål", t8.destinationID, 31)
check("I8 AD's aflæsningsmål skiftet", v8.ad.stateModule.secondMarkerId, 31)
check("I8 logget", countLogsSince(1, "resten") >= 1, true)
ADRunsController.store = ADRuns.newStore()
testVehicles = {}
-- ==== S: læs-bytte fra Wait — halm-rest ved Wait, målet mangler TMR -> aflæs halm i silo ==== -- ==== S: læs-bytte fra Wait — halm-rest ved Wait, målet mangler TMR -> aflæs halm i silo ====
local function buildSwapWorld() local function buildSwapWorld()
buildWaitWorld() -- stald: halm FULD (50000/50000), TMR 10000/100000 buildWaitWorld() -- stald: halm FULD (50000/50000), TMR 10000/100000