diff --git a/FS25_ADSmartPickup/adBuildings.lua b/FS25_ADSmartPickup/adBuildings.lua index 1aa7620..3484f94 100644 --- a/FS25_ADSmartPickup/adBuildings.lua +++ b/FS25_ADSmartPickup/adBuildings.lua @@ -104,6 +104,11 @@ local function collectNodes(placeable, loadStations, unloadStations) end if #nodes > 0 then table.insert(loadGroups, nodes) end end + -- staldens foderkrybbe (hønsehus: kun krybben, ingen trigger i aflæsningsstationen) + local food = placeable.spec_husbandryFood + local trough = food ~= nil and food.feedingTrough or nil + local troughNode = trough ~= nil and (trough.exactFillRootNode or trough.triggerNode) or nil + if troughNode ~= nil then table.insert(unloadNodes, troughNode) end -- stald med egne pallespawnere (hønsehus: æg, fårestald: uld) — én læssegruppe pr. spawner local husbandryPallets = placeable.spec_husbandryPallets local seenSpawners = {} diff --git a/FS25_ADSmartPickup/adFlowView.lua b/FS25_ADSmartPickup/adFlowView.lua new file mode 100644 index 0000000..54f4dcc --- /dev/null +++ b/FS25_ADSmartPickup/adFlowView.lua @@ -0,0 +1,119 @@ +-- AD Flow View +-- Rene regler for Flows-fanens oprydning (ingen spil-API): tavlens grupper og rækkefølge, hvilke +-- traktorer og bygninger der vises som standard, og hvad "Vis alle" tilføjer. + +ADFlowView = {} + +-- Naturlig sortering: tal i navne sammenlignes som tal ("Biogas 9" < "Biogas 10"). +local function naturalKey(text) + local parts = {} + local value = string.lower(tostring(text or "")) + local position = 1 + while position <= #value do + local digits = string.match(value, "^%d+", position) + if digits ~= nil then + table.insert(parts, {number = tonumber(digits)}) + position = position + #digits + else + local chunk = string.match(value, "^%D+", position) + table.insert(parts, {text = chunk}) + position = position + #chunk + end + end + return parts +end + +function ADFlowView.naturalLess(a, b) + local ka, kb = naturalKey(a), naturalKey(b) + for index = 1, math.max(#ka, #kb) do + local pa, pb = ka[index], kb[index] + if pa == nil then return true end + if pb == nil then return false end + if pa.number ~= nil and pb.number ~= nil then + if pa.number ~= pb.number then return pa.number < pb.number end + elseif pa.number ~= nil or pb.number ~= nil then + return pa.number ~= nil + elseif pa.text ~= pb.text then + return pa.text < pb.text + end + end + return false +end + +local function sortedBy(list, field) + local copy = {} + for _, item in ipairs(list or {}) do table.insert(copy, item) end + table.sort(copy, function(a, b) + if a[field] ~= b[field] then return ADFlowView.naturalLess(a[field], b[field]) end + return tostring(a.id) < tostring(b.id) + end) + return copy +end + +-- Tavlen: forsyning ("Ind til bygning") først, så udkørsel ("Ud fra bygning"); hver gruppe efter navn. +-- {{kind, runs}, ...}; tomme grupper udelades. +function ADFlowView.boardGroups(runs) + local byKind = {supply = {}, outbound = {}} + for _, run in ipairs(runs or {}) do + table.insert(byKind[run.kind == "outbound" and "outbound" or "supply"], run) + end + local groups = {} + for _, kind in ipairs({"supply", "outbound"}) do + if #byKind[kind] > 0 then + table.insert(groups, {kind = kind, runs = sortedBy(byKind[kind], "name")}) + end + end + return groups +end + +-- Samme rækkefølge som tavlen, som én liste (flow-vælgeren). +function ADFlowView.orderedRuns(runs) + local result = {} + for _, group in ipairs(ADFlowView.boardGroups(runs)) do + for _, run in ipairs(group.runs) do table.insert(result, run) end + end + return result +end + +-- Traktorer: vehicles = {{id, label, state = "in"|"free"|"other"|"blocked"}}. Standard: flowets egne, +-- så ledige der kan bruges. showAll tilføjer traktorer i andre flows og låste (vogn passer ikke). +local STATE_ORDER = {"in", "free", "other", "blocked"} + +function ADFlowView.vehicleRows(vehicles, showAll) + local byState = {} + for _, entry in ipairs(vehicles or {}) do + byState[entry.state] = byState[entry.state] or {} + table.insert(byState[entry.state], entry) + end + local rows = {} + for _, state in ipairs(STATE_ORDER) do + if showAll or state == "in" or state == "free" then + for _, entry in ipairs(sortedBy(byState[state], "label")) do table.insert(rows, entry) end + end + end + return rows +end + +function ADFlowView.hiddenCount(all, shown) + return #(all or {}) - #(shown or {}) +end + +-- Bygninger: buildings = {{id, name, hasMarker}}, chosen = {[id] = true}. Valgte først; bygninger uden +-- markør kun hvis valgte eller showAll (de kan alligevel ikke bruges før en markør er sat). +function ADFlowView.buildingRows(buildings, chosen, showAll) + local picked, withMarker, withoutMarker = {}, {}, {} + for _, building in ipairs(buildings or {}) do + if chosen ~= nil and chosen[building.id] then + table.insert(picked, building) + elseif building.hasMarker then + table.insert(withMarker, building) + elseif showAll then + table.insert(withoutMarker, building) + end + end + local rows = {} + for _, list in ipairs({sortedBy(picked, "name"), sortedBy(withMarker, "name"), sortedBy(withoutMarker, "name")}) do + for _, building in ipairs(list) do table.insert(rows, building) end + end + return rows +end diff --git a/FS25_ADSmartPickup/adSmartPickup.lua b/FS25_ADSmartPickup/adSmartPickup.lua index 3705a19..3e44926 100644 --- a/FS25_ADSmartPickup/adSmartPickup.lua +++ b/FS25_ADSmartPickup/adSmartPickup.lua @@ -167,8 +167,48 @@ local function getLoadStationAtMarker(adEnv, wayPoint, farmId) return getClosestStation(adEnv, g_currentMission.storageSystem:getLoadingStations(), "loadTriggers", LOAD_TRIGGER_NODE_FIELDS, wayPoint, farmId) end +-- Staldes foderkrybbe (spec_husbandryFood.feedingTrough, samme tippe-trigger som AutoDrive bruger) står +-- ikke altid i aflæsningsstationens unloadTriggers — hønsehuset har KUN krybben. Opslaget får derfor en +-- proxy pr. krybbe der peger på staldens station (eller en minimal station). Caches i UNLOAD_CACHE_MS. +ADSmartPickup.UNLOAD_CACHE_MS = 1000 +local unloadCache = {list = nil, time = -math.huge} + +function ADSmartPickup.invalidateUnloadCandidates() + unloadCache.list, unloadCache.time = nil, -math.huge +end + +local function getUnloadCandidates() + local now = g_currentMission ~= nil and type(g_currentMission.time) == "number" and g_currentMission.time or nil + if unloadCache.list ~= nil and now ~= nil and (now - unloadCache.time) < ADSmartPickup.UNLOAD_CACHE_MS then + return unloadCache.list + end + local list, byPlaceable = {}, {} + for _, station in pairs(g_currentMission.storageSystem:getUnloadingStations()) do + table.insert(list, station) + if station.owningPlaceable ~= nil and byPlaceable[station.owningPlaceable] == nil then + byPlaceable[station.owningPlaceable] = station + end + end + local placeables = g_currentMission.placeableSystem ~= nil and g_currentMission.placeableSystem.placeables or {} + for _, placeable in pairs(placeables) do + local food = placeable.spec_husbandryFood + local trough = food ~= nil and food.feedingTrough or nil + local node = trough ~= nil and (trough.exactFillRootNode or trough.triggerNode) or nil + if node ~= nil then + local station = byPlaceable[placeable] or { + owningPlaceable = placeable, + -- spillets adgangstjek kalder getOwnerFarmId på stationen + getOwnerFarmId = function() return placeable.getOwnerFarmId ~= nil and placeable:getOwnerFarmId() or placeable.ownerFarmId end, + } + table.insert(list, setmetatable({unloadTriggers = {{exactFillRootNode = node}}}, {__index = station})) + end + end + unloadCache.list, unloadCache.time = list, now or -math.huge + return list +end + local function getUnloadStationAtMarker(adEnv, wayPoint, farmId) - return getClosestStation(adEnv, g_currentMission.storageSystem:getUnloadingStations(), "unloadTriggers", UNLOAD_TRIGGER_NODE_FIELDS, wayPoint, farmId) + return getClosestStation(adEnv, getUnloadCandidates(), "unloadTriggers", UNLOAD_TRIGGER_NODE_FIELDS, wayPoint, farmId) end -- Station ved et AutoDrive-waypoint (bruges af runs-controlleren og læs-byttet); nil uden station. diff --git a/FS25_ADSmartPickup/gui/SmartPickupFlowTab.lua b/FS25_ADSmartPickup/gui/SmartPickupFlowTab.lua index ec35f84..ea1a6c3 100644 --- a/FS25_ADSmartPickup/gui/SmartPickupFlowTab.lua +++ b/FS25_ADSmartPickup/gui/SmartPickupFlowTab.lua @@ -70,10 +70,14 @@ function SmartPickupFrame:fillBoardRows(adEnv) local rows = {} local header = self:addTextRow(self.i18n:getText("spu_section_flows")) table.insert(rows, header.parent) - for _, run in ipairs(ADRunsController.store.runs) do - local title = self:addTextRow(self:describeFlow(adEnv, run, run.id == self.selectedRunId)) - table.insert(rows, title.parent) - table.insert(self.boardRows, {run = run, title = title}) + -- grupperet: Ind til bygning, så Ud fra bygning — hver efter navn + for _, group in ipairs(ADFlowView.boardGroups(ADRunsController.store.runs)) do + table.insert(rows, self:addTextRow(self.i18n:getText(group.kind == "outbound" and "spu_board_outbound" or "spu_board_supply")).parent) + for _, run in ipairs(group.runs) do + local title = self:addTextRow(self:describeFlow(adEnv, run, run.id == self.selectedRunId)) + table.insert(rows, title.parent) + table.insert(self.boardRows, {run = run, title = title}) + end end if #ADRunsController.store.runs == 0 then table.insert(rows, self:addTextRow(self.i18n:getText("spu_noRuns")).parent) @@ -126,17 +130,29 @@ function SmartPickupFrame:fillBuildingRows(adEnv, run, role) local rowKind = role == "load" and "sourceBuilding" or role == "store" and "storeBuilding" or "targetBuilding" for _, id in ipairs(ids or {}) do chosen[id] = true end local count = 0 + -- kandidater -> valgte først, så med markør; uden markør kun med "Vis alle bygninger" + local candidates = {} for _, building in ipairs(ADBuildings.list(adEnv, g_currentMission:getFarmId())) do -- "Lager til": siloer og gårdens fabrikker (fx BGA) — markøren er bygningens aflæsningsmarkør local fits = (role == "load" and building.canBeSource) or (role == "unload" and building.canBeTarget) or (role == "store" and (building.kind == "silo" or building.kind == "factory")) if fits then - count = count + 1 - local row = self:addRow(self.ingredientPrefab, {kind = rowKind, buildingId = building.id}) - row:getDescendantByName("title"):setText(self:describeBuilding(building, role == "store" and "unload" or role)) - row:getDescendantByName("option"):setState(chosen[building.id] and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false) + local markers = ADBuildings.getMarkers ~= nil and ADBuildings.getMarkers(building, role == "store" and "unload" or role) or {} + table.insert(candidates, {id = building.id, name = building.name, hasMarker = markers[1] ~= nil, building = building}) end end + local shown = ADFlowView.buildingRows(candidates, chosen, self.showAllBuildings == true) + local hiddenBuildings = ADFlowView.hiddenCount(candidates, shown) + if hiddenBuildings > 0 or self.showAllBuildings then + self:addToggleRow("showAllBuildings", string.format(self.i18n:getText("spu_showAllBuildings"), hiddenBuildings), {self.i18n:getText("ui_no"), self.i18n:getText("ui_yes")}, self.showAllBuildings == true) + end + for _, entry in ipairs(shown) do + local building = entry.building + count = count + 1 + local row = self:addRow(self.ingredientPrefab, {kind = rowKind, buildingId = building.id}) + row:getDescendantByName("title"):setText(self:describeBuilding(building, role == "store" and "unload" or role)) + row:getDescendantByName("option"):setState(chosen[building.id] and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false) + end -- valgte bygninger der ikke længere findes (solgt) for _, id in ipairs(ids or {}) do if ADBuildings.find(adEnv, g_currentMission:getFarmId(), id) == nil then diff --git a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua index 4cfb039..655c357 100644 --- a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua +++ b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua @@ -267,7 +267,11 @@ function SmartPickupFrame:setStaticRowsVisible(tab, run) end end +-- Samme rækkefølge som tavlen: Ind til bygning først, så Ud fra bygning, hver efter navn. function SmartPickupFrame:getRunsForTab() + if ADFlowView ~= nil then + return ADFlowView.orderedRuns(ADRunsController.store.runs) + end return ADRunsController.store.runs end @@ -433,28 +437,46 @@ function SmartPickupFrame:fillWaitPoolOption(adEnv, run) self.waitPoolOption:setState(index, false) end +-- Traktorer: som standard flowets egne og de ledige der kan bruges; "Vis alle" tilføjer traktorer i andre +-- flows og låste (vognen passer ikke) — de er tit mange og gør listen uoverskuelig. function SmartPickupFrame:fillVehicleRows(adEnv, run) local inRun = {} for _, id in ipairs(run.vehicleIds) do inRun[id] = true end + local entries = {} for _, vehicle in ipairs(ADRunsController.getFarmVehicles(adEnv)) do local id = ADRunsController.getVehicleId(vehicle) + local otherRun = ADRunsController.getRunForVehicle(vehicle) + local state = "free" + if inRun[id] then + state = "in" + elseif otherRun ~= nil and otherRun.id ~= run.id then + state = "other" + elseif SmartPickupFrame.VEHICLE_BLOCKING_REASONS[ADRunsController.validateVehicle(adEnv, run, vehicle).reason] then + state = "blocked" + end + table.insert(entries, {id = id, label = ADRunsController.getVehicleLabel(adEnv, vehicle), state = state, vehicle = vehicle, otherRun = otherRun}) + end + local rows = ADFlowView.vehicleRows(entries, self.showAllVehicles == true) + local hidden = ADFlowView.hiddenCount(entries, rows) + if hidden > 0 or self.showAllVehicles then + self.showAllVehiclesOption = self:addToggleRow("showAllVehicles", string.format(self.i18n:getText("spu_showAllVehicles"), hidden), {self.i18n:getText("ui_no"), self.i18n:getText("ui_yes")}, self.showAllVehicles == true) + end + for _, entry in ipairs(rows) do + local vehicle = entry.vehicle local row = self:addRow(self.vehiclePrefab, {kind = "vehicle", vehicle = vehicle, row = nil}) local option, title = row:getDescendantByName("option"), row:getDescendantByName("title") self.rowData[option].row = row - option:setState(inRun[id] and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false) - -- Traktorer i et ANDET run kan ikke vælges her (én traktor = ét run); rækken viser hvor den er. - local otherRun = ADRunsController.getRunForVehicle(vehicle) - if otherRun ~= nil and otherRun.id ~= run.id then + option:setState(entry.state == "in" and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false) + if entry.state == "other" then + -- én traktor = ét flow; rækken viser hvor den er option:setDisabled(true) self.rowData[option].locked = true - title:setText(string.format("%s — %s", ADRunsController.getVehicleLabel(adEnv, vehicle), string.format(self.i18n:getText("spu_inRun"), tostring(otherRun.name)))) + title:setText(string.format("%s — %s", entry.label, string.format(self.i18n:getText("spu_inRun"), tostring(entry.otherRun.name)))) else - title:setText(self:describeVehicle(adEnv, run, vehicle, inRun[id])) - -- Vogn der ikke kan bære run'ets varer (ingen vogn / forkert vogn / paller uden UAL) kan ikke - -- vælges; er den allerede i run'et, kan den stadig fravælges. - if not inRun[id] and SmartPickupFrame.VEHICLE_BLOCKING_REASONS[ADRunsController.validateVehicle(adEnv, run, vehicle).reason] then + title:setText(self:describeVehicle(adEnv, run, vehicle, entry.state == "in")) + if entry.state == "blocked" then option:setDisabled(true) self.rowData[option].locked = true end @@ -678,6 +700,11 @@ function SmartPickupFrame:onClickIngredient(state, element) return self:onClickCollectRound(state) elseif data ~= nil and data.kind == "direction" then return self:onClickDirection(state) + elseif data ~= nil and (data.kind == "showAllVehicles" or data.kind == "showAllBuildings") then + local isOn = state == SmartPickupFrame.STATE_ON + if data.kind == "showAllVehicles" then self.showAllVehicles = isOn else self.showAllBuildings = isOn end + self:requestRebuild() + return elseif data ~= nil and (data.kind == "sourceBuilding" or data.kind == "targetBuilding" or data.kind == "storeBuilding") then return self:onClickBuilding(state, element) end diff --git a/FS25_ADSmartPickup/l10n/l10n_da.xml b/FS25_ADSmartPickup/l10n/l10n_da.xml index 958fb2f..5738c0c 100644 --- a/FS25_ADSmartPickup/l10n/l10n_da.xml +++ b/FS25_ADSmartPickup/l10n/l10n_da.xml @@ -135,6 +135,10 @@ + + + + diff --git a/FS25_ADSmartPickup/l10n/l10n_en.xml b/FS25_ADSmartPickup/l10n/l10n_en.xml index fc254f1..1f407e2 100644 --- a/FS25_ADSmartPickup/l10n/l10n_en.xml +++ b/FS25_ADSmartPickup/l10n/l10n_en.xml @@ -135,6 +135,10 @@ + + + + diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index d0f9eb7..8f3e9e0 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.18.8.0 + 1.19.0.0 <en>AD Smart Pickup</en> @@ -21,6 +21,7 @@ + diff --git a/docs/BRUGERMANUAL.md b/docs/BRUGERMANUAL.md index 82272d1..afae118 100644 --- a/docs/BRUGERMANUAL.md +++ b/docs/BRUGERMANUAL.md @@ -1,6 +1,6 @@ # AD Smart Pickup — brugermanual -Version 1.18.8 · Farming Simulator 25 · tillæg til AutoDrive +Version 1.19.0 · Farming Simulator 25 · tillæg til AutoDrive *English version: [USER_MANUAL.md](USER_MANUAL.md)* @@ -139,7 +139,9 @@ Tryk **ESC** og vælg fanen **Smart Pickup**. Fanen har fem underfaner: Foder stald: efter behov · alle siloer → Cow Barn 1 · behov · JCB 1 + JCB 2 · Stoppet (2 traktorer) ``` -Linjen læses sådan: **navn: varer · fra → til · regel · traktorer · status**. +Linjen læses sådan: **navn: varer · fra → til · regel · traktorer · status**. Tavlen er delt i +**Ind til bygning** og **Ud fra bygning**, og hver gruppe er sorteret efter navn. Flow-vælgeren følger +samme rækkefølge. - Et `!` efter et bygningsnavn betyder, at bygningen mangler en markør. - `!!` i status er en advarsel, fx at et salgssted ikke kan nås. @@ -172,7 +174,8 @@ Cow Barn (large) — Cow 1 Milk 6 m · Cow 1 Slurry 4 m Silo 7 (silo) — INGEN MARKØR inden for 40 m — sæt en AD-markør ved triggeren ``` -Sæt **Ja** ud for de bygninger, flowet skal bruge. Listen er sorteret efter navn, så *Silo 9* +Sæt **Ja** ud for de bygninger, flowet skal bruge. De valgte står øverst. Bygninger uden markør er +skjult, indtil du slår **Vis alle bygninger** til. Listen er ellers sorteret efter navn, så *Silo 9* kommer før *Silo 10*. En bygning, du har solgt, står som *bygningen findes ikke længere*. --- @@ -396,6 +399,8 @@ Nederst i flowet står gårdens køretøjer med kapacitet. Sæt **Ja** ud for de - Kapaciteten står i liter for bulkvogne og som *autoload (paller)* for UAL-vogne. - **Køretøjer, der ikke kan bruges, er låst** og viser grunden. Grundene er beskrevet i tabellen. +- Som standard vises flowets egne traktorer og de ledige, der kan bruges. Traktorer i andre flows og + låste traktorer vises med **Vis alle traktorer**. - Et køretøj kan kun være med i ét flow. Flyttes det, står der *flyttet fra 'andet flow'*. - Fjerner du en kørende traktor fra et flow, stoppes den. - **Navne:** traktorerne får automatisk AutoDrive-førernavn efter deres flow, fx *Milk Collect 1* og diff --git a/docs/USER_MANUAL.md b/docs/USER_MANUAL.md index 3b0e859..96f9716 100644 --- a/docs/USER_MANUAL.md +++ b/docs/USER_MANUAL.md @@ -1,6 +1,6 @@ # AD Smart Pickup — user manual -Version 1.18.8 · Farming Simulator 25 · add-on for AutoDrive +Version 1.19.0 · Farming Simulator 25 · add-on for AutoDrive *Dansk version: [BRUGERMANUAL.md](BRUGERMANUAL.md)* @@ -141,7 +141,9 @@ At the top there is one line per flow. The selected flow is marked with `>`. Barn feed: by need · all silos → Cow Barn 1 · need · JCB 1 + JCB 2 · Stopped (2 tractors) ``` -Read the line as **name: goods · from → to · rule · tractors · status**. +Read the line as **name: goods · from → to · rule · tractors · status**. The board is split into +**Into building** and **Out of building**, and each group is sorted by name. The flow selector follows +the same order. - A `!` after a building name means the building has no marker. - `!!` in the status is a warning, such as a selling point that cannot be reached. @@ -174,7 +176,8 @@ Cow Barn (large) — Cow 1 Milk 6 m · Cow 1 Slurry 4 m Silo 7 (silo) — NO MARKER within 40 m — place an AD marker at the trigger ``` -Set **Yes** on the buildings the flow should use. The list is sorted by name, so *Silo 9* comes +Set **Yes** on the buildings the flow should use. The chosen ones are at the top. Buildings without a +marker are hidden until you switch on **Show all buildings**. Otherwise the list is sorted by name, so *Silo 9* comes before *Silo 10*. A building you have sold shows as *building no longer exists*. --- @@ -401,6 +404,8 @@ ones that should drive. - Capacity is shown in litres for bulk trailers and as *autoload (pallets)* for UAL trailers. - **Vehicles that cannot be used are locked** and show the reason. The reasons are in the table. +- By default the flow's own tractors and the free usable ones are shown. Tractors in other flows and + locked tractors appear with **Show all tractors**. - A vehicle can only be in one flow. If it is moved, it shows *moved from 'other flow'*. - If you remove a running tractor from a flow, it is stopped. - **Names:** tractors automatically get an AutoDrive driver name after their flow, such as diff --git a/tests/test_adBuildings.lua b/tests/test_adBuildings.lua index 26ba01d..396b12f 100644 --- a/tests/test_adBuildings.lua +++ b/tests/test_adBuildings.lua @@ -189,8 +189,9 @@ end -- B-COOP: hønsehus — foder aflæsses i den ene ende, æg-paller spawnes i den anden (ingen læssestation) do local coop = placeable("coop1", "Chicken Coop", 10000, 0, {getHusbandryFillLevel = function() return 0 end, + spec_husbandryFood = {feedingTrough = {exactFillRootNode = {x = 9970, z = 0}}}, spec_husbandryPallets = {fillTypeIndexToPalletSpawner = {[99] = {spawnPlaces = {{startNode = {x = 10030, z = 0}}}}}}}) - table.insert(unloadStations, {owningPlaceable = coop, unloadTriggers = {{exactFillRootNode = {x = 9970, z = 0}}}}) + table.insert(unloadStations, {owningPlaceable = coop, unloadTriggers = {}}) -- som chickenBarnBig: tom station addMarker(61, "Chicken Food", "Chicken", 9965, 0) addMarker(62, "Egg Pickup", "Chicken", 10035, 0) ADBuildings.invalidate() diff --git a/tests/test_adFlowView.lua b/tests/test_adFlowView.lua new file mode 100644 index 0000000..3fefd5f --- /dev/null +++ b/tests/test_adFlowView.lua @@ -0,0 +1,64 @@ +-- Kør: luajit tests/test_adFlowView.lua (fra repo-roden) +-- Ren oprydning af Flows-fanen: tavlens rækkefølge, hvilke traktorer og bygninger der vises. + +dofile("FS25_ADSmartPickup/adFlowView.lua") +local V = ADFlowView + +local failures = 0 +local function check(name, actual, expected) + if actual == expected then + print("OK " .. name) + else + failures = failures + 1 + print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual))) + end +end +local function ids(list, field) + local out = {} + for _, item in ipairs(list) do table.insert(out, tostring(item[field or "id"])) end + return table.concat(out, ",") +end + +-- FV1: tavlen — forsyning (Ind) først, så udkørsel (Ud); hver gruppe sorteret efter navn (naturligt) +local runs = { + {id = 1, name = "Pig Food", kind = "supply"}, + {id = 2, name = "Milk Sale", kind = "outbound"}, + {id = 3, name = "Cow Food", kind = "supply"}, + {id = 4, name = "Biogas 10", kind = "supply"}, + {id = 5, name = "Biogas 9", kind = "supply"}, + {id = 6, name = "Digestate", kind = "outbound"}, +} +local groups = V.boardGroups(runs) +check("FV1 to grupper", #groups, 2) +check("FV1 Ind først", groups[1].kind, "supply") +check("FV1 Ind sorteret (Biogas 9 før 10)", ids(groups[1].runs), "5,4,3,1") +check("FV1 Ud sorteret", ids(groups[2].runs), "6,2") +check("FV1 samlet rækkefølge til vælgeren", ids(V.orderedRuns(runs)), "5,4,3,1,6,2") +check("FV1 tom gruppe udelades", #V.boardGroups({{id = 1, name = "A", kind = "supply"}}), 1) + +-- FV2: traktorer — egne først, så ledige brugbare; andre flows og låste kun med "vis alle" +local vehicles = { + {id = "a", label = "Zetor", state = "free"}, + {id = "b", label = "Fendt", state = "in"}, + {id = "c", label = "Case", state = "other"}, + {id = "d", label = "Claas", state = "blocked"}, + {id = "e", label = "Deutz", state = "free"}, +} +check("FV2 standard: egne + ledige, sorteret", ids(V.vehicleRows(vehicles, false)), "b,e,a") +check("FV2 vis alle: + andre flows + låste til sidst", ids(V.vehicleRows(vehicles, true)), "b,e,a,c,d") +check("FV2 antal skjulte", V.hiddenCount(vehicles, V.vehicleRows(vehicles, false)), 2) + +-- FV3: bygninger — valgte først; uden markør skjult medmindre valgt eller "vis alle" +local buildings = { + {id = "s2", name = "Silo 2", hasMarker = true}, + {id = "s10", name = "Silo 10", hasMarker = true}, + {id = "x", name = "Gammel lade", hasMarker = false}, + {id = "y", name = "Anden uden", hasMarker = false}, + {id = "a", name = "Anlæg", hasMarker = true}, +} +local chosen = {s10 = true, y = true} +check("FV3 standard: valgte først, så med markør", ids(V.buildingRows(buildings, chosen, false)), "y,s10,a,s2") +check("FV3 vis alle: uden markør til sidst", ids(V.buildingRows(buildings, chosen, true)), "y,s10,a,s2,x") + +print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) +os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adSmartPickup.lua b/tests/test_adSmartPickup.lua index bdf0a17..5677889 100644 --- a/tests/test_adSmartPickup.lua +++ b/tests/test_adSmartPickup.lua @@ -1259,5 +1259,22 @@ check("X3 Wait-logik springer udkørsel over", okWait, true) FS25_AutoDrive.AutoDrive.getAllDischargeableUnits = originalUnits ADOutbound = nil +-- FT: hønsehus — foderkrybben (spec_husbandryFood.feedingTrough) står ikke i aflæsningsstationens +-- unloadTriggers; stationsopslaget ved markøren skal alligevel finde hønsehuset +do + local coop = {getHusbandryFillLevel = function() return 0 end, getName = function() return "Chicken Coop" end, + spec_husbandryFood = {feedingTrough = {exactFillRootNode = 9901}}} + nodePositions[9901] = {x = 20000, z = 3} + local coopStation = {owningPlaceable = coop, unloadTriggers = {}} + table.insert(unloadStations, coopStation) + g_currentMission.placeableSystem = {placeables = {coop}} + ADSmartPickup.invalidateUnloadCandidates() + addMarker(95, "Chicken 1 food", "Chicken", 20000, 10) + local found = ADSmartPickup.getUnloadStationAtWayPointId(FS25_AutoDrive, 95, 1) + check("FT krybbe findes via markøren", found ~= nil and found.owningPlaceable == coop, true) + g_currentMission.placeableSystem = nil + ADSmartPickup.invalidateUnloadCandidates() +end + print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)