From a2816007600953bc17a98f50fb402bd3e950cbb1 Mon Sep 17 00:00:00 2001 From: masterdraco Date: Thu, 24 Sep 2026 18:07:24 +0200 Subject: [PATCH] =?UTF-8?q?fix(forsyning):=20rest=20ved=20fuldt=20m=C3=A5l?= =?UTF-8?q?=20k=C3=B8res=20til=20run'ets=20andet=20m=C3=A5l=20med=20mest?= =?UTF-8?q?=20plads=20f=C3=B8r=20Wait;=20staldes=20l=C3=A6ssestationer=20e?= =?UTF-8?q?r=20aldrig=20kilde=20for=20halm/foder=20(kun=20output)=20(v1.18?= =?UTF-8?q?.6.0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FS25_ADSmartPickup/adSmartPickup.lua | 20 +++++++++++++++++ FS25_ADSmartPickup/adUnloadWait.lua | 32 ++++++++++++++++++++++++++++ FS25_ADSmartPickup/modDesc.xml | 2 +- docs/BRUGERMANUAL.md | 11 ++++++---- docs/USER_MANUAL.md | 11 ++++++---- tests/test_adSmartPickup.lua | 30 ++++++++++++++++++++++++++ 6 files changed, 97 insertions(+), 9 deletions(-) diff --git a/FS25_ADSmartPickup/adSmartPickup.lua b/FS25_ADSmartPickup/adSmartPickup.lua index 04f7fc5..be6120a 100644 --- a/FS25_ADSmartPickup/adSmartPickup.lua +++ b/FS25_ADSmartPickup/adSmartPickup.lua @@ -272,7 +272,27 @@ end -- 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. -- 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) + if isProtectedHusbandryInput(station, fillTypes) then + return true + end local productionPoint = getProductionPoint(station) if productionPoint == nil or productionPoint.productions == nil then return false diff --git a/FS25_ADSmartPickup/adUnloadWait.lua b/FS25_ADSmartPickup/adUnloadWait.lua index 7a49c4d..8483833 100644 --- a/FS25_ADSmartPickup/adUnloadWait.lua +++ b/FS25_ADSmartPickup/adUnloadWait.lua @@ -119,6 +119,27 @@ local function isAtUnloadTarget(adEnv, vehicle) end -- 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 vehicle = task.vehicle -- Kun "fremme ved målet". At AD stadig regner vognen som aflæssende (isActiveAtTrigger) @@ -157,6 +178,17 @@ local function startWaiting(adEnv, task) return 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) return hasRouteBothWays(adEnv, vehicle, candidate.marker.id, task.destinationID) end diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index d2cd772..48282ed 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.18.5.0 + 1.18.6.0 <en>AD Smart Pickup</en> diff --git a/docs/BRUGERMANUAL.md b/docs/BRUGERMANUAL.md index d737fbb..9bfcadc 100644 --- a/docs/BRUGERMANUAL.md +++ b/docs/BRUGERMANUAL.md @@ -1,6 +1,6 @@ # 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)* @@ -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. 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 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: 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. -3. Den kører tilbage, når målet kan tage **hele resten**, eller er faldet under **Kør hjem fra Wait +2. Har flowet flere mål, køres resten til det af de andre mål, der har mest plads til varen. Det + 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**. En rest på under 100 l, som målet ikke kan tage, tæller som tom. Traktoren kører videre i stedet for diff --git a/docs/USER_MANUAL.md b/docs/USER_MANUAL.md index eea0509..c69d9d2 100644 --- a/docs/USER_MANUAL.md +++ b/docs/USER_MANUAL.md @@ -1,6 +1,6 @@ # 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)* @@ -212,7 +212,8 @@ Before each loading, the mod looks at the targets and picks: *fullest* source, so production never stops. 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 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: 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. -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**. A rest of less than 100 l that the target cannot take counts as empty. The tractor drives on instead diff --git a/tests/test_adSmartPickup.lua b/tests/test_adSmartPickup.lua index 6b3cd47..bdf0a17 100644 --- a/tests/test_adSmartPickup.lua +++ b/tests/test_adSmartPickup.lua @@ -409,6 +409,16 @@ local function buildCowWorld() return driver 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) local cowDriver = buildCowWorld() 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) 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 ==== local function buildSwapWorld() buildWaitWorld() -- stald: halm FULD (50000/50000), TMR 10000/100000