1281 lines
72 KiB
Lua
1281 lines
72 KiB
Lua
-- Kør: luajit tests/test_adSmartPickup.lua (fra repo-roden)
|
|
-- Mock'er de få FS25-/AutoDrive-API'er moden rører og tester valg-logikken.
|
|
|
|
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,
|
|
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 ----
|
|
MathUtil = {vector2Length = function(x, z) return math.sqrt(x * x + z * z) end}
|
|
Logging = {
|
|
info = function(fmt, ...) table.insert(logLines, string.format(fmt, ...)) end,
|
|
warning = function(fmt, ...) table.insert(logLines, "WARN " .. string.format(fmt, ...)) end,
|
|
}
|
|
local nodePositions = {}
|
|
function entityExists(node) return nodePositions[node] ~= nil end
|
|
function getWorldTranslation(node) local p = nodePositions[node]; return p.x, 0, p.z end
|
|
local listener
|
|
function addModEventListener(l) listener = l end
|
|
|
|
local function makeStation(node, x, z, levels, isBroken, ownLevels)
|
|
nodePositions[node] = {x = x, z = z}
|
|
local placeable = ownLevels ~= nil and {spec_silo = {storages = {
|
|
{getFillLevel = function(_, fillType) return ownLevels[fillType] or 0 end},
|
|
}}} or nil
|
|
return {
|
|
owningPlaceable = placeable,
|
|
loadTriggers = {{triggerNode = node}},
|
|
unloadTriggers = {{exactFillRootNode = node}},
|
|
getFillLevel = function(_, fillType)
|
|
if isBroken then error("boom") end
|
|
return levels[fillType] or 0
|
|
end,
|
|
getIsFillTypeSupported = function(_, fillType) return levels[fillType] ~= nil or (ownLevels ~= nil and ownLevels[fillType] ~= nil) end,
|
|
getFreeCapacity = function(_, fillType) return (levels.capacity or 200000) - (levels[fillType] or 0) end,
|
|
}
|
|
end
|
|
|
|
-- Aflæsningsmål (fx TMR-blander): ét lager med niveau + kapacitet pr. vare.
|
|
local function makeUnloadStation(node, x, z, levels, capacities, placeable, isBroken)
|
|
nodePositions[node] = {x = x, z = z}
|
|
local storage = {
|
|
getFillLevel = function(_, fillType)
|
|
if isBroken then error("boom") end
|
|
return levels[fillType] or 0
|
|
end,
|
|
getFreeCapacity = function(_, fillType) return (capacities[fillType] or 0) - (levels[fillType] or 0) end,
|
|
}
|
|
return {
|
|
owningPlaceable = placeable,
|
|
unloadTriggers = {{exactFillRootNode = node}},
|
|
targetStorages = {[storage] = storage},
|
|
getIsFillTypeSupported = function(_, fillType) return capacities[fillType] ~= nil end,
|
|
}
|
|
end
|
|
|
|
local stations, unloadStations = {}, {}
|
|
g_currentMission = {
|
|
storageSystem = {
|
|
getLoadingStations = function() return stations end,
|
|
getUnloadingStations = function() return unloadStations end,
|
|
},
|
|
accessHandler = {canFarmAccess = function() return true end},
|
|
getFarmId = function() return 1 end,
|
|
}
|
|
|
|
-- ---- AutoDrive-mocks ----
|
|
local markers, wayPoints = {}, {}
|
|
-- fromX/fromZ: forrige waypoint på ruten ind til markøren (giver ankomstretningen)
|
|
local function addMarker(index, name, group, x, z, fromX, fromZ)
|
|
wayPoints[index] = {x = x, z = z, incoming = {}}
|
|
if fromX ~= nil then
|
|
wayPoints[1000 + index] = {x = fromX, z = fromZ, incoming = {}}
|
|
wayPoints[index].incoming = {1000 + index}
|
|
end
|
|
table.insert(markers, {id = index, markerIndex = index, name = name, group = group})
|
|
end
|
|
|
|
local function resetWorld()
|
|
if ADSmartPickup ~= nil and ADSmartPickup.resetEnRoute ~= nil then ADSmartPickup.resetEnRoute() end
|
|
stations, unloadStations, markers, wayPoints, nodePositions, logLines = {}, {}, {}, {}, {}, {}
|
|
FS25_AutoDrive.ADMultipleTargetsManager.pickups = {}
|
|
end
|
|
|
|
local RT_NONE, RT_ONLYPICKUP = 1, 2
|
|
local testVehicles = {}
|
|
FS25_AutoDrive = {
|
|
AutoDrive = {
|
|
RT_NONE = RT_NONE,
|
|
getSetting = function(settingName) return settingName == "rotateTargets" and RT_ONLYPICKUP or "andet" end,
|
|
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)
|
|
self.seenRotate = FS25_AutoDrive.AutoDrive.getSetting("rotateTargets", self.vehicle)
|
|
self.seenOther = FS25_AutoDrive.AutoDrive.getSetting("useFolders", self.vehicle)
|
|
if self.shouldFail then error("task boom") end
|
|
end},
|
|
ADMultipleTargetsManager = {pickups = {}, getNextPickup = function() return "ORIGINAL" end},
|
|
-- aflæsningsopgaven: original update tæller bare kald; finished markerer
|
|
UnloadAtDestinationTask = {
|
|
STATE_PATHPLANNING = 1, STATE_DRIVING = 2,
|
|
update = function(self) self.updates = (self.updates or 0) + 1 end,
|
|
finished = function(self) self.isFinished = true end,
|
|
},
|
|
ADGraphManager = {
|
|
getMapMarkersInGroup = function(_, group)
|
|
local result = {}
|
|
for _, m in ipairs(markers) do if m.group == group then table.insert(result, m) end end
|
|
return result
|
|
end,
|
|
getWayPointById = function(_, id) return wayPoints[id] end,
|
|
getMapMarkers = function() return markers end,
|
|
getMapMarkerByWayPointId = function(_, id) for _, m in ipairs(markers) do if m.id == id then return m end end end,
|
|
-- ruter: alt nåeligt medmindre testen sætter unreachable[id] (til eller fra)
|
|
unreachable = {},
|
|
getPathTo = function(self, vehicle, id) if self.unreachable[id] then return {} end; return {{id = id}} end,
|
|
pathFromTo = function(self, fromId, toId) if self.unreachable[fromId] or self.unreachable[toId] then return {} end; return {{id = fromId}, {id = toId}} end,
|
|
},
|
|
}
|
|
|
|
local function makeDriver(firstMarker, fillTypes)
|
|
return {
|
|
getName = function() return "Testtraktor" end,
|
|
getOwnerFarmId = function() return 1 end,
|
|
ad = {stateModule = {
|
|
getFirstMarker = function() return firstMarker end,
|
|
getSecondMarker = function() return nil end,
|
|
getSelectedFillTypes = function() return fillTypes end,
|
|
getFillType = function() return fillTypes[1] end,
|
|
}},
|
|
}
|
|
end
|
|
|
|
-- Køretøjet er "fremme ved markøren" medmindre testen siger andet.
|
|
local function withDrivePath(driver)
|
|
driver.targetReached = true
|
|
driver.ad.drivePathModule = {isTargetReached = function() return driver.targetReached end}
|
|
return driver
|
|
end
|
|
|
|
-- Muterbar stateModule-mock: behovsstyringen skifter vare og indsnævrer AD's valg.
|
|
local function makeSupplyDriver(firstMarker, secondMarker, fillTypes)
|
|
local stateModule = {selectedFillTypes = fillTypes, fillType = fillTypes[1], active = true, dirtyCount = 0}
|
|
stateModule.getFirstMarker = function() return firstMarker end
|
|
stateModule.getSecondMarker = function() return secondMarker end
|
|
stateModule.getSelectedFillTypes = function(self) return self.selectedFillTypes end
|
|
stateModule.getFillType = function(self) return self.fillType end
|
|
stateModule.setFillType = function(self, fillType) self.fillType = fillType end
|
|
stateModule.raiseDirtyFlag = function(self) self.dirtyCount = self.dirtyCount + 1 end
|
|
stateModule.isActive = function(self) return self.active end
|
|
return {
|
|
getName = function() return "Fodertraktor" end,
|
|
getOwnerFarmId = function() return 1 end,
|
|
ad = {stateModule = stateModule},
|
|
}
|
|
end
|
|
|
|
dofile("FS25_ADSmartPickup/adSupplyPlanner.lua")
|
|
dofile("FS25_ADSmartPickup/adRuns.lua")
|
|
dofile("FS25_ADSmartPickup/adRunsController.lua")
|
|
dofile("FS25_ADSmartPickup/adLoadSwap.lua")
|
|
dofile("FS25_ADSmartPickup/adWaitPool.lua")
|
|
dofile("FS25_ADSmartPickup/adUnloadWait.lua")
|
|
dofile("FS25_ADSmartPickup/adSmartPickup.lua")
|
|
listener:update(16) -- installerer hook'en
|
|
local manager = FS25_AutoDrive.ADMultipleTargetsManager
|
|
|
|
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
|
|
|
|
-- 1: vælger mindst fyldte silo med varen; springer tomme og forkert vare over
|
|
resetWorld()
|
|
addMarker(1, "Silo 1", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[WHEAT] = 900000}))
|
|
addMarker(2, "Silo 2", "Siloer", 500, 0); table.insert(stations, makeStation(102, 500, 15, {[SUGARBEET] = 800000}))
|
|
addMarker(3, "Silo 3", "Siloer", 1000, 0); table.insert(stations, makeStation(103, 1000, 15, {[SUGARBEET] = 12000}))
|
|
addMarker(4, "Silo 4", "Siloer", 1500, 0); table.insert(stations, makeStation(104, 1500, 15, {[SUGARBEET] = 0}))
|
|
addMarker(5, "Fabrik", "Andet", 1000, 5); -- anden mappe, skal ignoreres
|
|
check("mindst fyldte med varen vælges", manager:getNextPickup(makeDriver(markers[1], {SUGARBEET}), false), 3)
|
|
check("AD-rotationstilstand opdateret", manager.pickups["Siloer"].lastTarget, 3)
|
|
|
|
-- 2: ingen silo har varen -> AutoDrives originale rotation
|
|
check("fallback når varen ikke findes", manager:getNextPickup(makeDriver(markers[1], {99}), false), "ORIGINAL")
|
|
|
|
-- 3: markør uden silo i nærheden ignoreres
|
|
resetWorld()
|
|
addMarker(1, "Langt væk", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 200, {[SUGARBEET] = 5}))
|
|
addMarker(2, "Silo 2", "Siloer", 500, 0); table.insert(stations, makeStation(102, 500, 15, {[SUGARBEET] = 700}))
|
|
check("markør uden nær silo ignoreres", manager:getNextPickup(makeDriver(markers[1], {SUGARBEET}), false), 2)
|
|
|
|
-- 4: flere valgte varer lægges sammen
|
|
resetWorld()
|
|
addMarker(1, "Silo 1", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[SUGARBEET] = 100, [WHEAT] = 5000}))
|
|
addMarker(2, "Silo 2", "Siloer", 500, 0); table.insert(stations, makeStation(102, 500, 15, {[SUGARBEET] = 3000}))
|
|
check("flere varetyper summeres", manager:getNextPickup(makeDriver(markers[1], {SUGARBEET, WHEAT}), false), 2)
|
|
|
|
-- 4b: klynge med delt lager — eget indhold slår nabo-sum, også når nabo-summen er mindre
|
|
resetWorld()
|
|
addMarker(1, "Nabo", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[SUGARBEET] = 800}, false, {}))
|
|
addMarker(2, "Ejer stor", "Siloer", 14, 0); table.insert(stations, makeStation(102, 14, 15, {[SUGARBEET] = 900000}, false, {[SUGARBEET] = 500000}))
|
|
addMarker(3, "Ejer lille", "Siloer", 28, 0);table.insert(stations, makeStation(103, 28, 15, {[SUGARBEET] = 900000}, false, {[SUGARBEET] = 20000}))
|
|
check("eget lager foran nabo-lån, mindst først", manager:getNextPickup(makeDriver(markers[1], {SUGARBEET}), false), 3)
|
|
|
|
-- 4c: kun nabo-lån muligt -> bruges stadig
|
|
resetWorld()
|
|
addMarker(1, "Nabo", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[SUGARBEET] = 800}, false, {}))
|
|
check("nabo-lån bruges når ingen ejer varen", manager:getNextPickup(makeDriver(markers[1], {SUGARBEET}), false), 1)
|
|
|
|
-- 5: fejl i spil-API -> fallback + advarsel, aldrig crash
|
|
resetWorld()
|
|
addMarker(1, "Silo 1", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, {}, true))
|
|
check("fejl giver fallback", manager:getNextPickup(makeDriver(markers[1], {SUGARBEET}), false), "ORIGINAL")
|
|
check("fejl logges som advarsel", logLines[#logLines]:sub(1, 4), "WARN")
|
|
|
|
-- 6: gruppen "All" og manglende første markør røres ikke
|
|
check("gruppe All -> fallback", manager:getNextPickup(makeDriver({group = "All", markerIndex = 1}, {SUGARBEET}), false), "ORIGINAL")
|
|
check("ingen første markør -> fallback", manager:getNextPickup(makeDriver(nil, {SUGARBEET}), false), "ORIGINAL")
|
|
|
|
-- ==== Behovsstyret forsyning (flere varer valgt + aflæsningsmål med lager) ====
|
|
local BLANDER = {name = "TMR-blander placeable"}
|
|
local FULL = {[HAY] = 100000, [GRASS] = 100000, [SILAGE] = 100000}
|
|
local blanderLevels
|
|
|
|
local function buildFeedWorld()
|
|
resetWorld()
|
|
blanderLevels = {[HAY] = 80000, [GRASS] = 20000, [SILAGE] = 50000}
|
|
addMarker(1, "Hølade", "Lader", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[HAY] = 50000}))
|
|
addMarker(2, "Græssilo stor", "Siloer", 500, 0); table.insert(stations, makeStation(102, 500, 15, {[GRASS] = 300000}))
|
|
addMarker(3, "Græssilo lille", "Andet", 1000, 0);table.insert(stations, makeStation(103, 1000, 15, {[GRASS] = 40000}))
|
|
addMarker(4, "Ensilagesilo", "Siloer", 1500, 0); table.insert(stations, makeStation(104, 1500, 15, {[SILAGE] = 200000}))
|
|
addMarker(10, "TMR-blander", "Produktion", 5000, 0)
|
|
table.insert(unloadStations, makeUnloadStation(110, 5000, 10, blanderLevels, FULL, BLANDER))
|
|
end
|
|
local function markerByIndex(index)
|
|
for _, m in ipairs(markers) do if m.markerIndex == index then return m end end
|
|
end
|
|
|
|
-- S1: mest trængende ingrediens vælges, kilde findes på tværs af mapper, AD's vare skiftes
|
|
buildFeedWorld()
|
|
local feeder = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS, SILAGE})
|
|
check("S1 mest trængende vare, mindst fyldte kilde", manager:getNextPickup(feeder, false), 3)
|
|
check("S1 AD's vare skiftet til græs", feeder.ad.stateModule.fillType, GRASS)
|
|
check("S1 AD's valg indsnævret til én vare", #feeder.ad.stateModule.selectedFillTypes, 1)
|
|
check("S1 ændring synkroniseres (dirty-flag)", feeder.ad.stateModule.dirtyCount > 0, true)
|
|
check("S1 rotationstilstand følger valgt markørs mappe", manager.pickups["Andet"].lastTarget, 3)
|
|
|
|
-- S2: næste tur — gemt liste genbruges; græs er nu fuldt og springes over -> ensilage
|
|
blanderLevels[GRASS] = 99500
|
|
check("S2 fuld ingrediens springes over", manager:getNextPickup(feeder, false), 4)
|
|
check("S2 vare skiftet til ensilage", feeder.ad.stateModule.fillType, SILAGE)
|
|
|
|
-- S3: ingrediens uden kilde springes over -> næste i behovs-rækkefølgen
|
|
blanderLevels[SILAGE] = 10000
|
|
stations[4] = makeStation(104, 1500, 15, {[SILAGE] = 0})
|
|
check("S3 ingen kilde -> næste ingrediens", manager:getNextPickup(feeder, false), 1)
|
|
check("S3 vare skiftet til hø", feeder.ad.stateModule.fillType, HAY)
|
|
|
|
-- S4: AD stopper -> hele ingredienslisten gendannes i AD's varevalg
|
|
feeder.ad.stateModule.active = false
|
|
listener:update(16)
|
|
check("S4 valg gendannet ved stop", #feeder.ad.stateModule.selectedFillTypes, 3)
|
|
|
|
-- S5: aflæsningsmålets egen læssestation bruges ikke som kilde
|
|
buildFeedWorld()
|
|
addMarker(11, "Blander udtag", "Produktion", 5030, 0)
|
|
table.insert(stations, makeStation(111, 5030, 15, {[GRASS] = 10}, false, nil))
|
|
stations[#stations].owningPlaceable = BLANDER
|
|
feeder = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS})
|
|
check("S5 målets egen station udelukkes som kilde", manager:getNextPickup(feeder, false), 3)
|
|
|
|
-- S6: brugeren vælger selv en anden enkelt vare -> behovsstyring fra (mappe-logik)
|
|
feeder.ad.stateModule.selectedFillTypes = {HAY}
|
|
feeder.ad.stateModule.fillType = HAY
|
|
check("S6 brugerens enkelt-valg -> mappe-logik", manager:getNextPickup(feeder, false), 1)
|
|
check("S6 vare røres ikke", feeder.ad.stateModule.fillType, HAY)
|
|
|
|
-- S7: intet behov (alt fuldt) -> mappe-logik, valg indsnævres ikke
|
|
buildFeedWorld()
|
|
blanderLevels[HAY], blanderLevels[GRASS], blanderLevels[SILAGE] = 99900, 99900, 99900
|
|
feeder = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS, SILAGE})
|
|
check("S7 intet behov -> mappe-logik", manager:getNextPickup(feeder, false), 1)
|
|
check("S7 valg ikke indsnævret", #feeder.ad.stateModule.selectedFillTypes, 3)
|
|
|
|
-- S8: ingen aflæsningsstation ved målet -> mappe-logik
|
|
buildFeedWorld()
|
|
unloadStations = {}
|
|
feeder = makeSupplyDriver(markerByIndex(2), markerByIndex(10), {GRASS, SILAGE})
|
|
check("S8 ingen station ved mål -> mappe-logik", manager:getNextPickup(feeder, false), 4)
|
|
|
|
-- S9: fejl i målets lager-API -> AutoDrives original + advarsel
|
|
buildFeedWorld()
|
|
unloadStations = {makeUnloadStation(110, 5000, 10, blanderLevels, FULL, BLANDER, true)}
|
|
feeder = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS})
|
|
check("S9 fejl giver fallback", manager:getNextPickup(feeder, false), "ORIGINAL")
|
|
check("S9 fejl logges som advarsel", logLines[#logLines]:sub(1, 4), "WARN")
|
|
|
|
-- S10: stationen eksponerer ikke targetStorages -> lageret læses fra produktionspunktet
|
|
buildFeedWorld()
|
|
local bareStation = unloadStations[1]
|
|
local productionStorage = next(bareStation.targetStorages)
|
|
bareStation.targetStorages = nil
|
|
bareStation.owningPlaceable = {spec_productionPoint = {productionPoint = {storage = productionStorage}}}
|
|
feeder = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS, SILAGE})
|
|
check("S10 lager læses fra produktionspunktet", manager:getNextPickup(feeder, false), 3)
|
|
|
|
-- ==== Fabriksstyret forsyning (aflæsningsmålet er et produktionspunkt) ====
|
|
ProductionPoint = {PROD_STATUS = {INACTIVE = 0, RUNNING = 1, MISSING_INPUTS = 2, NO_OUTPUT_SPACE = 3}}
|
|
local WATER = 40
|
|
FILL_TYPE_NAMES[WATER] = "WATER"
|
|
|
|
-- Fabrik med TMR-opskrift: sluger ensilage, nipper til hø. Vand kan vognen ikke bære.
|
|
local function buildFactoryWorld(tmrEnabled, tmrStatus)
|
|
buildFeedWorld()
|
|
local pp = {
|
|
enabled = {tmr = tmrEnabled},
|
|
productions = {
|
|
{id = "tmr", cyclesPerMinute = 1, status = tmrStatus, inputs = {{type = SILAGE, amount = 250}, {type = HAY, amount = 10}, {type = WATER, amount = 1000}}},
|
|
{id = "off", cyclesPerMinute = 1, status = ProductionPoint.PROD_STATUS.INACTIVE, inputs = {{type = GRASS, amount = 500}}},
|
|
},
|
|
getIsProductionEnabled = function(self, id) return self.enabled[id] == true end,
|
|
}
|
|
local factory = {spec_productionPoint = {productionPoint = pp}, getName = function() return "AIO" end}
|
|
unloadStations = {makeUnloadStation(110, 5000, 10, blanderLevels, {[HAY] = 100000, [GRASS] = 100000, [SILAGE] = 100000, [WATER] = 100000}, factory)}
|
|
local driver = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS})
|
|
driver.carriable = {HAY, GRASS, SILAGE}
|
|
return driver, pp
|
|
end
|
|
|
|
-- F1: tændt produktion styrer listen: ensilage (200 min til tom) vinder over hø (8000 min)
|
|
-- og over græs, som er relativt tommest men ikke et input i fabrikken
|
|
local factoryDriver = buildFactoryWorld(true, ProductionPoint.PROD_STATUS.RUNNING)
|
|
check("F1 fabrikkens input med kortest tid til tom", manager:getNextPickup(factoryDriver, false), 4)
|
|
check("F1 AD's vare skiftet til ensilage (ikke i brugerens valg)", factoryDriver.ad.stateModule.fillType, SILAGE)
|
|
check("F1 log nævner minutter til tom", logLines[#logLines]:find("min til tom", 1, true) ~= nil, true)
|
|
|
|
-- F2: næste tur — listen genudledes fra fabrikken, ikke fra brugerens valg
|
|
blanderLevels[SILAGE] = 99500
|
|
check("F2 fuld ensilage -> hø (næste fabriks-input)", manager:getNextPickup(factoryDriver, false), 1)
|
|
|
|
-- F3: AD stopper -> brugerens OPRINDELIGE valg gendannes, ikke fabrikkens liste
|
|
factoryDriver.ad.stateModule.active = false
|
|
listener:update(16)
|
|
check("F3 brugerens valg gendannet ved stop", #factoryDriver.ad.stateModule.selectedFillTypes, 2)
|
|
|
|
-- F4: manuelt slukket produktion -> fallback til brugerens valg (græs er tommest)
|
|
factoryDriver = buildFactoryWorld(false, ProductionPoint.PROD_STATUS.INACTIVE)
|
|
local function countLogsSince(startIndex, needle)
|
|
local count = 0
|
|
for i = startIndex + 1, #logLines do
|
|
if logLines[i]:find(needle, 1, true) then count = count + 1 end
|
|
end
|
|
return count
|
|
end
|
|
local logMark = #logLines
|
|
check("F4 slukket fabrik -> brugerens valg", manager:getNextPickup(factoryDriver, false), 3)
|
|
check("F4 loggen forklarer fallback", countLogsSince(logMark, "ingen ønskede produktioner"), 1)
|
|
manager:getNextPickup(factoryDriver, false)
|
|
check("F4 forklaringen logges kun én gang", countLogsSince(logMark, "ingen ønskede produktioner"), 1)
|
|
|
|
-- F5: auto-slukket af kortet pga. manglende input -> stadig fabrikkens liste
|
|
factoryDriver = buildFactoryWorld(false, ProductionPoint.PROD_STATUS.MISSING_INPUTS)
|
|
check("F5 auto-slukket fabrik fodres stadig", manager:getNextPickup(factoryDriver, false), 4)
|
|
|
|
-- F6: produktionspunkt uden getIsProductionEnabled -> activeProductions-listen bruges
|
|
local fallbackDriver, fallbackPp = buildFactoryWorld(false, ProductionPoint.PROD_STATUS.RUNNING)
|
|
fallbackPp.getIsProductionEnabled = nil
|
|
fallbackPp.activeProductions = {fallbackPp.productions[1]}
|
|
check("F6 activeProductions som fallback", manager:getNextPickup(fallbackDriver, false), 4)
|
|
|
|
-- ==== En fabriks inputs stjæles aldrig (halm+TMR til kostald; TMR-fabrikken har halm som input) ====
|
|
local STRAW, TMR = 41, 42
|
|
FILL_TYPE_NAMES[STRAW], FILL_TYPE_NAMES[TMR] = "STRAW", "FORAGE"
|
|
local cowLevels
|
|
local function buildCowWorld()
|
|
resetWorld()
|
|
local tmrFactory = {
|
|
getName = function() return "TMR-fabrik" end,
|
|
spec_productionPoint = {productionPoint = {productions = {
|
|
{id = "tmr", cyclesPerMinute = 1, inputs = {{type = STRAW, amount = 10}, {type = SILAGE, amount = 100}}, outputs = {{type = TMR, amount = 50}}},
|
|
}}},
|
|
}
|
|
-- fabrikkens læssestation har BÅDE halm (input, mindst fyldt) og TMR (output)
|
|
addMarker(20, "TMR Load", "Fabrication", 0, 0)
|
|
table.insert(stations, makeStation(120, 0, 15, {[STRAW] = 5000, [TMR] = 30000}))
|
|
stations[#stations].owningPlaceable = tmrFactory
|
|
addMarker(21, "Halmlade", "Lader", 500, 0)
|
|
table.insert(stations, makeStation(121, 500, 15, {[STRAW] = 50000}))
|
|
addMarker(30, "Cow 1 Food", "Cow Food", 5000, 0)
|
|
cowLevels = {[STRAW] = 0, [TMR] = 40000}
|
|
table.insert(unloadStations, makeUnloadStation(130, 5000, 10, cowLevels, {[STRAW] = 100000, [TMR] = 100000}, {name = "kostald"}))
|
|
local driver = makeSupplyDriver(markerByIndex(21), markerByIndex(30), {TMR, STRAW})
|
|
driver.carriable = {TMR, STRAW}
|
|
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)
|
|
|
|
-- P2: halm fuld -> TMR hentes gerne fra fabrikken (det er dens output)
|
|
cowLevels[STRAW] = 99500
|
|
check("P2 fabrikkens output må hentes", manager:getNextPickup(cowDriver, false), 20)
|
|
|
|
-- P3: mappe-logik (én vare) beskytter også fabrikkens input
|
|
cowDriver = buildCowWorld()
|
|
addMarker(22, "Halmlade 2", "Fabrication", 1000, 0)
|
|
table.insert(stations, makeStation(122, 1000, 15, {[STRAW] = 60000}))
|
|
local strawDriver = makeDriver(markerByIndex(20), {STRAW})
|
|
check("P3 mappe-logik springer fabrikkens input over", manager:getNextPickup(strawDriver, false), 22)
|
|
|
|
-- P4: stald som mål — niveau/kapacitet læses fra stalden selv (getHusbandry*), ikke fra
|
|
-- stationens lagre; stationen selv "understøtter" kun foder, men stalden tager også halm
|
|
cowDriver = buildCowWorld()
|
|
local barnLevels = {[STRAW] = 45000, [TMR] = 10000}
|
|
local barnCapacity = {[STRAW] = 50000, [TMR] = 100000}
|
|
local barn = {
|
|
getName = function() return "Kostald" end,
|
|
getHusbandryFillLevel = function(_, fillType) return barnLevels[fillType] or 0 end,
|
|
getHusbandryCapacity = function(_, fillType) return barnCapacity[fillType] or 0 end,
|
|
getHusbandryFreeCapacity = function(_, fillType) return (barnCapacity[fillType] or 0) - (barnLevels[fillType] or 0) end,
|
|
getHusbandryIsFillTypeSupported = function(_, fillType) return barnCapacity[fillType] ~= nil end,
|
|
}
|
|
local barnStation = unloadStations[1]
|
|
barnStation.targetStorages = nil
|
|
barnStation.owningPlaceable = barn
|
|
barnStation.getIsFillTypeSupported = function(_, fillType) return fillType == TMR end
|
|
barnStation.getFreeCapacity = function(_, fillType) return 999999 end
|
|
check("P4 stald: TMR (90 % tom) hentes før halm (10 % tom)", manager:getNextPickup(cowDriver, false), 20)
|
|
barnLevels[TMR] = 95000
|
|
check("P4 stald: halm hentes når TMR er fuld", manager:getNextPickup(cowDriver, false), 21)
|
|
|
|
-- ==== Wait-punkter: rest-læs + fuld stald -> vent ved nærmeste ledige Wait, tilbage når resten passer ====
|
|
local UnloadTask = FS25_AutoDrive.UnloadAtDestinationTask
|
|
local waitBarnLevels, waitBarnCapacity = {}, {[STRAW] = 50000, [TMR] = 100000}
|
|
local waitVehicles = {}
|
|
local function buildWaitWorld()
|
|
-- tidligere testkøretøjer slipper deres Wait-punkter (AD "stoppes" på dem)
|
|
for _, vehicle in ipairs(waitVehicles) do vehicle.ad.stateModule.active = false end
|
|
listener:update(16)
|
|
waitVehicles = {}
|
|
buildCowWorld()
|
|
waitBarnLevels = {[STRAW] = 50000, [TMR] = 10000} -- halm FULD
|
|
local barn2 = {
|
|
getName = function() return "Kostald" end,
|
|
getHusbandryFillLevel = function(_, fillType) return waitBarnLevels[fillType] or 0 end,
|
|
getHusbandryCapacity = function(_, fillType) return waitBarnCapacity[fillType] or 0 end,
|
|
getHusbandryFreeCapacity = function(_, fillType) return (waitBarnCapacity[fillType] or 0) - (waitBarnLevels[fillType] or 0) end,
|
|
getHusbandryIsFillTypeSupported = function(_, fillType) return waitBarnCapacity[fillType] ~= nil end,
|
|
}
|
|
unloadStations[1].targetStorages = nil
|
|
unloadStations[1].owningPlaceable = barn2
|
|
addMarker(40, "Wait 1", "Cow Wait", 5100, 0)
|
|
addMarker(41, "Wait 2", "Cow Wait", 5300, 0)
|
|
end
|
|
-- traktor der holder ved stalden (markør 30) med rest-læs halm på vognen
|
|
local function makeUnloadingVehicle(name, node, x, restStraw)
|
|
local vehicle = makeSupplyDriver(markerByIndex(21), markerByIndex(30), {TMR, STRAW})
|
|
vehicle.ad.stateModule.secondMarkerId = 30
|
|
vehicle.ad.stateModule.setSecondMarkerByWayPointId = function(self, id) self.secondMarkerId = id end
|
|
vehicle.ad.stateModule.getSecondMarker = function(self) return markerByIndex(self.secondMarkerId) end
|
|
vehicle.getName = function() return name end
|
|
vehicle.rootNode = node
|
|
nodePositions[node] = {x = x, z = 10}
|
|
vehicle.units = {{object = {
|
|
getFillUnitFillLevel = function() return restStraw end,
|
|
getFillUnitFillType = function() return STRAW end,
|
|
}, fillUnitIndex = 1}}
|
|
vehicle.trailers = {level = restStraw, capacity = 40000}
|
|
vehicle.targetReached = true
|
|
vehicle.ad.drivePathModule = {
|
|
isTargetReached = function() return vehicle.targetReached end,
|
|
setPathTo = function(self, id) self.pathTo = id end,
|
|
}
|
|
vehicle.ad.trailerModule = {isActiveAtTrigger = function() return false end}
|
|
local task = {vehicle = vehicle, destinationID = 30, state = UnloadTask.STATE_DRIVING, trailers = vehicle.trailers}
|
|
table.insert(waitVehicles, vehicle)
|
|
return vehicle, task
|
|
end
|
|
|
|
-- W1: fuld stald + rest-læs -> omdirigeres til nærmeste ledige Wait; AD's original kaldes stadig
|
|
buildWaitWorld()
|
|
local v1, t1 = makeUnloadingVehicle("Fastrac A", 501, 5000, 35000)
|
|
local waitLogMark = #logLines
|
|
UnloadTask.update(t1, 16)
|
|
check("W1 omdirigeret til nærmeste Wait", t1.destinationID, 40)
|
|
check("W1 rute lagt til Wait", v1.ad.drivePathModule.pathTo, 40)
|
|
check("W1 original update kaldt", t1.updates, 1)
|
|
check("W1 log fortæller om ventning", countLogsSince(waitLogMark, "Wait 1"), 1)
|
|
|
|
-- W2: anden traktor i samme situation -> Wait 1 er optaget -> Wait 2
|
|
local v2, t2 = makeUnloadingVehicle("Fastrac B", 502, 5010, 20000)
|
|
UnloadTask.update(t2, 16)
|
|
check("W2 optaget Wait springes over", t2.destinationID, 41)
|
|
|
|
-- W3: mens A venter, skjules rotationen (ellers kører AD videre fra Wait-punktet ved "rotér aflæsning")
|
|
check("W3 rotation skjult for ventende", FS25_AutoDrive.AutoDrive.getSetting("rotateTargets", v1), RT_NONE)
|
|
check("W3 andre indstillinger urørt", FS25_AutoDrive.AutoDrive.getSetting("useFolders", v1), "andet")
|
|
|
|
-- W4: stalden får plads til HELE resten -> A sendes hjem, Wait 1 frigives, rotation tilbage
|
|
waitBarnLevels[STRAW] = 10000 -- 40000 fri >= 35000
|
|
v1.targetReached = true -- står ved Wait 1
|
|
UnloadTask.update(t1, 16)
|
|
check("W4 sendt tilbage til stalden", t1.destinationID, 30)
|
|
check("W4 rute lagt hjem", v1.ad.drivePathModule.pathTo, 30)
|
|
check("W4 rotation gendannet", FS25_AutoDrive.AutoDrive.getSetting("rotateTargets", v1), RT_ONLYPICKUP)
|
|
local v3, t3 = makeUnloadingVehicle("Fastrac C", 503, 5020, 30000)
|
|
waitBarnLevels[STRAW] = 50000
|
|
UnloadTask.update(t3, 16)
|
|
check("W4 Wait 1 er ledig igen", t3.destinationID, 40)
|
|
|
|
-- W5: plads til noget, men ikke hele resten -> B bliver ved Wait (kun hele læs kommer af)
|
|
waitBarnLevels[STRAW] = 45000 -- 5000 fri < 20000
|
|
UnloadTask.update(t2, 16)
|
|
check("W5 delvis plads -> venter stadig", t2.destinationID, 41)
|
|
|
|
-- W6: stalden har plads ved ankomst -> ingen omdirigering (AD læsser af som normalt)
|
|
buildWaitWorld()
|
|
waitBarnLevels[STRAW] = 10000
|
|
local v6, t6 = makeUnloadingVehicle("Fastrac D", 506, 5000, 35000)
|
|
UnloadTask.update(t6, 16)
|
|
check("W6 plads ved ankomst -> uændret mål", t6.destinationID, 30)
|
|
|
|
-- W7: ingen Wait-punkter -> uændret (AD venter som i dag) + én advarsel
|
|
buildCowWorld()
|
|
unloadStations[1].targetStorages = nil
|
|
unloadStations[1].owningPlaceable = { getHusbandryFillLevel = function() return 50000 end, getHusbandryCapacity = function() return 50000 end,
|
|
getHusbandryFreeCapacity = function() return 0 end, getHusbandryIsFillTypeSupported = function() return true end }
|
|
local v7, t7 = makeUnloadingVehicle("Fastrac E", 507, 5000, 35000)
|
|
local warnMark = #logLines
|
|
UnloadTask.update(t7, 16); UnloadTask.update(t7, 16)
|
|
check("W7 uden Wait-punkter -> uændret mål", t7.destinationID, 30)
|
|
check("W7 én advarsel", countLogsSince(warnMark, "WARN"), 1)
|
|
|
|
-- W8: to andre holder begge punkter; stoppes de i AD, frigives punkterne
|
|
buildWaitWorld()
|
|
local vA, tA = makeUnloadingVehicle("Fastrac A2", 521, 5000, 35000)
|
|
local vB, tB = makeUnloadingVehicle("Fastrac B2", 522, 5000, 35000)
|
|
UnloadTask.update(tA, 16); UnloadTask.update(tB, 16)
|
|
local v8, t8 = makeUnloadingVehicle("Fastrac F", 508, 5000, 35000)
|
|
UnloadTask.update(t8, 16)
|
|
check("W8 alle Wait-punkter optaget -> uændret", t8.destinationID, 30)
|
|
vA.ad.stateModule.active, vB.ad.stateModule.active = false, false
|
|
listener:update(16)
|
|
UnloadTask.update(t8, 16)
|
|
check("W8 venter ved Wait 1 efter frigivelse", t8.destinationID, 40)
|
|
UnloadTask.finished(t8)
|
|
check("W8 original finished kaldt", t8.isFinished, true)
|
|
local v9, t9 = makeUnloadingVehicle("Fastrac G", 509, 5000, 35000)
|
|
UnloadTask.update(t9, 16)
|
|
check("W8 Wait 1 frigivet efter finished", t9.destinationID, 40)
|
|
check("W8 rotation gendannet efter finished", FS25_AutoDrive.AutoDrive.getSetting("rotateTargets", v8), RT_ONLYPICKUP)
|
|
|
|
-- W9: intet vejnet til nærmeste Wait -> næste; intet net til nogen -> uændret (AD slukker ellers sig selv)
|
|
buildWaitWorld()
|
|
FS25_AutoDrive.ADGraphManager.unreachable = {[40] = true}
|
|
local v10, t10 = makeUnloadingVehicle("Fastrac H", 510, 5000, 35000)
|
|
UnloadTask.update(t10, 16)
|
|
check("W9 unåeligt Wait 1 -> Wait 2", t10.destinationID, 41)
|
|
FS25_AutoDrive.ADGraphManager.unreachable = {[40] = true, [41] = true}
|
|
local v11, t11 = makeUnloadingVehicle("Fastrac I", 511, 5000, 35000)
|
|
local unreachableMark = #logLines
|
|
UnloadTask.update(t11, 16)
|
|
check("W9 intet nåeligt -> uændret mål", t11.destinationID, 30)
|
|
check("W9 advarsel om manglende vejnet", countLogsSince(unreachableMark, "WARN"), 1)
|
|
FS25_AutoDrive.ADGraphManager.unreachable = {}
|
|
|
|
-- W10: kæmpe vogn (700.000 l rest) i 50.000 l-stald -> hjem når halvdelen af stalden er fri
|
|
buildWaitWorld()
|
|
local v12, t12 = makeUnloadingVehicle("Fastrac J", 512, 5000, 700000)
|
|
UnloadTask.update(t12, 16)
|
|
check("W10 venter", t12.destinationID, 40)
|
|
waitBarnLevels[STRAW] = 30000 -- 20000 fri < 25000
|
|
UnloadTask.update(t12, 16)
|
|
check("W10 under halv stald fri -> venter stadig", t12.destinationID, 40)
|
|
waitBarnLevels[STRAW] = 20000 -- 30000 fri >= 25000
|
|
UnloadTask.update(t12, 16)
|
|
check("W10 halv stald fri -> hjem", t12.destinationID, 30)
|
|
|
|
-- L1: moden valgte siloen -> AutoDrive skal stå og læsse (rotation skjules under opgaven)
|
|
resetWorld()
|
|
local loadLevels = {[SUGARBEET] = 5000}
|
|
addMarker(1, "Silo 1", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, loadLevels))
|
|
local loader = withDrivePath(makeDriver(markers[1], {SUGARBEET}))
|
|
manager:getNextPickup(loader, false)
|
|
local task = {vehicle = loader, trailers = {level = 0}}
|
|
FS25_AutoDrive.LoadAtDestinationTask.update(task, 16)
|
|
check("L1 rotation skjult mens kilden har varen", task.seenRotate, RT_NONE)
|
|
check("L1 andre indstillinger uberørt", task.seenOther, "andet")
|
|
check("L1 rotation synlig igen efter opgaven", FS25_AutoDrive.AutoDrive.getSetting("rotateTargets", loader), RT_ONLYPICKUP)
|
|
|
|
-- L8: PÅ VEJ til siloen ruller vognen gennem nabosiloers triggere -> dér skal
|
|
-- AutoDrives rotation gælde (kør videre ved tom trigger), ikke "stå og vent"
|
|
loader.targetReached = false
|
|
FS25_AutoDrive.LoadAtDestinationTask.update(task, 16)
|
|
check("L8 undervejs: rotation uberørt", task.seenRotate, RT_ONLYPICKUP)
|
|
loader.targetReached = true
|
|
FS25_AutoDrive.LoadAtDestinationTask.update(task, 16)
|
|
check("L8 fremme ved markøren: står og læsser", task.seenRotate, RT_NONE)
|
|
|
|
-- L2: andre køretøjer (ikke sendt af moden) beholder AutoDrives rotation
|
|
local strangerTask = {vehicle = makeDriver(markers[1], {SUGARBEET})}
|
|
FS25_AutoDrive.LoadAtDestinationTask.update(strangerTask, 16)
|
|
check("L2 fremmed køretøj uberørt", strangerTask.seenRotate, RT_ONLYPICKUP)
|
|
|
|
-- L3: kilden løber tør -> grebet slippes, AutoDrive kører videre med det den har
|
|
loadLevels[SUGARBEET] = 0
|
|
FS25_AutoDrive.LoadAtDestinationTask.update(task, 16)
|
|
check("L3 tom kilde -> rotation synlig igen", task.seenRotate, RT_ONLYPICKUP)
|
|
|
|
-- L4: fejl i AutoDrives opgave må ikke efterlade rotationen skjult
|
|
loadLevels[SUGARBEET] = 5000
|
|
task.shouldFail = true
|
|
check("L4 fejl i opgaven sendes videre", pcall(FS25_AutoDrive.LoadAtDestinationTask.update, task, 16), false)
|
|
check("L4 rotation synlig efter fejl", FS25_AutoDrive.AutoDrive.getSetting("rotateTargets", loader), RT_ONLYPICKUP)
|
|
|
|
-- L5: AutoDrives original valgte (intet fund) -> ingen ventetid
|
|
task.shouldFail = false
|
|
loader.ad.stateModule.getSelectedFillTypes = function() return {WHEAT} end
|
|
manager:getNextPickup(loader, false)
|
|
FS25_AutoDrive.LoadAtDestinationTask.update(task, 16)
|
|
check("L5 AutoDrives eget valg -> rotation uberørt", task.seenRotate, RT_ONLYPICKUP)
|
|
|
|
-- G1: tæt silo-række, kørsel mod -x. Næsen stopper ved markøren, vognen holder et
|
|
-- vogntog BAGVED -> markørens silo er den bag næsen, ikke den (nærmere) foran.
|
|
resetWorld()
|
|
addMarker(1, "1 Silo", "Siloer", 1243, 0, 1260, 0)
|
|
addMarker(2, "2 Silo", "Siloer", 1228, 0, 1243, 0)
|
|
table.insert(stations, makeStation(101, 1255, 1, {[HAY] = 9000}, false, {[HAY] = 9000})) -- bag markør 1
|
|
table.insert(stations, makeStation(102, 1239, 1, {[SUGARBEET] = 5000}, false, {[SUGARBEET] = 5000})) -- foran markør 1, bag markør 2
|
|
table.insert(stations, makeStation(103, 1224, 1, {}, false, {})) -- foran markør 2
|
|
check("G1 roer ligger i siloen bag markør 2", manager:getNextPickup(makeDriver(markers[1], {SUGARBEET}), false), 2)
|
|
check("G1 hø ligger i siloen bag markør 1", manager:getNextPickup(makeDriver(markers[1], {HAY}), false), 1)
|
|
|
|
-- G2: ingen trigger bag næsen (eller ukendt retning) -> nærmeste trigger som før
|
|
resetWorld()
|
|
addMarker(1, "Ensom silo", "Siloer", 100, 0, 120, 0)
|
|
table.insert(stations, makeStation(101, 95, 1, {[HAY] = 9000}))
|
|
check("G2 kun trigger foran -> nærmeste bruges", manager:getNextPickup(makeDriver(markers[1], {HAY}), false), 1)
|
|
|
|
-- G3: tank med aflæsningsbane på den ene side og pålæsningsbane på den anden.
|
|
-- Dropoff-markøren ligger 18 m ved siden af læsserøret -> ikke en pålæsningsplads,
|
|
-- selv om den står først i listen og "ser" samme tank.
|
|
resetWorld()
|
|
addMarker(1, "1 Silage Dropof", "Ensilage", 1265, -2278, 1265, -2290) -- kører +z, røret 18 m til siden
|
|
addMarker(2, "1 Silage Pickup", "Ensilage", 1247, -2278, 1247, -2266) -- kører -z, røret 7 m bag næsen
|
|
table.insert(stations, makeStation(101, 1247, -2271, {[SILAGE] = 4000}))
|
|
check("G3 pickup-markøren vælges, ikke dropoff", manager:getNextPickup(makeDriver(markers[1], {SILAGE}), false), 2)
|
|
|
|
-- G5: markør-drift i en tank-række — næste tanks rør ligger 2 m FORAN næsen og er
|
|
-- nærmest, men vognen holder under røret 11 m bagved
|
|
resetWorld()
|
|
addMarker(1, "5 Silage Pickup", "Ensilage", 1246, -2218, 1246, -2230) -- kører +z
|
|
table.insert(stations, makeStation(101, 1246, -2216, {[SILAGE] = 9000})) -- 2 m foran næsen
|
|
table.insert(stations, makeStation(102, 1246, -2229, {[HAY] = 4000})) -- 11 m bag næsen: kun den har hø
|
|
addMarker(2, "6 Silage Pickup", "Ensilage", 1246, -2208, 1246, -2218)
|
|
check("G5 markør 5 hører til tanken bag næsen", manager:getNextPickup(makeDriver(markers[1], {HAY}), false), 1)
|
|
|
|
-- G4: kurve ind til markøren -> retningen snyder og røret ligger "foran"; tæt på = ok
|
|
resetWorld()
|
|
addMarker(1, "7 Silage Pickup", "Ensilage", 100, 0, 100, 20) -- kører -z
|
|
table.insert(stations, makeStation(101, 103, -8, {[SILAGE] = 4000})) -- 8 m foran, 3 m til siden
|
|
check("G4 tæt trigger foran næsen bruges som nødløsning", manager:getNextPickup(makeDriver(markers[1], {SILAGE}), false), 1)
|
|
|
|
-- L9: sikkerhedsventil — står den ved markøren uden at vognen fyldes, slippes grebet
|
|
resetWorld()
|
|
addMarker(1, "Silo 1", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[SUGARBEET] = 5000}))
|
|
local stuck = withDrivePath(makeDriver(markers[1], {SUGARBEET}))
|
|
manager:getNextPickup(stuck, false)
|
|
local stuckTask = {vehicle = stuck, trailers = {level = 0}}
|
|
FS25_AutoDrive.LoadAtDestinationTask.update(stuckTask, 10000)
|
|
stuckTask.trailers.level = 800 -- der læsses: uret nulstilles
|
|
FS25_AutoDrive.LoadAtDestinationTask.update(stuckTask, 25000)
|
|
FS25_AutoDrive.LoadAtDestinationTask.update(stuckTask, 25000)
|
|
check("L9 venter stadig mens der er læsset for nylig", stuckTask.seenRotate, RT_NONE)
|
|
FS25_AutoDrive.LoadAtDestinationTask.update(stuckTask, 10000) -- 35 s uden fremgang
|
|
check("L9 ingen læsning i 30 s -> greb sluppet", stuckTask.seenRotate, RT_ONLYPICKUP)
|
|
check("L9 sluppet greb logges som advarsel", logLines[#logLines]:sub(1, 4), "WARN")
|
|
FS25_AutoDrive.LoadAtDestinationTask.update(stuckTask, 16)
|
|
check("L9 grebet tages ikke igen på samme tur", stuckTask.seenRotate, RT_ONLYPICKUP)
|
|
|
|
-- M1: mælketanke løber fulde -> den FYLDESTE hentes først (siloer: mindst fyldte)
|
|
resetWorld()
|
|
addMarker(1, "Kostald 1", "Mælk", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[MILK] = 2000}))
|
|
addMarker(2, "Kostald 2", "Mælk", 500, 0); table.insert(stations, makeStation(102, 500, 15, {[MILK] = 48000}))
|
|
addMarker(3, "Kostald 3", "Mælk", 1000, 0); table.insert(stations, makeStation(103, 1000, 15, {[MILK] = 30000}))
|
|
check("M1 fyldeste mælketank først", manager:getNextPickup(makeDriver(markers[1], {MILK}), false), 2)
|
|
check("M1 loggen nævner strategien", logLines[#logLines]:find("fyldeste først", 1, true) ~= nil, true)
|
|
|
|
-- L7: AD stopper -> grebet ryddes, næste opgave venter ikke på en gammel silo
|
|
resetWorld()
|
|
addMarker(1, "Silo 1", "Andet", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[HAY] = 5000}))
|
|
addMarker(2, "Blander", "Fabrik", 5000, 0)
|
|
table.insert(unloadStations, makeUnloadStation(201, 5000, 15, {[HAY] = 0}, {[HAY] = 1000000, [GRASS] = 1000000}, {}))
|
|
local stopper = withDrivePath(makeSupplyDriver(markers[1], markers[2], {HAY, GRASS}))
|
|
manager:getNextPickup(stopper, false)
|
|
local stopperTask = {vehicle = stopper, trailers = {level = 0}}
|
|
FS25_AutoDrive.LoadAtDestinationTask.update(stopperTask, 16)
|
|
check("L7 venter mens AD kører", stopperTask.seenRotate, RT_NONE)
|
|
stopper.ad.stateModule.active = false
|
|
listener:update(16)
|
|
FS25_AutoDrive.LoadAtDestinationTask.update(stopperTask, 16)
|
|
check("L7 greb ryddet når AD stopper", stopperTask.seenRotate, RT_ONLYPICKUP)
|
|
|
|
-- W11: reservationer kan aflæses (GUI'ets Wait-puljer-visning)
|
|
buildWaitWorld()
|
|
local vc1, tc1 = makeUnloadingVehicle("Fastrac Claim", 531, 5000, 35000)
|
|
UnloadTask.update(tc1, 16)
|
|
local claims = ADUnloadWait.getClaims()
|
|
check("W11 én reservation", #claims, 1)
|
|
check("W11 markør", claims[1].marker.id, 40)
|
|
check("W11 køretøj", claims[1].vehicle, vc1)
|
|
|
|
|
|
-- W12: AD regner vognen som "aflæssende" ved et fuldt trug (isUnloading bliver hængende) —
|
|
-- omdirigeres alligevel, og AD's aflæsning stoppes først
|
|
buildWaitWorld()
|
|
local vs, ts = makeUnloadingVehicle("Fastrac Tippet", 541, 5000, 35000)
|
|
local stopped = false
|
|
vs.ad.trailerModule = {isActiveAtTrigger = function() return true end, stopUnloading = function() stopped = true end, stopLoading = function() end}
|
|
UnloadTask.update(ts, 16)
|
|
check("W12 omdirigeret trods isActiveAtTrigger", ts.destinationID, 40)
|
|
check("W12 AD's aflæsning stoppet før kørsel", stopped, true)
|
|
|
|
-- E5: fodertrug = én trang; inden for truget vælges efter spillets fodervægt (TMR 1,0 > hø 0,8 > græs 0,4);
|
|
-- et tommere halmlager vinder over truget
|
|
buildCowWorld()
|
|
addMarker(23, "Høsilo", "Lader", 700, 0); table.insert(stations, makeStation(123, 700, 15, {[HAY] = 80000}, false, {[HAY] = 80000}))
|
|
addMarker(24, "Græssilo", "Lader", 800, 0); table.insert(stations, makeStation(124, 800, 15, {[GRASS] = 80000}, false, {[GRASS] = 80000}))
|
|
local e5Food = {[TMR] = 0, [HAY] = 0, [GRASS] = 0}
|
|
local e5Straw = {level = 20000}
|
|
local e5Barn = {
|
|
getName = function() return "Prioritets-stald" end,
|
|
spec_husbandryFood = {fillLevels = e5Food, capacity = 30000, animalTypeIndex = 7},
|
|
spec_husbandryStraw = {},
|
|
getHusbandryIsFillTypeSupported = function(_, fillType) return fillType == STRAW end,
|
|
getHusbandryFillLevel = function(_, fillType) if fillType == STRAW then return e5Straw.level end return 0 end,
|
|
getHusbandryCapacity = function(_, fillType) if fillType == STRAW then return 100000 end return 0 end,
|
|
}
|
|
g_currentMission.animalFoodSystem = {
|
|
getAnimalFood = function(_, animalTypeIndex) if animalTypeIndex == 7 then return {groups = {
|
|
{fillTypes = {TMR}, productionWeight = 1.0}, {fillTypes = {HAY}, productionWeight = 0.8}, {fillTypes = {GRASS}, productionWeight = 0.4}}} end end,
|
|
getMixturesByAnimalTypeIndex = function() return {} end,
|
|
}
|
|
unloadStations[1].targetStorages = nil
|
|
unloadStations[1].owningPlaceable = e5Barn
|
|
local e5Driver = makeSupplyDriver(markerByIndex(21), markerByIndex(30), {TMR, HAY, GRASS, STRAW})
|
|
e5Driver.carriable = {TMR, HAY, GRASS, STRAW}
|
|
e5Driver.trailers = {level = 0, capacity = 8000}
|
|
-- trug tomt (100 % trang) vs. halm 80 % tomt -> truget først, og TMR før hø/græs
|
|
check("E5 tomt trug: TMR først", manager:getNextPickup(e5Driver, false), 20)
|
|
-- ingen TMR-kilde -> hø (0,8) før græs (0,4)
|
|
stations[1].getFillLevel = function() return 0 end
|
|
stations[1].owningPlaceable = nil
|
|
check("E5 uden TMR-kilde: hø før græs", manager:getNextPickup(e5Driver, false), 23)
|
|
-- trug 70 % fuldt (30 % trang) vs. halm 80 % tomt -> halm
|
|
e5Food[HAY] = 21000
|
|
check("E5 halmlager tommere end truget -> halm", manager:getNextPickup(e5Driver, false), 21)
|
|
-- E6 (bedste foder først): TMR-kilden er tom, truget halvfuldt, halm fuld -> INGEN ensilage/hø; en traktor
|
|
-- i et forsynings-run holder ved Wait i stedet (in-game 24/9: Fastrac'erne fyldte truget med ensilage)
|
|
e5Straw.level = 100000
|
|
e5Food[TMR], e5Food[HAY], e5Food[GRASS] = 0, 15000, 0 -- 50 % fri
|
|
e5Driver.uniqueId = "vehicleE6"
|
|
testVehicles = {e5Driver}
|
|
ADRunsController.store = ADRuns.newStore()
|
|
local e6Run = ADRunsController.createRun("Cow Food")
|
|
ADRunsController.updateRun(e6Run.id, {targetWayPointId = 30, ingredientMode = "manual", ingredients = {"FORAGE", "DRYGRASS_WINDROW", "GRASS_WINDROW", "STRAW"}})
|
|
ADRunsController.assignVehicle(e6Run.id, e5Driver)
|
|
local holdCalls = {}
|
|
ADOutbound = {
|
|
isOutboundVehicle = function() return false end,
|
|
holdSupplyVehicle = function(adEnv, vehicle, run, reasons, replan) table.insert(holdCalls, {reasons = reasons, replan = replan}) return {markerIndex = 99, markerName = "Cow Wait", group = "Cow Wait", fillTypes = {}} end,
|
|
releaseInactive = function() end, install = function() end,
|
|
}
|
|
check("E6 halvfuldt trug uden TMR: hold ved Wait", manager:getNextPickup(e5Driver, false), 99)
|
|
check("E6 hold kaldt én gang", #holdCalls, 1)
|
|
check("E6 replan finder intet endnu", holdCalls[1].replan(), nil)
|
|
e5Food[HAY] = 3000 -- 90 % fri: nødlidende -> hø tilladt
|
|
check("E6 replan: nødlidende trug -> hø", holdCalls[1].replan(), 23)
|
|
check("E6 nødlidende trug: hø direkte", manager:getNextPickup(e5Driver, false), 23)
|
|
ADOutbound = nil
|
|
ADRunsController.store = ADRuns.newStore()
|
|
testVehicles = {}
|
|
g_currentMission.animalFoodSystem = nil
|
|
|
|
-- W13: AD stopper vognen i triggeren FØR markøren (isTargetReached=false) — Wait udløses alligevel
|
|
-- når vognen er inden for AD's trigger-afstand til aflæsningsstedet
|
|
buildWaitWorld()
|
|
local vn, tn = makeUnloadingVehicle("Fastrac Før markør", 581, 5000, 35000)
|
|
vn.targetReached = false
|
|
FS25_AutoDrive.AutoDrive.getDistanceToUnloadPosition = function() return 8.6 end
|
|
FS25_AutoDrive.AutoDrive.getMaxTriggerDistance = function() return 15 end
|
|
UnloadTask.update(tn, 16)
|
|
check("W13 Wait selv om markøren ikke er nået", tn.destinationID, 40)
|
|
FS25_AutoDrive.AutoDrive.getDistanceToUnloadPosition = nil
|
|
FS25_AutoDrive.AutoDrive.getMaxTriggerDistance = nil
|
|
|
|
-- W14: mens der ventes, peger AD's mål på Wait-punktet (så AD ikke tipper i småportioner);
|
|
-- behovet måles stadig mod hjem-målet, og AD's mål gendannes når den kører hjem
|
|
buildWaitWorld()
|
|
local vm, tm = makeUnloadingVehicle("Fastrac Mål", 591, 5000, 35000)
|
|
UnloadTask.update(tm, 16)
|
|
check("W14 AD's mål = Wait-punkt under ventetid", vm.ad.stateModule.secondMarkerId, 40)
|
|
waitBarnLevels[STRAW] = 10000 -- plads hjemme
|
|
UnloadTask.update(tm, 16)
|
|
check("W14 kører hjem (behov målt mod stalden, ikke Wait-punktet)", tm.destinationID, 30)
|
|
check("W14 AD's mål gendannet", vm.ad.stateModule.secondMarkerId, 30)
|
|
|
|
-- ==== 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()
|
|
|
|
-- 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
|
|
-- halmladen (markør 21) er også et aflæsningssted med plads
|
|
table.insert(unloadStations, stations[2])
|
|
stations[2].unloadTriggers = {{exactFillRootNode = 121}}
|
|
stations[2].getIsFillTypeSupported = function(_, fillType) return fillType == STRAW end
|
|
stations[2].getFreeCapacity = function() return 100000 end
|
|
stations[2].owningPlaceable = {spec_silo = {storages = {{getFillLevel = function(_, fillType) return fillType == STRAW and 50000 or 0 end}}}}
|
|
end
|
|
|
|
-- S1: ventende traktor med halm-rest -> TMR mangler et fuldt læs (40000 l vogn, 90000 l fri) -> til halmladen
|
|
buildSwapWorld()
|
|
local sv, st = makeUnloadingVehicle("Fastrac Bytte", 521, 5000, 35000)
|
|
UnloadTask.update(st, 16)
|
|
check("S1 venter først ved Wait", st.destinationID, 40)
|
|
UnloadTask.update(st, ADLoadSwap.CHECK_INTERVAL_MS + 1)
|
|
check("S1 omdirigeret til halmladen", st.destinationID, 21)
|
|
check("S1 logget bytte", countLogsSince(1, "bytter læs") >= 1, true)
|
|
check("S1 Wait-punkt frigivet", FS25_AutoDrive.AutoDrive.getSetting("rotateTargets", sv), RT_ONLYPICKUP)
|
|
check("S1 AD's mål peger midlertidigt på siloen (ellers tipper AD ikke)", sv.ad.stateModule.secondMarkerId, 21)
|
|
UnloadTask.finished(st)
|
|
check("S1 mål gendannet efter aflæsning", sv.ad.stateModule.secondMarkerId, 30)
|
|
|
|
-- S2: målet mangler kun lidt TMR (10 % af lageret) og et fuldt læs passer ikke -> bliv ved Wait
|
|
buildSwapWorld()
|
|
waitBarnLevels[TMR] = 90000 -- 10000 fri < 40000 vogn og < 25 % af 100000
|
|
local sv2, st2 = makeUnloadingVehicle("Fastrac Bliv", 522, 5000, 35000)
|
|
UnloadTask.update(st2, 16)
|
|
UnloadTask.update(st2, ADLoadSwap.CHECK_INTERVAL_MS + 1)
|
|
check("S2 bliver ved Wait", st2.destinationID, 40)
|
|
|
|
-- S2b: vognen er større end stalden (fuldt læs passer aldrig), men TMR mangler 25 % -> byt alligevel
|
|
-- (in-game 24/9: Fastrac'erne byttede aldrig, fordi vognen er større end staldens lager)
|
|
buildSwapWorld()
|
|
waitBarnLevels[TMR] = 75000 -- 25000 fri < 40000 vogn, men = 25 % af 100000
|
|
local sv2b, st2b = makeUnloadingVehicle("Fastrac Stor", 526, 5000, 35000)
|
|
UnloadTask.update(st2b, 16)
|
|
UnloadTask.update(st2b, ADLoadSwap.CHECK_INTERVAL_MS + 1)
|
|
check("S2b bytter når 25 % mangler", st2b.destinationID, 21)
|
|
|
|
-- S3: ingen silo med plads/vare -> bliv ved Wait, én WARN
|
|
buildSwapWorld()
|
|
stations[2].getFreeCapacity = function() return 1000 end
|
|
local sv3, st3 = makeUnloadingVehicle("Fastrac Ingen", 523, 5000, 35000)
|
|
UnloadTask.update(st3, 16)
|
|
local mark3 = #logLines
|
|
UnloadTask.update(st3, ADLoadSwap.CHECK_INTERVAL_MS + 1)
|
|
UnloadTask.update(st3, ADLoadSwap.CHECK_INTERVAL_MS + 1)
|
|
check("S3 bliver ved Wait", st3.destinationID, 40)
|
|
check("S3 én advarsel", countLogsSince(mark3, "bliver ved Wait-punktet"), 1)
|
|
|
|
-- S4: silo uden vejnet hjem springes over
|
|
buildSwapWorld()
|
|
FS25_AutoDrive.ADGraphManager.unreachable[21] = true
|
|
local sv4, st4 = makeUnloadingVehicle("Fastrac Vej", 524, 5000, 35000)
|
|
UnloadTask.update(st4, 16)
|
|
UnloadTask.update(st4, ADLoadSwap.CHECK_INTERVAL_MS + 1)
|
|
check("S4 unåelig silo -> bliv", st4.destinationID, 40)
|
|
FS25_AutoDrive.ADGraphManager.unreachable = {}
|
|
|
|
-- S5: traktor med kun én vare i listen bytter aldrig
|
|
buildSwapWorld()
|
|
local sv5, st5 = makeUnloadingVehicle("Fastrac Solo", 525, 5000, 35000)
|
|
sv5.ad.stateModule.selectedFillTypes = {STRAW}
|
|
UnloadTask.update(st5, 16)
|
|
UnloadTask.update(st5, ADLoadSwap.CHECK_INTERVAL_MS + 1)
|
|
check("S5 én vare -> bliver", st5.destinationID, 40)
|
|
|
|
-- S6: efter bytte til siloen skal traktoren blive der (ikke sendes til et nyt Wait-punkt,
|
|
-- fordi siloen fejlagtigt tolkes som "målet er fuldt")
|
|
buildSwapWorld()
|
|
local sv6, st6 = makeUnloadingVehicle("Fastrac Silo", 526, 5000, 35000)
|
|
UnloadTask.update(st6, 16)
|
|
UnloadTask.update(st6, ADLoadSwap.CHECK_INTERVAL_MS + 1)
|
|
local mark6 = #logLines
|
|
UnloadTask.update(st6, 16)
|
|
UnloadTask.update(st6, 16)
|
|
check("S6 bliver ved siloen efter bytte", st6.destinationID, 21)
|
|
check("S6 ingen ny Wait-omdirigering", countLogsSince(mark6, "venter ved"), 0)
|
|
|
|
-- I5: run'ets "kør hjem under X %" styrer hvornår den ventende kører hjem
|
|
buildWaitWorld()
|
|
local vt, tt = makeUnloadingVehicle("Fastrac Tærskel", 551, 5000, 60000) -- rest > stald: kun tærsklen kan sende hjem
|
|
vt.uniqueId = "vehicleThreshold"
|
|
testVehicles = {vt}
|
|
ADRunsController.store = ADRuns.newStore()
|
|
local thrRun = ADRunsController.createRun("Tærskel")
|
|
ADRunsController.updateRun(thrRun.id, {targetWayPointId = 30, returnBelowPercent = 20})
|
|
ADRunsController.assignVehicle(thrRun.id, vt)
|
|
UnloadTask.update(tt, 16)
|
|
check("I5 venter først", tt.destinationID, 40)
|
|
waitBarnLevels[STRAW] = 15000 -- 70 % fri: 50 %-reglen ville sende hjem, men tærsklen er "under 20 %" (= 80 % fri)
|
|
UnloadTask.update(tt, 16)
|
|
check("I5 70 % fri er ikke nok ved 'under 20 %'", tt.destinationID, 40)
|
|
waitBarnLevels[STRAW] = 9000 -- 82 % fri >= 80 %
|
|
UnloadTask.update(tt, 16)
|
|
check("I5 hjem når målet er under 20 %", tt.destinationID, 30)
|
|
ADRunsController.store = ADRuns.newStore()
|
|
|
|
-- I6: run med to stalde — ved turstart vælges den stald der mangler mest, AD's mål skiftes, og
|
|
-- varen hentes efter DEN stalds behov
|
|
buildCowWorld()
|
|
addMarker(31, "Cow 2 Food", "Cow Food", 6000, 0)
|
|
local cow2Levels = {[STRAW] = 95000, [TMR] = 95000}
|
|
table.insert(unloadStations, makeUnloadStation(131, 6000, 10, cow2Levels, {[STRAW] = 100000, [TMR] = 100000}, {name = "kostald 2"}))
|
|
cowLevels[STRAW], cowLevels[TMR] = 90000, 20000 -- stald 1: TMR mangler
|
|
local i6 = makeSupplyDriver(markerByIndex(21), markerByIndex(31), {TMR, STRAW}) -- AD peger på stald 2 (fuld)
|
|
i6.uniqueId = "vehicleI6"; i6.carriable = {TMR, STRAW}; i6.trailers = {level = 0, capacity = 8000}
|
|
i6.ad.stateModule.setSecondMarkerByWayPointId = function(self, id) self.secondMarkerId = id end
|
|
i6.ad.stateModule.getSecondMarker = function(self) return markerByIndex(self.secondMarkerId or 31) end
|
|
testVehicles = {i6}
|
|
ADRunsController.store = ADRuns.newStore()
|
|
local twoBarns = ADRunsController.createRun("Kvæg")
|
|
ADRunsController.updateRun(twoBarns.id, {targetWayPointIds = {30, 31}})
|
|
ADRunsController.assignVehicle(twoBarns.id, i6)
|
|
check("I6 henter TMR til stald 1", manager:getNextPickup(i6, false), 20)
|
|
check("I6 AD's mål skiftet til stald 1", i6.ad.stateModule.secondMarkerId, 30)
|
|
check("I6 skiftet logges", countLogsSince(1, "kører til Cow 1 Food") >= 1, true)
|
|
cowLevels[STRAW], cowLevels[TMR] = 99000, 99000
|
|
cow2Levels[STRAW] = 10000 -- nu mangler stald 2 halm
|
|
check("I6 næste tur: halm til stald 2", manager:getNextPickup(i6, false), 21)
|
|
check("I6 mål skiftet til stald 2", i6.ad.stateModule.secondMarkerId, 31)
|
|
ADRunsController.store = ADRuns.newStore()
|
|
|
|
-- I7: det valgte mål har intet der kan hentes (TMR-kilden er tom) -> prøv run'ets ANDRE mål før Wait
|
|
-- (in-game 24/9: to traktorer fik begge 'Biogas Drop 1'; nr. 2 holdt selv om 4 DOB'er var tomme)
|
|
buildCowWorld()
|
|
addMarker(31, "Cow 2 Food", "Cow Food", 6000, 0)
|
|
local cow2LevelsI7 = {[STRAW] = 10000, [TMR] = 100000}
|
|
table.insert(unloadStations, makeUnloadStation(131, 6000, 10, cow2LevelsI7, {[STRAW] = 100000, [TMR] = 100000}, {name = "kostald 2"}))
|
|
cowLevels[STRAW], cowLevels[TMR] = 100000, 0 -- stald 1 mangler KUN TMR (mest trængende)
|
|
stations[1].getFillLevel = function(_, fillType) return fillType == STRAW and 5000 or 0 end -- TMR-kilden tom
|
|
local i7 = makeSupplyDriver(markerByIndex(21), markerByIndex(30), {TMR, STRAW})
|
|
i7.uniqueId = "vehicleI7"; i7.carriable = {TMR, STRAW}; i7.trailers = {level = 0, capacity = 8000}
|
|
i7.ad.stateModule.setSecondMarkerByWayPointId = function(self, id) self.secondMarkerId = id end
|
|
i7.ad.stateModule.getSecondMarker = function(self) return markerByIndex(self.secondMarkerId or 30) end
|
|
testVehicles = {i7}
|
|
ADRunsController.store = ADRuns.newStore()
|
|
local twoBarnsI7 = ADRunsController.createRun("Kvæg2")
|
|
ADRunsController.updateRun(twoBarnsI7.id, {targetWayPointIds = {30, 31}})
|
|
ADRunsController.assignVehicle(twoBarnsI7.id, i7)
|
|
check("I7 henter halm til det andet mål", manager:getNextPickup(i7, false), 21)
|
|
check("I7 AD's mål skiftet til stald 2", i7.ad.stateModule.secondMarkerId, 31)
|
|
ADRunsController.store = ADRuns.newStore()
|
|
|
|
-- ==== V: forkert last på vognen ====
|
|
-- V1: pålæsning starter med anden vare på vognen -> opgaven afsluttes straks (AD leverer det den har)
|
|
buildCowWorld()
|
|
cowLevels[STRAW], cowLevels[TMR] = 90000, 0 -- TMR mangler
|
|
local wv = makeSupplyDriver(markerByIndex(21), markerByIndex(30), {TMR, STRAW})
|
|
wv.carriable = {TMR, STRAW}; wv.trailers = {level = 8000, capacity = 8000}
|
|
wv.units = {{object = {getFillUnitFillLevel = function() return 8000 end, getFillUnitFillType = function() return STRAW end}, fillUnitIndex = 1}}
|
|
withDrivePath(wv); wv.targetReached = false
|
|
check("V1 TMR vælges som behov", manager:getNextPickup(wv, false), 20)
|
|
local loadTask = {vehicle = wv, trailers = wv.trailers, finished = function(self) self.isFinished = true end}
|
|
FS25_AutoDrive.LoadAtDestinationTask.update(loadTask, 16)
|
|
check("V1 pålæsningsopgave afsluttet pga. halm på vognen", loadTask.isFinished, true)
|
|
check("V1 logget", countLogsSince(1, "leverer først") >= 1, true)
|
|
-- samme vare på vognen som den der hentes -> opgaven kører normalt
|
|
wv.units[1].object.getFillUnitFillType = function() return TMR end
|
|
local loadTask2 = {vehicle = wv, trailers = wv.trailers, finished = function(self) self.isFinished = true end}
|
|
manager:getNextPickup(wv, false)
|
|
FS25_AutoDrive.LoadAtDestinationTask.update(loadTask2, 16)
|
|
check("V1 samme vare -> ikke afsluttet", loadTask2.isFinished, nil)
|
|
|
|
-- V2: ved målet med en vare målet ikke tager -> Wait; ved Wait -> silo uanset "manglende" vare
|
|
buildWaitWorld()
|
|
table.insert(unloadStations, stations[2]) -- halmladen tager halm
|
|
stations[2].unloadTriggers = {{exactFillRootNode = 121}}
|
|
stations[2].getIsFillTypeSupported = function(_, fillType) return fillType == STRAW end
|
|
stations[2].getFreeCapacity = function() return 100000 end
|
|
stations[2].owningPlaceable = {spec_silo = {storages = {{getFillLevel = function() return 0 end}}}}
|
|
waitBarnCapacity[STRAW] = nil -- stalden tager IKKE halm
|
|
local vw, tw = makeUnloadingVehicle("Fastrac Forkert", 561, 5000, 35000)
|
|
UnloadTask.update(tw, 16)
|
|
check("V2 målet tager ikke varen -> Wait", tw.destinationID, 40)
|
|
UnloadTask.update(tw, ADLoadSwap.CHECK_INTERVAL_MS + 1)
|
|
check("V2 fra Wait til silo selv uden manglende vare", tw.destinationID, 21)
|
|
waitBarnCapacity[STRAW] = 50000
|
|
|
|
-- V3: Start med forkert last -> AD's mål = silo; første getNextPickup må IKKE sætte run-målet tilbage,
|
|
-- og efter aflæsningen (finished) gendannes run-målet
|
|
buildCowWorld()
|
|
table.insert(unloadStations, stations[2]) -- halmladen tager halm
|
|
stations[2].unloadTriggers = {{exactFillRootNode = 121}}
|
|
stations[2].getIsFillTypeSupported = function(_, fillType) return fillType == STRAW end
|
|
stations[2].getFreeCapacity = function() return 100000 end
|
|
stations[2].owningPlaceable = {spec_silo = {storages = {{getFillLevel = function() return 0 end}}}}
|
|
local v3 = makeSupplyDriver(markerByIndex(21), markerByIndex(30), {TMR, STRAW})
|
|
v3.uniqueId = "vehicleV3"; v3.carriable = {TMR}; v3.trailers = {level = 4000, capacity = 8000}; v3.rootNode = 571; nodePositions[571] = {x = 0, z = 0}
|
|
v3.units = {{object = {getFillUnitFillLevel = function() return 4000 end, getFillUnitFillType = function() return STRAW end}, fillUnitIndex = 1}}
|
|
v3.ad.stateModule.secondMarkerId = 30
|
|
v3.ad.stateModule.setSecondMarkerByWayPointId = function(self, id) self.secondMarkerId = id end
|
|
v3.ad.stateModule.getSecondMarker = function(self) return markerByIndex(self.secondMarkerId) end
|
|
cowLevels[STRAW] = nil -- stald 1 tager i denne test kun TMR
|
|
unloadStations[1].getIsFillTypeSupported = function(_, fillType) return fillType == TMR end
|
|
testVehicles = {v3}
|
|
ADRunsController.store = ADRuns.newStore()
|
|
local v3Run = ADRunsController.createRun("TMR only")
|
|
ADRunsController.updateRun(v3Run.id, {targetWayPointId = 30, ingredientMode = "manual", ingredients = {"FORAGE"}})
|
|
ADRunsController.assignVehicle(v3Run.id, v3)
|
|
ADUnloadWait.setDumpTarget(v3, 21) -- som Start gør ved forkert last
|
|
check("V3 AD's mål = silo", v3.ad.stateModule.secondMarkerId, 21)
|
|
manager:getNextPickup(v3, false)
|
|
check("V3 getNextPickup rører ikke målet mens silo-aflæsning er undervejs", v3.ad.stateModule.secondMarkerId, 21)
|
|
UnloadTask.finished({vehicle = v3})
|
|
check("V3 mål gendannet efter aflæsning", v3.ad.stateModule.secondMarkerId, 30)
|
|
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])
|
|
check("E1 ukendt waypoint -> nil", ADSmartPickup.getUnloadStationAtWayPointId(FS25_AutoDrive, 9999, 1), nil)
|
|
check("E2 læssestation ved waypoint", ADSmartPickup.getLoadStationAtWayPointId(FS25_AutoDrive, 21, 1), stations[2])
|
|
local eLevel, eCapacity = ADSmartPickup.getStationLevelAndCapacity(unloadStations[1], TMR, 1)
|
|
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
|
|
resetWorld()
|
|
addMarker(1, "Silo 1", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[SUGARBEET] = 5000}, false, {[SUGARBEET] = 5000}))
|
|
local chatty = makeDriver(markers[1], {SUGARBEET})
|
|
manager:getNextPickup(chatty, false); manager:getNextPickup(chatty, false)
|
|
check("L6 gentaget valg logges én gang", #logLines, 1)
|
|
check("L6 loggen viser eget lager", logLines[1]:find("eget lager", 1, true) ~= nil, true)
|
|
|
|
-- W15: efter genindlæsning er ventetilstanden væk, men AD's mål peger stadig på Wait-punktet og
|
|
-- traktoren står der med rest-læs -> ventningen genoptages (hjem = run'ets mål) og den kører hjem
|
|
-- når der er plads
|
|
buildWaitWorld()
|
|
local orphan, orphanTask = makeUnloadingVehicle("Genindlæst", 515, 5090, 20000)
|
|
orphan.uniqueId = "vehicleOrphan"
|
|
orphan.spec_motorized = {}
|
|
orphan.ad.stateModule.secondMarkerId = 40 -- AD's gemte mål = Wait 1
|
|
orphanTask.destinationID = 40
|
|
orphan.ad.taskModule = {getActiveTask = function() return orphanTask end}
|
|
testVehicles = {orphan}
|
|
ADRunsController.store = ADRuns.newStore()
|
|
local orphanRun = ADRunsController.createRun("Cow Feed")
|
|
ADRunsController.updateRun(orphanRun.id, {targetWayPointId = 30})
|
|
ADRunsController.assignVehicle(orphanRun.id, orphan)
|
|
check("W15 ingen ventning før", ADUnloadWait.getWaitFor(orphan), nil)
|
|
ADUnloadWait.adoptOrphans(FS25_AutoDrive)
|
|
local adopted = ADUnloadWait.getWaitFor(orphan)
|
|
check("W15 ventning genoptaget", adopted ~= nil and adopted.markerName, "Wait 1")
|
|
check("W15 Wait-punktet reserveret", ADWaitPool.getOwner(40), orphan)
|
|
check("W15 hjem-mål husket", ADUnloadWait.getPendingTargetRestore(orphan), 30)
|
|
check("W15 logget", logLines[#logLines]:find("genoptager", 1, true) ~= nil, true)
|
|
ADUnloadWait.adoptOrphans(FS25_AutoDrive)
|
|
check("W15 ikke dobbelt", ADWaitPool.getOwner(40), orphan)
|
|
waitBarnLevels[STRAW] = 0 -- plads til hele resten
|
|
UnloadTask.update(orphanTask, 16)
|
|
check("W15 kører hjem", orphanTask.destinationID, 30)
|
|
check("W15 AD's mål gendannet", orphan.ad.stateModule.secondMarkerId, 30)
|
|
testVehicles = {}
|
|
|
|
-- N1: løst opslag (stald/fabrik): nærmeste læssestation inden for maxDistance, uanset retning og "bag næsen"
|
|
-- (in-game 23/9: mælketankens trigger lå ved siden af markøren, 20+ m væk -> "ingen læssestation")
|
|
resetWorld()
|
|
addMarker(1, "cow 1 milk", "Milk", 0, 0, -30, 0) -- kører mod +x; tanken ligger 25 m til siden
|
|
table.insert(stations, makeStation(101, 0, 25, {[WHEAT] = 1}))
|
|
check("N1 strengt opslag finder intet", ADSmartPickup.getLoadStationAtWayPointId(FS25_AutoDrive, 1, 1), nil)
|
|
check("N1 løst opslag finder tanken", ADSmartPickup.getLoadStationNearWayPointId(FS25_AutoDrive, 1, 1, 40), stations[1])
|
|
check("N1 uden for afstanden -> nil", ADSmartPickup.getLoadStationNearWayPointId(FS25_AutoDrive, 1, 1, 20), nil)
|
|
|
|
-- W16: lille rest (< 100 l) som målet ikke kan tage -> aflæsningen afsluttes, traktoren kører videre
|
|
-- (in-game 24/9: "tom" vogn holdt i Cow 1 Food med en rest, to andre ventede bag den)
|
|
buildWaitWorld()
|
|
local vr, tr = makeUnloadingVehicle("Fastrac Rest", 601, 5000, 50)
|
|
setmetatable(tr, {__index = UnloadTask})
|
|
UnloadTask.update(tr, 16)
|
|
check("W16 lille rest + fuldt mål -> afsluttet", tr.isFinished, true)
|
|
check("W16 ikke sendt til Wait", tr.destinationID, 30)
|
|
buildWaitWorld()
|
|
waitBarnLevels[STRAW] = 0
|
|
local vr2, tr2 = makeUnloadingVehicle("Fastrac Rest2", 602, 5000, 50)
|
|
setmetatable(tr2, {__index = UnloadTask})
|
|
UnloadTask.update(tr2, 16)
|
|
check("W16 lille rest med plads -> AD læsser selv af", tr2.isFinished, nil)
|
|
|
|
-- V: flere traktorer til samme mål fordeler sig — læs på vej tælles med (in-game 24/9: alle 4 Fastrac'er
|
|
-- hentede det samme). Græs mangler mest (20000/100000), ensilage næst (50000/100000).
|
|
buildFeedWorld()
|
|
local feederA = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS, SILAGE})
|
|
feederA.trailers = {level = 0, capacity = 70000}
|
|
local feederB = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS, SILAGE})
|
|
feederB.trailers = {level = 0, capacity = 70000}
|
|
check("V1 A henter græs (mangler mest)", manager:getNextPickup(feederA, false), 3)
|
|
check("V1 B henter ensilage (græs er på vej med A)", manager:getNextPickup(feederB, false), 4)
|
|
check("V1 A's læs registreret på vej", ADSmartPickup.getPending(10, feederB)[GRASS], 40000)
|
|
ADSmartPickup.clearEnRoute(feederA)
|
|
check("V2 A læsset af -> intet på vej", ADSmartPickup.getPending(10, feederB)[GRASS], nil)
|
|
check("V2 egne læs tæller ikke", ADSmartPickup.getPending(10, feederB)[SILAGE], nil)
|
|
|
|
-- X: udkørsel koblet på getNextPickup-wrap'en og Wait-logikken
|
|
resetWorld()
|
|
addMarker(1, "Suger Load", "Pickup points", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[WHEAT] = 1000}))
|
|
addMarker(2, "Wait 3", "General Wait", 100, 0)
|
|
local outboundDriver = makeDriver(markers[1], {WHEAT})
|
|
local outboundCalls, outboundChoice, outboundError = 0, nil, false
|
|
ADOutbound = {
|
|
isOutboundVehicle = function(vehicle) return vehicle == outboundDriver end,
|
|
choosePickup = function(adEnv, vehicle) outboundCalls = outboundCalls + 1; if outboundError then error("boom") end return outboundChoice end,
|
|
releaseInactive = function() end,
|
|
install = function() end,
|
|
}
|
|
outboundChoice = {markerIndex = markers[1].markerIndex, markerName = "Suger Load", group = "Pickup points", station = stations[1], fillTypes = {WHEAT}, fillType = WHEAT}
|
|
check("X1 udkørsel: kilden vælges af ADOutbound", manager:getNextPickup(outboundDriver, false), markers[1].markerIndex)
|
|
check("X1 ADOutbound spurgt", outboundCalls, 1)
|
|
check("X1 greb sat (station kendt)", ADSmartPickup.getActivePickup(outboundDriver) ~= nil, true)
|
|
outboundChoice = {markerIndex = markers[2].markerIndex, markerName = "Wait 3", group = "General Wait", station = nil, fillTypes = {}, fillType = nil}
|
|
check("X1 Wait-markør uden station", manager:getNextPickup(outboundDriver, false), markers[2].markerIndex)
|
|
check("X1 intet greb uden station", ADSmartPickup.getActivePickup(outboundDriver), nil)
|
|
outboundChoice = nil
|
|
check("X1 nil -> AutoDrives original", manager:getNextPickup(outboundDriver, false), "ORIGINAL")
|
|
outboundError = true
|
|
check("X1 fejl -> AutoDrives original", manager:getNextPickup(outboundDriver, false), "ORIGINAL")
|
|
outboundError = false
|
|
-- setActivePickup (bruges af opsamlingsrunden) opdaterer grebet direkte
|
|
ADSmartPickup.setActivePickup(outboundDriver, {station = stations[1], fillTypes = {WHEAT}, markerName = "Suger Load", fillType = WHEAT})
|
|
check("X2 setActivePickup", ADSmartPickup.getActivePickup(outboundDriver).markerName, "Suger Load")
|
|
ADSmartPickup.setActivePickup(outboundDriver, nil)
|
|
check("X2 setActivePickup nil rydder", ADSmartPickup.getActivePickup(outboundDriver), nil)
|
|
-- Wait-logikken springer udkørsels-traktorer over (rører ikke vognens indhold)
|
|
local originalUnits = FS25_AutoDrive.AutoDrive.getAllDischargeableUnits
|
|
FS25_AutoDrive.AutoDrive.getAllDischargeableUnits = function() error("må ikke kaldes for udkørsel") end
|
|
outboundDriver.ad.drivePathModule = {isTargetReached = function() return true end}
|
|
local okWait = pcall(ADUnloadWait.beforeUpdate, FS25_AutoDrive, {vehicle = outboundDriver, destinationID = 1}, 16)
|
|
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 = {unloadTrigger = {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)
|