From 3c22eae0eb61823988ee4467edb8cb0b6e6dacfa Mon Sep 17 00:00:00 2001 From: masterdraco Date: Wed, 23 Sep 2026 00:13:58 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20fane=20sidst=20i=20ESC-menuen=20(ingen?= =?UTF-8?q?=20omrokering),=20stald-foder=20via=20spec=5FhusbandryFood/anim?= =?UTF-8?q?alFoodSystem,=20m=C3=A5l=20=3D=20kun=20stalde+fabrikker,=20kort?= =?UTF-8?q?e=20Wait-labels=20(v1.9.2.0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FS25_ADSmartPickup/adRunsController.lua | 35 ++++++- FS25_ADSmartPickup/adSmartPickup.lua | 108 ++++++++++++++------ FS25_ADSmartPickup/gui/SmartPickupFrame.lua | 4 +- FS25_ADSmartPickup/modDesc.xml | 2 +- tests/test_adRunsController.lua | 12 ++- tests/test_adSmartPickup.lua | 31 ++++++ 6 files changed, 150 insertions(+), 42 deletions(-) diff --git a/FS25_ADSmartPickup/adRunsController.lua b/FS25_ADSmartPickup/adRunsController.lua index 8855a58..3b63865 100644 --- a/FS25_ADSmartPickup/adRunsController.lua +++ b/FS25_ADSmartPickup/adRunsController.lua @@ -371,14 +371,41 @@ end -- ---- opslag til dropdowns ---- +-- Mål er steder der SKAL holdes fyldt: stalde (1) og fabrikker (2). Siloer tager alt og +-- er kilder, ikke mål. Returnerer markører sorteret efter type, dernæst navn. +local function getTargetKind(station) + local placeable = station.owningPlaceable + if placeable == nil then + return nil + end + if placeable.getHusbandryFillLevel ~= nil then + return 1 + end + if placeable.spec_productionPoint ~= nil then + return 2 + end + return nil +end + function ADRunsController.getTargetMarkers(adEnv, farmId) - local result = {} + local entries = {} for _, marker in pairs(adEnv.ADGraphManager:getMapMarkers()) do - if ADSmartPickup.getUnloadStationAtWayPointId(adEnv, marker.id, farmId) ~= nil then - table.insert(result, marker) + local station = ADSmartPickup.getUnloadStationAtWayPointId(adEnv, marker.id, farmId) + local kind = station ~= nil and getTargetKind(station) or nil + if kind ~= nil then + table.insert(entries, {marker = marker, kind = kind}) end end - table.sort(result, function(a, b) return tostring(a.name) < tostring(b.name) end) + table.sort(entries, function(a, b) + if a.kind ~= b.kind then + return a.kind < b.kind + end + return tostring(a.marker.name) < tostring(b.marker.name) + end) + local result = {} + for index, entry in ipairs(entries) do + result[index] = entry.marker + end return result end diff --git a/FS25_ADSmartPickup/adSmartPickup.lua b/FS25_ADSmartPickup/adSmartPickup.lua index 72c9645..fb7c27e 100644 --- a/FS25_ADSmartPickup/adSmartPickup.lua +++ b/FS25_ADSmartPickup/adSmartPickup.lua @@ -368,13 +368,68 @@ local function getHusbandry(station) return nil end +-- FS25-staldes foder ligger i spec_husbandryFood: ét fælles trug (capacity) med niveau pr. +-- vare (fillLevels). Hvilke varer der er foder, ved animalFoodSystem (grupper + blandinger). +-- Returnerer {[fillType] = true} eller nil når stalden ikke har foder-spec. +local function getHusbandryFoodFillTypes(husbandry) + local foodSpec = husbandry.spec_husbandryFood + local foodSystem = g_currentMission ~= nil and g_currentMission.animalFoodSystem or nil + if foodSpec == nil or foodSystem == nil or foodSpec.animalTypeIndex == nil then + return nil + end + local fillTypes = {} + local animalFood = foodSystem.getAnimalFood ~= nil and foodSystem:getAnimalFood(foodSpec.animalTypeIndex) or nil + for _, group in pairs(animalFood ~= nil and animalFood.groups or {}) do + for _, fillType in pairs(group.fillTypes or {}) do + fillTypes[fillType] = true + end + end + local mixtures = foodSystem.getMixturesByAnimalTypeIndex ~= nil and foodSystem:getMixturesByAnimalTypeIndex(foodSpec.animalTypeIndex) or nil + for _, mixture in pairs(mixtures or {}) do + local fillType = type(mixture) == "table" and (mixture.fillType or mixture.fillTypeIndex) or mixture + if type(fillType) == "number" then + fillTypes[fillType] = true + end + end + return fillTypes +end + +-- En stald kan MODTAGE foder, halm og vand — ikke sine egne outputs (mælk, gødning, gylle), +-- selv om placeable'n "understøtter" dem som lager. Uden foder-spec (ældre/mod-stalde) +-- spørges staldens egen understøttelse. local function isFillTypeAccepted(station, husbandry, fillType) - if husbandry ~= nil and husbandry.getHusbandryIsFillTypeSupported ~= nil then - return husbandry:getHusbandryIsFillTypeSupported(fillType) + if husbandry ~= nil then + local foodFillTypes = getHusbandryFoodFillTypes(husbandry) + if foodFillTypes ~= nil then + if foodFillTypes[fillType] then + return true + end + local strawSpec, waterSpec = husbandry.spec_husbandryStraw, husbandry.spec_husbandryWater + local isStraw = strawSpec ~= nil and (strawSpec.fillType == nil or strawSpec.fillType == fillType) + and g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(fillType) == "STRAW" + local isWater = waterSpec ~= nil and waterSpec.automaticWaterSupply ~= true + and (waterSpec.fillType == fillType or g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(fillType) == "WATER") + return isStraw or isWater + end + if husbandry.getHusbandryIsFillTypeSupported ~= nil then + return husbandry:getHusbandryIsFillTypeSupported(fillType) + end end return station.getIsFillTypeSupported == nil or station:getIsFillTypeSupported(fillType) end +-- Foder deler ét trug: varens eget niveau + trugets fri plads = den kapacitet varen kan nå. +local function getHusbandryFoodLevelAndCapacity(husbandry, fillType) + local foodSpec = husbandry.spec_husbandryFood + local total = 0 + for _, level in pairs(foodSpec.fillLevels or {}) do + total = total + (level or 0) + end + local level = (foodSpec.fillLevels or {})[fillType] or 0 + local freeSpace = math.max(0, (foodSpec.capacity or 0) - total) + return level, level + freeSpace +end + -- Niveau + kapacitet for én vare i aflæsningsmålet; nil hvis målet ikke tager varen. function ADSmartPickup.getStationLevelAndCapacity(station, fillType, farmId) local husbandry = getHusbandry(station) @@ -382,6 +437,10 @@ function ADSmartPickup.getStationLevelAndCapacity(station, fillType, farmId) return nil end if husbandry ~= nil then + local foodFillTypes = getHusbandryFoodFillTypes(husbandry) + if foodFillTypes ~= nil and foodFillTypes[fillType] then + return getHusbandryFoodLevelAndCapacity(husbandry, fillType) + end local level = husbandry:getHusbandryFillLevel(fillType) or 0 return level, husbandry:getHusbandryCapacity(fillType) or level end @@ -676,48 +735,31 @@ local function restoreStoppedVehicles() end end --- Indsætter fanen i ESC-menuen foran Statistik/Priser (mønster fra FS25_FarmOperationsDashboard). -local function insertInGameMenuPage(frame, pageName, iconFile, iconUVs, position, predicate) +-- Tilføjer fanen SIDST i ESC-menuen (mønster fra FS25_FarmManager/Courseplay). Siden skal +-- først være barn af PagingElement, ellers mangler den i idPageHash og rebuildTabList +-- fejler. Omrokering af siderne forskyder fane↔side-koblingen (sidebjælkens markering +-- hoppede), så positionen er den samme overalt: efter de eksisterende sider. +local function insertInGameMenuPage(frame, pageName, iconFile, iconUVs, predicate) local inGameMenu = g_gui ~= nil and g_gui.screenControllers ~= nil and g_gui.screenControllers[InGameMenu] or nil - if inGameMenu == nil or inGameMenu.pagingElement == nil then + if inGameMenu == nil or inGameMenu.pagingElement == nil or inGameMenu.pageFrames == nil then return false end - local insertAt = position - for i = 1, #inGameMenu.pagingElement.elements do - local child = inGameMenu.pagingElement.elements[i] - if child == inGameMenu.pageStatistics or child == inGameMenu.pagePrices then - insertAt = i - break - end - end + local position = #inGameMenu.pageFrames + 1 if inGameMenu.controlIDs ~= nil then inGameMenu.controlIDs[pageName] = nil end + if inGameMenu.exposeControlsAsFields ~= nil then + inGameMenu:exposeControlsAsFields({pageName}) + end inGameMenu[pageName] = frame inGameMenu.pagingElement:addElement(frame) - if inGameMenu.exposeControlsAsFields ~= nil then - inGameMenu:exposeControlsAsFields(pageName) - end - for _, list in ipairs({inGameMenu.pagingElement.elements, inGameMenu.pageFrames}) do - for i = 1, #list do - if list[i] == frame then - table.remove(list, i) - table.insert(list, insertAt, frame) - break - end - end - end - for i = 1, #inGameMenu.pagingElement.pages do - if inGameMenu.pagingElement.pages[i].element == frame then - local page = table.remove(inGameMenu.pagingElement.pages, i) - table.insert(inGameMenu.pagingElement.pages, insertAt, page) - break - end - end inGameMenu.pagingElement:updateAbsolutePosition() inGameMenu.pagingElement:updatePageMapping() inGameMenu:registerPage(frame, position, predicate) inGameMenu:addPageTab(frame, iconFile, iconUVs) + if frame.updateAbsolutePosition ~= nil then + frame:updateAbsolutePosition() + end inGameMenu:rebuildTabList() return true end @@ -733,7 +775,7 @@ local function loadGui() -- icon.dds er 512×512. Uden referencestørrelsen normaliserer getUVs mod 1024×1024, -- og fanen ville kun vise ikonets øverste venstre fjerdedel. local iconUVs = GuiUtils.getUVs({0, 0, 512, 512}, {512, 512}) - if not insertInGameMenuPage(frame, "smartPickupFrame", iconFile, iconUVs, 2, function() return g_currentMission ~= nil end) then + if not insertInGameMenuPage(frame, "smartPickupFrame", iconFile, iconUVs, function() return g_currentMission ~= nil end) then Logging.warning(ADSmartPickup.LOG_PREFIX .. "kunne ikke indsætte fanen i ESC-menuen") return end diff --git a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua index 2fbd6db..1adfa67 100644 --- a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua +++ b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua @@ -340,7 +340,9 @@ function SmartPickupFrame:fillPoolRows(adEnv) local occupied = {} for _, claim in ipairs(ADUnloadWait.getClaims()) do occupied[claim.marker.group] = occupied[claim.marker.group] or {} - table.insert(occupied[claim.marker.group], ADRunsController.getVehicleLabel(adEnv, claim.vehicle)) + -- kun løbenummeret ("#31") — fulde navne klippes af i rækken + local label = ADRunsController.getVehicleLabel(adEnv, claim.vehicle) + table.insert(occupied[claim.marker.group], string.match(label, "%(#(%d+)%)") and ("#" .. string.match(label, "%(#(%d+)%)")) or label) end for _, pool in ipairs(ADRunsController.getWaitPools(adEnv)) do local names = occupied[pool.group] or {} diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index 4e647fd..33dd0ad 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.9.1.0 + 1.9.2.0 <en>AD Smart Pickup</en> diff --git a/tests/test_adRunsController.lua b/tests/test_adRunsController.lua index 5949eb3..bf70daa 100644 --- a/tests/test_adRunsController.lua +++ b/tests/test_adRunsController.lua @@ -215,10 +215,16 @@ fastracA.ad.stateModule.active = true ADRunsController.startRun(adEnv, cow.id) check("C7 ikke dobbelt-startet", fastracA.started, 1) --- C8: mål-markører og Wait-puljer til dropdowns +-- C8: mål-markører (kun stalde og fabrikker — siloer tager alt og er ikke mål; stalde først, +-- så fabrikker, så navn) og Wait-puljer til dropdowns +unloadAt[94].owningPlaceable = {getHusbandryFillLevel = function() return 0 end} +addMarker(96, "8 Silo Storage", "Siloer", 100, 0); unloadAt[96] = {accepts = {[STRAW] = 1}, owningPlaceable = {spec_silo = {}}} +addMarker(97, "TMR Unload", "Fabrication", 900, 0); unloadAt[97] = {accepts = {[STRAW] = 1}, owningPlaceable = {spec_productionPoint = {}}} +addMarker(98, "Bull Food", "Cow Food", 5100, 0); unloadAt[98] = {accepts = {[STRAW] = 1}, owningPlaceable = {getHusbandryFillLevel = function() return 0 end}} local targets = ADRunsController.getTargetMarkers(adEnv, 1) -check("C8 kun markører med aflæsningsstation", #targets, 1) -check("C8 navn", targets[1].name, "Cow 1 Food") +check("C8 kun stalde og fabrikker (ikke siloen)", #targets, 3) +check("C8 stalde først, alfabetisk", targets[1].name .. "," .. targets[2].name, "Bull Food,Cow 1 Food") +check("C8 fabrik sidst", targets[3].name, "TMR Unload") local pools = ADRunsController.getWaitPools(adEnv) check("C8 to puljer (F91 er ikke en Wait-mappe)", #pools, 2) check("C8 sorteret", pools[1].group, "Cow Wait") diff --git a/tests/test_adSmartPickup.lua b/tests/test_adSmartPickup.lua index 0be8ef1..51fa14f 100644 --- a/tests/test_adSmartPickup.lua +++ b/tests/test_adSmartPickup.lua @@ -840,6 +840,37 @@ local eLevel, eCapacity = ADSmartPickup.getStationLevelAndCapacity(unloadStation check("E3 niveau i målet", eLevel, 40000) check("E3 kapacitet i målet", eCapacity, 100000) check("E3 vare målet ikke tager -> nil", ADSmartPickup.getStationLevelAndCapacity(unloadStations[1], MILK, 1), nil) + +-- E4: FS25-stald med foder-spec: foder læses fra spec_husbandryFood (fælles kapacitet), +-- fodertyper fra animalFoodSystem, halm fra spec_husbandryStraw; outputs (mælk) tages ikke +buildCowWorld() +local foodLevels = {[TMR] = 10000, [HAY] = 5000} +local fs25Barn = { + getName = function() return "FS25-kostald" end, + spec_husbandryFood = {fillLevels = foodLevels, capacity = 100000, animalTypeIndex = 7}, + spec_husbandryStraw = {fillLevel = 20000, capacity = 300000}, + -- staldens egen "understøtter" siger også ja til mælk (output) — må ikke blive et mål + getHusbandryIsFillTypeSupported = function(_, fillType) return fillType == MILK or fillType == STRAW or fillType == TMR end, + getHusbandryFillLevel = function(_, fillType) if fillType == STRAW then return 20000 end return 0 end, + getHusbandryCapacity = function(_, fillType) if fillType == STRAW then return 300000 end return 0 end, +} +g_currentMission.animalFoodSystem = { + getAnimalFood = function(_, animalTypeIndex) if animalTypeIndex == 7 then return {groups = {{fillTypes = {TMR}}, {fillTypes = {HAY, GRASS}}}} end end, + getMixturesByAnimalTypeIndex = function() return {} end, +} +unloadStations[1].targetStorages = nil +unloadStations[1].owningPlaceable = fs25Barn +local tmrLevel, tmrCapacity = ADSmartPickup.getStationLevelAndCapacity(unloadStations[1], TMR, 1) +check("E4 TMR-niveau fra foder-spec", tmrLevel, 10000) +check("E4 TMR-kapacitet = eget + fælles fri plads", tmrCapacity, 10000 + (100000 - 15000)) +local grassLevel, grassCapacity = ADSmartPickup.getStationLevelAndCapacity(unloadStations[1], GRASS, 1) +check("E4 græs (fodergruppe uden lager) niveau 0", grassLevel, 0) +check("E4 græs kapacitet = fælles fri plads", grassCapacity, 85000) +local strawLevel, strawCapacity = ADSmartPickup.getStationLevelAndCapacity(unloadStations[1], STRAW, 1) +check("E4 halm via husbandry-opslag", strawLevel .. "/" .. strawCapacity, "20000/300000") +check("E4 mælk (output) er ikke et mål", ADSmartPickup.getStationLevelAndCapacity(unloadStations[1], MILK, 1), nil) +check("E4 sukkerroer (ikke foder) er ikke et mål", ADSmartPickup.getStationLevelAndCapacity(unloadStations[1], SUGARBEET, 1), nil) +g_currentMission.animalFoodSystem = nil check("E3 farmId", ADSmartPickup.getFarmId(eDriver), 1) -- L6: samme valg logges kun én gang