diff --git a/FS25_ADSmartPickup/adBuildings.lua b/FS25_ADSmartPickup/adBuildings.lua index 4f506f3..1aa7620 100644 --- a/FS25_ADSmartPickup/adBuildings.lua +++ b/FS25_ADSmartPickup/adBuildings.lua @@ -104,6 +104,19 @@ local function collectNodes(placeable, loadStations, unloadStations) end if #nodes > 0 then table.insert(loadGroups, nodes) end end + -- stald med egne pallespawnere (hønsehus: æg, fårestald: uld) — én læssegruppe pr. spawner + local husbandryPallets = placeable.spec_husbandryPallets + local seenSpawners = {} + for _, palletSpawner in pairs(husbandryPallets ~= nil and husbandryPallets.fillTypeIndexToPalletSpawner or {}) do + if not seenSpawners[palletSpawner] then + seenSpawners[palletSpawner] = true + local nodes = {} + for _, place in ipairs(palletSpawner.spawnPlaces or {}) do + if place.startNode ~= nil then table.insert(nodes, place.startNode) end + end + if #nodes > 0 then table.insert(loadGroups, nodes) end + end + end if placeable.spec_objectStorage ~= nil and placeable.rootNode ~= nil then table.insert(loadGroups, {placeable.rootNode}) table.insert(unloadNodes, placeable.rootNode) @@ -249,9 +262,25 @@ local function isSellPlaceable(placeable, unloadStations) return false end +-- Brændstof er ikke en vare der køres rundt (gårdens tankanlæg har en læssestation til diesel). +local FUEL_FILL_TYPES = {DIESEL = true, DEF = true, ELECTRICCHARGE = true, METHANE = true} + +local function isFuelOnly(station) + local supported = station.supportedFillTypes + if supported == nil or next(supported) == nil or g_fillTypeManager == nil then + return false + end + for fillType, isSupported in pairs(supported) do + if isSupported and not FUEL_FILL_TYPES[tostring(g_fillTypeManager:getFillTypeNameByIndex(fillType))] then + return false + end + end + return true +end + local function hasOwnLoadingStation(placeable, loadStations) for _, station in pairs(loadStations) do - if station.owningPlaceable == placeable then return true end + if station.owningPlaceable == placeable and not isFuelOnly(station) then return true end end return false end diff --git a/FS25_ADSmartPickup/adSources.lua b/FS25_ADSmartPickup/adSources.lua index 8691402..64d5734 100644 --- a/FS25_ADSmartPickup/adSources.lua +++ b/FS25_ADSmartPickup/adSources.lua @@ -11,6 +11,17 @@ ADSources.MAX_DISTANCE = 40 -- Paller der står inden for denne afstand af fabrikkens pallespawner tæller som fabrikkens output. ADSources.PALLET_RADIUS = 25 +-- Staldens egne pallespawnere (hønsehus: æg, fårestald: uld): {[fillType] = spawner}. +local function getHusbandryPalletSpawners(placeable) + local spec = placeable ~= nil and placeable.spec_husbandryPallets or nil + return spec ~= nil and spec.fillTypeIndexToPalletSpawner or nil +end + +local function firstSpawnerNode(spawner) + return spawner ~= nil and spawner.spawnPlaces ~= nil and spawner.spawnPlaces[1] ~= nil and spawner.spawnPlaces[1].startNode or nil +end + + local function distance2(ax, az, bx, bz) local dx, dz = ax - bx, az - bz return dx * dx + dz * dz @@ -63,7 +74,10 @@ end -- Palle eller bulk ved DENNE kilde: silo = bulk; objektlager = palle; fabrik = bulk hvis -- markørens læssestation udleverer varen, ellers palle hvis varen er en palletype. function ADSources.isPalletAt(source, fillType) - if source.kind == "silo" or source.kind == "husbandry" then + if source.kind == "husbandry" then + local spawners = getHusbandryPalletSpawners(source.placeable) + return spawners ~= nil and spawners[fillType] ~= nil + elseif source.kind == "silo" then return false elseif source.kind == "objectStorage" then return true @@ -76,11 +90,14 @@ function ADSources.isPalletAt(source, fillType) return ADSources.isPalletFillType(fillType) end --- Placeable'ns "nærmeste punkt" for kildegenkendelse: pallespawnerens første plads (fabrik) --- eller rodnoden. +-- Placeable'ns "nærmeste punkt" for kildegenkendelse: pallespawnerens første plads (fabrik, +-- stald med paller) eller rodnoden. local function getPlaceableAnchor(placeable) local productionPoint = getProductionPoint(placeable) local spawner = productionPoint ~= nil and productionPoint.palletSpawner or nil + if spawner == nil then + for _, husbandrySpawner in pairs(getHusbandryPalletSpawners(placeable) or {}) do spawner = husbandrySpawner break end + end local place = spawner ~= nil and spawner.spawnPlaces ~= nil and spawner.spawnPlaces[1] or nil local node = place ~= nil and place.startNode or placeable.rootNode local x, z = getNodePosition(node) @@ -158,6 +175,11 @@ function ADSources.identify(adEnv, wayPointId, farmId) end end end + -- stald med egne pallespawnere (hønsehus: æg) — markøren står ved spawneren, ingen læssestation + local palletBarn = findPlaceableNear(wayPoint, farmId, function(placeable) return placeable.getHusbandryFillLevel ~= nil and getHusbandryPalletSpawners(placeable) ~= nil end) + if palletBarn ~= nil then + return {kind = "husbandry", placeable = palletBarn, station = nil, wayPointId = wayPointId, name = getPlaceableName(palletBarn, markerName)} + end -- Rene pallefabrikker (halmvarer, bageri ...) har ingen læssestation: find dem på afstand -- til pallespawneren; dernæst objektlagre. local factory = findPlaceableNear(wayPoint, farmId, isProductionPoint) @@ -221,10 +243,10 @@ end -- Liter af varen på paller der står ved fabrikkens spawner (pallespawneren tømmer lageret -- ud på jorden løbende, så det er dér varen fysisk er). -local function getPalletsNearSpawner(placeable, fillType, farmId) +local function getPalletsNearSpawner(placeable, fillType, farmId, spawnerNode) local productionPoint = getProductionPoint(placeable) local spawner = productionPoint ~= nil and productionPoint.palletSpawner or nil - local node = spawner ~= nil and spawner.spawnPlaces ~= nil and spawner.spawnPlaces[1] ~= nil and spawner.spawnPlaces[1].startNode or placeable.rootNode + local node = spawnerNode or (spawner ~= nil and spawner.spawnPlaces ~= nil and spawner.spawnPlaces[1] ~= nil and spawner.spawnPlaces[1].startNode) or placeable.rootNode local sx, sz = getNodePosition(node) if sx == nil then return 0 @@ -369,8 +391,27 @@ local function getNearestTriggerFillTypes(adEnv, source) return #list > 0 and list or nil end +local function getHusbandryPalletProducts(source, farmId) + local products = {} + local spec = source.placeable.spec_husbandryPallets + for fillType, spawner in pairs(getHusbandryPalletSpawners(source.placeable) or {}) do + local liters, count = getPalletsNearSpawner(source.placeable, fillType, farmId, firstSpawnerNode(spawner)) + local capacity = spec ~= nil and spec.capacities ~= nil and spec.capacities[fillType] or 0 + local product = newProduct(fillType, liters, liters, math.max(capacity or 0, liters), true) + product.palletLiters, product.palletCount = liters, count + table.insert(products, product) + end + table.sort(products, function(a, b) return a.fillType < b.fillType end) + return products +end + local function getHusbandryProducts(adEnv, source) local placeable, station = source.placeable, source.station + local farmId = g_currentMission ~= nil and g_currentMission.getFarmId ~= nil and g_currentMission:getFarmId() or 1 + if station == nil then + -- kilde via staldens pallespawner (hønsehus: æg): kun pallevarerne + return getHusbandryPalletProducts(source, farmId) + end local fillTypes = getNearestTriggerFillTypes(adEnv, source) or {} if #fillTypes == 0 then for fillType, supported in pairs(station ~= nil and station.supportedFillTypes or {}) do diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index 5715e49..d0f9eb7 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.18.7.0 + 1.18.8.0 <en>AD Smart Pickup</en> diff --git a/docs/BRUGERMANUAL.md b/docs/BRUGERMANUAL.md index d87dbae..82272d1 100644 --- a/docs/BRUGERMANUAL.md +++ b/docs/BRUGERMANUAL.md @@ -1,6 +1,6 @@ # AD Smart Pickup — brugermanual -Version 1.18.7 · Farming Simulator 25 · tillæg til AutoDrive +Version 1.18.8 · Farming Simulator 25 · tillæg til AutoDrive *English version: [USER_MANUAL.md](USER_MANUAL.md)* @@ -285,7 +285,7 @@ Under **Fra** kan du vælge: |---|---| | **Fabrik** | Fabrikkens outputs, både bulk og paller fra pallespawneren | | **Silo** | Siloens indhold | -| **Stald** | Mælk, gylle og gødning fra staldens læssepunkter, inklusive tilknyttede udvidelsestanke som Milk Tank Extension | +| **Stald** | Mælk, gylle og gødning fra staldens læssepunkter, inklusive tilknyttede udvidelsestanke som Milk Tank Extension. Æg og uld hentes som paller ved staldens pallespawner, fx i hønsehuset. Sæt en markør ved spawneren | | **Objektlager** | Paller og baller. De tages ud én ad gangen | | **Lager** | Andre bygninger med en læssestation, fx Pumps & Hoses' separatorhal med digestat. Varerne læses fra læssestationen | diff --git a/docs/USER_MANUAL.md b/docs/USER_MANUAL.md index 0717ceb..3b0e859 100644 --- a/docs/USER_MANUAL.md +++ b/docs/USER_MANUAL.md @@ -1,6 +1,6 @@ # AD Smart Pickup — user manual -Version 1.18.7 · Farming Simulator 25 · add-on for AutoDrive +Version 1.18.8 · Farming Simulator 25 · add-on for AutoDrive *Dansk version: [BRUGERMANUAL.md](BRUGERMANUAL.md)* @@ -288,7 +288,7 @@ Under **From** you can pick: |---|---| | **Factory** | The factory's outputs, both bulk and pallets from the pallet spawner | | **Silo** | The silo's contents | -| **Barn** | Milk, slurry and manure from the barn's loading points, including attached extension tanks such as Milk Tank Extension | +| **Barn** | Milk, slurry and manure from the barn's loading points, including attached extension tanks such as Milk Tank Extension. Eggs and wool are collected as pallets at the barn's pallet spawner, such as the chicken coop's. Place a marker at the spawner | | **Object storage** | Pallets and bales. They are taken out one at a time | | **Storage** | Other buildings with a loading station, such as the Pumps & Hoses separator hall with digestate. The goods are read from the loading station | diff --git a/tests/test_adBuildings.lua b/tests/test_adBuildings.lua index b142fb8..26ba01d 100644 --- a/tests/test_adBuildings.lua +++ b/tests/test_adBuildings.lua @@ -186,5 +186,32 @@ do check("B-DROP silo beholder sin egen markør (ikke nabosiloens)", byC.silo1.unloadMarker.id, 6) 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_husbandryPallets = {fillTypeIndexToPalletSpawner = {[99] = {spawnPlaces = {{startNode = {x = 10030, z = 0}}}}}}}) + table.insert(unloadStations, {owningPlaceable = coop, unloadTriggers = {{exactFillRootNode = {x = 9970, z = 0}}}}) + addMarker(61, "Chicken Food", "Chicken", 9965, 0) + addMarker(62, "Egg Pickup", "Chicken", 10035, 0) + ADBuildings.invalidate() + local byCoop = {} + for _, b in ipairs(ADBuildings.list(adEnv, 1)) do byCoop[b.id] = b end + check("B-COOP læssemarkør ved æggene", byCoop.coop1.loadMarker and byCoop.coop1.loadMarker.id, 62) + check("B-COOP aflæsningsmarkør ved foderet", byCoop.coop1.unloadMarker and byCoop.coop1.unloadMarker.id, 61) +end + +-- B-FUEL: brændstoftank (læssestation kun med DIESEL) er ikke et lager/en kilde +do + local fmBefore = g_fillTypeManager + g_fillTypeManager = {getFillTypeNameByIndex = function(_, index) return index == 900 and "DIESEL" or "X" end} + local tank = placeable("fuel1", "Small Petrol Tank", 11000, 0, {}) + table.insert(loadStations, {owningPlaceable = tank, supportedFillTypes = {[900] = true}, loadTriggers = {{triggerNode = {x = 11000, z = 3}}}}) + ADBuildings.invalidate() + local byFuel = {} + for _, b in ipairs(ADBuildings.list(adEnv, 1)) do byFuel[b.id] = b end + check("B-FUEL brændstoftank udeladt", byFuel.fuel1, nil) + g_fillTypeManager = fmBefore +end + print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adSources.lua b/tests/test_adSources.lua index 03b17cd..83008e3 100644 --- a/tests/test_adSources.lua +++ b/tests/test_adSources.lua @@ -389,6 +389,27 @@ local sepProducts = sepSource and ADSources.getProducts(adEnv, sepSource, 1) or check("S14d digestat fra stationen", sepProducts[1] and sepProducts[1].fillType, DIGESTATE) check("S14d mængde", sepProducts[1] and sepProducts[1].level, 42000) +-- S14e: hønsehus — æg kommer som PALLER fra staldens egen pallespawner (spec_husbandryPallets), ingen +-- læssestation ved æggene: markøren ved spawneren er en staldkilde, æggene er pallevarer +local EGGS = 31 +DESC[EGGS] = {index = EGGS, name = "EGG", title = "Æg", isPallet = true} +local coopSpawner = {spawnPlaces = {{startNode = {x = 8000, z = 0}}}} +local coop = {rootNode = {x = 8040, z = 0}, ownerFarmId = 1, getOwnerFarmId = function() return 1 end, getName = function() return "Chicken Coop" end, + getHusbandryFillLevel = function() return 0 end, + spec_husbandryPallets = {fillTypeIndexToPalletSpawner = {[EGGS] = coopSpawner}, activeFillTypes = {EGGS}, fillLevels = {[EGGS] = 0}, capacities = {[EGGS] = 5000}}} +table.insert(g_currentMission.placeableSystem.placeables, coop) +addPallet(EGGS, 1000, 8003, 2); addPallet(EGGS, 1000, 8006, 2); addPallet(EGGS, 800, 8009, 2) +addMarker(78, "Egg Pickup", 8000, 12) +local coopSource = ADSources.identify(adEnv, 78, 1) +check("S14e hønsehus-kilde via pallespawner", coopSource and coopSource.kind, "husbandry") +local coopProducts = coopSource and ADSources.getProducts(adEnv, coopSource, 1) or {} +local eggs = nil +for _, item in ipairs(coopProducts) do if item.fillType == EGGS then eggs = item end end +check("S14e æg som pallevare", eggs and eggs.isPallet, true) +check("S14e æg på paller ved spawneren", eggs and eggs.available, 2800) +check("S14e tre paller", eggs and eggs.palletCount, 3) +check("S14e isPalletAt æg", coopSource and ADSources.isPalletAt(coopSource, EGGS), true) + -- S15: forklaring af en markør til advarslen (hvad opslaget ser) check("S15 forklaring stald", ADSources.explainMarker(adEnv, 10, 1), "læssestation ved markøren hører til 'Kostald' (stald)") check("S15 forklaring ingen station", ADSources.explainMarker(adEnv, 4, 1), "ingen læssestation ved markøren, og ingen fabrik/objektlager inden for 40 m")