From aa040d95ef7f00ddd5833e56983a4802aeeb25ab Mon Sep 17 00:00:00 2001 From: masterdraco Date: Tue, 22 Sep 2026 22:39:31 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20run-traktorer=20f=C3=A5r=20ingredienser?= =?UTF-8?q?=20og=20Wait-pulje=20fra=20deres=20run?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FS25_ADSmartPickup/adSmartPickup.lua | 23 ++++++++++-- FS25_ADSmartPickup/adUnloadWait.lua | 17 ++++++--- FS25_ADSmartPickup/modDesc.xml | 3 ++ tests/test_adSmartPickup.lua | 54 ++++++++++++++++++++++++++-- 4 files changed, 88 insertions(+), 9 deletions(-) diff --git a/FS25_ADSmartPickup/adSmartPickup.lua b/FS25_ADSmartPickup/adSmartPickup.lua index 4c5ed7c..dcd6c80 100644 --- a/FS25_ADSmartPickup/adSmartPickup.lua +++ b/FS25_ADSmartPickup/adSmartPickup.lua @@ -489,10 +489,23 @@ local function applySupplyChoice(driver, ingredients, choice) supplyState[driver] = {ingredients = ingredients, narrowedTo = choice.fillType} end --- Samlet valg: behovsstyret når flere varer er valgt, ellers mappe-logik. nil = AutoDrives original. +-- Ingredienslisten for køretøjet: run'ets liste når det er i et run (ingen tændknap +-- nødvendig), ellers AD's multi-valg (>1 vare). nil = ingen behovsstyring. +function ADSmartPickup.getIngredientsFor(adEnv, driver) + local run = ADRunsController ~= nil and ADRunsController.getRunForVehicle(driver) or nil + if run ~= nil then + local ingredients = ADRunsController.getRunIngredients(adEnv, run, driver) + if #ingredients > 0 then + return ingredients + end + return nil + end + return ADSupplyPlanner.resolveIngredients(supplyState[driver], driver.ad.stateModule:getSelectedFillTypes()) +end + +-- Samlet valg: behovsstyret med ingrediensliste, ellers mappe-logik. nil = AutoDrives original. function ADSmartPickup.choosePickup(adEnv, driver) - local selected = driver.ad.stateModule:getSelectedFillTypes() - local ingredients = ADSupplyPlanner.resolveIngredients(supplyState[driver], selected) + local ingredients = ADSmartPickup.getIngredientsFor(adEnv, driver) if ingredients == nil then supplyState[driver] = nil return ADSmartPickup.findLeastFilledPickup(adEnv, driver) @@ -664,6 +677,10 @@ function ADSmartPickup:update(dt) if adEnv ~= nil and g_currentMission ~= nil and g_currentMission.storageSystem ~= nil then installHook(adEnv) isHooked = true + if ADRunsController ~= nil then + pcall(ADRunsController.load, adEnv) + pcall(ADRunsController.installSaveHook) + end end return end diff --git a/FS25_ADSmartPickup/adUnloadWait.lua b/FS25_ADSmartPickup/adUnloadWait.lua index 99aa28c..abb7b0f 100644 --- a/FS25_ADSmartPickup/adUnloadWait.lua +++ b/FS25_ADSmartPickup/adUnloadWait.lua @@ -39,10 +39,12 @@ local function getRestLoad(adEnv, vehicle) return bestFillType, bestLevel end -local function getWaitCandidates(adEnv) +-- poolGroup: run'ets Wait-mappe; tom/nil = alle Wait-mapper. +local function getWaitCandidates(adEnv, poolGroup) local candidates = {} for _, marker in pairs(adEnv.ADGraphManager:getMapMarkers()) do - if ADSupplyPlanner.isWaitMarker(marker) then + local isInPool = poolGroup == nil or poolGroup == "" or marker.group == poolGroup + if isInPool and ADSupplyPlanner.isWaitMarker(marker) then local wayPoint = adEnv.ADGraphManager:getWayPointById(marker.id) if wayPoint ~= nil then table.insert(candidates, {marker = marker, x = wayPoint.x, z = wayPoint.z}) @@ -52,6 +54,11 @@ local function getWaitCandidates(adEnv) return candidates end +local function getWaitPoolGroup(vehicle) + local run = ADRunsController ~= nil and ADRunsController.getRunForVehicle(vehicle) or nil + return run ~= nil and run.waitPoolGroup or nil +end + local function getVehiclePosition(vehicle) if vehicle.rootNode == nil then return nil @@ -110,11 +117,13 @@ local function startWaiting(adEnv, task) local isReachable = function(candidate) return hasRouteBothWays(adEnv, vehicle, candidate.marker.id, task.destinationID) end - local candidate = x ~= nil and ADSupplyPlanner.pickNearestFreeWaitPoint(getWaitCandidates(adEnv), claims, x, z, vehicle, isReachable) or nil + local pool = getWaitPoolGroup(vehicle) + local candidate = x ~= nil and ADSupplyPlanner.pickNearestFreeWaitPoint(getWaitCandidates(adEnv, pool), claims, x, z, vehicle, isReachable) or nil if candidate == nil then if not warnedNoWaitPoint[vehicle] then warnedNoWaitPoint[vehicle] = true - Logging.warning(ADUnloadWait.LOG_PREFIX .. "'%s' har %.0f l rest, og målet er fuldt — intet ledigt Wait-punkt med vejnet til OG fra målet (markør i en mappe med 'Wait' i navnet), så AutoDrive venter ved målet", tostring(vehicle:getName()), rest) + local poolText = pool ~= nil and pool ~= "" and (" i puljen '" .. pool .. "'") or "" + Logging.warning(ADUnloadWait.LOG_PREFIX .. "'%s' har %.0f l rest, og målet er fuldt — intet ledigt Wait-punkt%s med vejnet til OG fra målet (markør i en mappe med 'Wait' i navnet), så AutoDrive venter ved målet", tostring(vehicle:getName()), rest, poolText) end return end diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index 0183d76..e23508f 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -12,6 +12,9 @@ + + + diff --git a/tests/test_adSmartPickup.lua b/tests/test_adSmartPickup.lua index 9a34fe9..6c4f67d 100644 --- a/tests/test_adSmartPickup.lua +++ b/tests/test_adSmartPickup.lua @@ -5,7 +5,10 @@ local SUGARBEET, WHEAT = 10, 11 local HAY, GRASS, SILAGE = 20, 21, 22 local MILK = 30 local FILL_TYPE_NAMES = {[SUGARBEET] = "SUGARBEET", [WHEAT] = "WHEAT", [HAY] = "DRYGRASS_WINDROW", [GRASS] = "GRASS_WINDROW", [SILAGE] = "SILAGE", [MILK] = "MILK"} -g_fillTypeManager = {getFillTypeNameByIndex = function(_, index) return FILL_TYPE_NAMES[index] end} +g_fillTypeManager = { + getFillTypeNameByIndex = function(_, index) return FILL_TYPE_NAMES[index] end, + getFillTypeIndexByName = function(_, name) for index, candidate in pairs(FILL_TYPE_NAMES) do if candidate == name then return index end end return nil end, +} local logLines = {} -- ---- FS25-mocks ---- @@ -60,6 +63,7 @@ g_currentMission = { getUnloadingStations = function() return unloadStations end, }, accessHandler = {canFarmAccess = function() return true end}, + getFarmId = function() return 1 end, } -- ---- AutoDrive-mocks ---- @@ -80,6 +84,7 @@ local function resetWorld() end local RT_NONE, RT_ONLYPICKUP = 1, 2 +local testVehicles = {} FS25_AutoDrive = { AutoDrive = { RT_NONE = RT_NONE, @@ -87,6 +92,8 @@ FS25_AutoDrive = { getAllFillLevels = function(trailers) return trailers.level, trailers.capacity or 0, false, 0 end, getAllUnits = function(vehicle) return vehicle.trailers or {level = 0} end, getAllDischargeableUnits = function(vehicle) return vehicle.units or {} end, + getSupportedFillTypesOfAllUnitsAlphabetically = function(vehicle) return vehicle.carriable or {} end, + getAllVehicles = function() return testVehicles end, }, -- opgaven noterer hvad AutoDrive "ser" af indstillinger mens den opdaterer LoadAtDestinationTask = {update = function(self) @@ -154,6 +161,8 @@ local function makeSupplyDriver(firstMarker, secondMarker, fillTypes) end dofile("FS25_ADSmartPickup/adSupplyPlanner.lua") +dofile("FS25_ADSmartPickup/adRuns.lua") +dofile("FS25_ADSmartPickup/adRunsController.lua") dofile("FS25_ADSmartPickup/adUnloadWait.lua") dofile("FS25_ADSmartPickup/adSmartPickup.lua") listener:update(16) -- installerer hook'en @@ -307,7 +316,6 @@ check("S10 lager læses fra produktionspunktet", manager:getNextPickup(feeder, f ProductionPoint = {PROD_STATUS = {INACTIVE = 0, RUNNING = 1, MISSING_INPUTS = 2, NO_OUTPUT_SPACE = 3}} local WATER = 40 FILL_TYPE_NAMES[WATER] = "WATER" -FS25_AutoDrive.AutoDrive.getSupportedFillTypesOfAllUnitsAlphabetically = function(vehicle) return vehicle.carriable end -- Fabrik med TMR-opskrift: sluger ensilage, nipper til hø. Vand kan vognen ikke bære. local function buildFactoryWorld(tmrEnabled, tmrStatus) @@ -699,6 +707,48 @@ listener:update(16) FS25_AutoDrive.LoadAtDestinationTask.update(stopperTask, 16) check("L7 greb ryddet når AD stopper", stopperTask.seenRotate, RT_ONLYPICKUP) +-- ==== I: run-integration — ingredienser fra run i stedet for multi-valg; Wait-pulje pr. run ==== +-- I1: traktor i et run med ÉN valgt vare i AD får alligevel behovsstyring (run'ets liste) +local runDriver = buildCowWorld() +runDriver.uniqueId = "vehicleRun" +runDriver.ad.stateModule.selectedFillTypes = {STRAW} +runDriver.ad.stateModule.fillType = STRAW +testVehicles = {runDriver} +ADRunsController.store = ADRuns.newStore() +local feedRun = ADRunsController.createRun("Cow Feed") +ADRunsController.updateRun(feedRun.id, {targetWayPointId = 30, ingredientMode = "manual", ingredients = {"FORAGE", "STRAW"}}) +ADRunsController.assignVehicle(feedRun.id, runDriver) +check("I1 run-liste styrer: halm mangler -> halmladen", manager:getNextPickup(runDriver, false), 21) +cowLevels[STRAW] = 99500 +check("I1 run-liste styrer: TMR når halm er fuld", manager:getNextPickup(runDriver, false), 20) +check("I1 getIngredientsFor = run'ets liste", table.concat(ADSmartPickup.getIngredientsFor(FS25_AutoDrive, runDriver), ","), TMR .. "," .. STRAW) + +-- I2: samme traktor uden run -> som før (én vare = mappe-logik i mappen "Lader") +ADRunsController.store = ADRuns.newStore() +runDriver.ad.stateModule.selectedFillTypes = {STRAW} +cowLevels[STRAW] = 0 +check("I2 uden run: mappe-logik", manager:getNextPickup(runDriver, false), 21) +check("I2 uden run og én vare: ingen ingrediensliste", ADSmartPickup.getIngredientsFor(FS25_AutoDrive, runDriver), nil) + +-- I3: run med tom auto-liste (intet mål) -> falder tilbage til AD's valg +local emptyRun = ADRunsController.createRun("Tomt") +ADRunsController.assignVehicle(emptyRun.id, runDriver) +check("I3 tom run-liste -> som uden run", manager:getNextPickup(runDriver, false), 21) + +-- I4: Wait-kandidater begrænses til run'ets pulje +buildWaitWorld() +addMarker(42, "Pig Wait 1", "Pig Wait", 5050, 0) -- nærmere end Cow Wait, men forkert pulje +local vp, tp = makeUnloadingVehicle("Fastrac Pulje", 511, 5000, 35000) +vp.uniqueId = "vehiclePool" +testVehicles = {vp} +ADRunsController.store = ADRuns.newStore() +local poolRun = ADRunsController.createRun("Cow Feed") +ADRunsController.updateRun(poolRun.id, {targetWayPointId = 30, waitPoolGroup = "Cow Wait"}) +ADRunsController.assignVehicle(poolRun.id, vp) +UnloadTask.update(tp, 16) +check("I4 kun run'ets pulje", tp.destinationID, 40) +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])