-- Kør: luajit tests/test_adSources.lua (fra repo-roden) -- Kilde-genkendelse ved en AD-markør (fabrik / silo / objektlager), lagerstatus pr. vare, -- paller ved fabrikkens spawner, salgssteder, objektlager-udtag. local SUGAR, FORAGE, BASKET, CRATE = 5, 6, 7, 8 local DESC = { [SUGAR] = {index = SUGAR, name = "SUGAR", title = "Sukker", isPalletType = true}, [FORAGE] = {index = FORAGE, name = "FORAGE", title = "TMR"}, [BASKET] = {index = BASKET, name = "STRAW_BASKET", title = "Halmkurve", palletFilename = "basketPallet.xml"}, [CRATE] = {index = CRATE, name = "STRAW_CRATE", title = "Halmkasser", palletFilename = "cratePallet.xml"}, } g_fillTypeManager = { getFillTypeByIndex = function(_, index) return DESC[index] end, getFillTypeNameByIndex = function(_, index) return DESC[index] and DESC[index].name end, getFillTypeTitleByIndex = function(_, index) return DESC[index] and DESC[index].title end, } Logging = {info = function() end, warning = function() end} getWorldTranslation = function(node) return node.x, 0, node.z end SellingStation = {} dofile("FS25_ADSmartPickup/adPrices.lua") dofile("FS25_ADSmartPickup/adSources.lua") 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 -- ---- fakes ---- local wayPoints, markers = {}, {} local function addMarker(id, name, x, z) wayPoints[id] = {id = id, x = x, z = z} table.insert(markers, {id = id, markerIndex = #markers + 1, name = name, group = "g"}) end local adEnv = {ADGraphManager = { 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, }} local function storageOf(levels, capacities) return { fillLevels = levels, getFillLevel = function(_, ft) return levels[ft] or 0 end, getCapacity = function(_, ft) return capacities[ft] or 0 end, } end local sugarStorage = storageOf({[SUGAR] = 150000, [BASKET] = 0}, {[SUGAR] = 180000, [BASKET] = 8000}) local factory = { rootNode = {x = 100, z = 100}, getName = function() return "Sugar mill" end, spec_productionPoint = {productionPoint = { productions = {{outputs = {{type = SUGAR, amount = 1}}}, {outputs = {{type = BASKET, amount = 1}}}}, storage = sugarStorage, palletSpawner = {spawnPlaces = {{startNode = {x = 110, z = 100}}}}, }}, } local factoryStation = {owningPlaceable = factory, supportedFillTypes = {[SUGAR] = true}} local silo = {rootNode = {x = 200, z = 200}, spec_silo = {storages = {storageOf({[FORAGE] = 74500}, {[FORAGE] = 12000000})}}, getName = function() return "Farma" end} local siloStation = {owningPlaceable = silo} local spawnCalls = {} local objectStorage = { rootNode = {x = 300, z = 300}, ownerFarmId = 1, getName = function() return "Objektlager" end, spec_objectStorage = { objectInfos = { {objects = {{palletAttributes = {fillType = BASKET, fillLevel = 1000}}}, numObjects = 8}, {objects = {{baleAttributes = {fillType = FORAGE, fillLevel = 4000}}}, numObjects = 2}, }, numStoredObjects = 10, capacity = 4000, }, removeAbstractObjectsFromStorage = function(self, index, amount, connection) table.insert(spawnCalls, {index, amount, connection}) end, } local sellStation = {owningPlaceable = {rootNode = {x = 500, z = 500}}, isa = function(_, c) return c == SellingStation end, isSellingPoint = true, acceptedFillTypes = {[SUGAR] = true}, getName = function() return "Grain Elevator" end} local factoryUnload = {owningPlaceable = factory, isa = function() return true end, isSellingPoint = false} addMarker(1, "Suger Load", 100, 90) addMarker(2, "silo 2", 200, 190) addMarker(3, "Objektlager", 300, 280) -- 20 m fra lageret addMarker(4, "Langt væk", 300, 240) -- 60 m fra lageret addMarker(5, "selling station", 500, 490) addMarker(6, "sugar offload", 100, 80) local loadAt = {[1] = factoryStation, [2] = siloStation} local unloadAt = {[5] = sellStation, [6] = factoryUnload} ADSmartPickup = { getLoadStationAtWayPointId = function(_, id) return loadAt[id] end, getUnloadStationAtWayPointId = function(_, id) return unloadAt[id] end, getOwnStorageLevel = function(station, fillTypes) local total = 0 for _, storage in pairs(station.owningPlaceable.spec_silo.storages) do for _, ft in ipairs(fillTypes) do total = total + storage:getFillLevel(ft) end end return total end, } local pallets = {} local function addPallet(fillType, level, x, z, farmId) table.insert(pallets, {spec_pallet = {}, rootNode = {x = x, z = z}, getOwnerFarmId = function() return farmId or 1 end, getFillUnitFillType = function() return fillType end, getFillUnitFillLevel = function() return level end}) end g_currentMission = { placeableSystem = {placeables = {factory, silo, objectStorage}}, accessHandler = {canFarmAccess = function(_, farmId, placeable) return true end}, vehicleSystem = {vehicles = pallets}, } -- S1: fabrik local source1 = ADSources.identify(adEnv, 1, 1) check("S1 fabrik", source1.kind, "factory") check("S1 navn", source1.name, "Sugar mill") local products1 = ADSources.getProducts(adEnv, source1, 1) check("S1 to produkter", #products1, 2) check("S1 SUGAR available", products1[1].available, 150000) check("S1 SUGAR level", products1[1].level, 150000) check("S1 SUGAR capacity", products1[1].capacity, 180000) check("S1 SUGAR bulk", products1[1].isPallet, false) check("S1 BASKET er palle", products1[2].isPallet, true) check("S1 BASKET tomt", products1[2].available, 0) -- S2: silo local source2 = ADSources.identify(adEnv, 2, 1) check("S2 silo", source2.kind, "silo") local products2 = ADSources.getProducts(adEnv, source2, 1) check("S2 én vare", #products2, 1) check("S2 FORAGE", products2[1].fillType, FORAGE) check("S2 level", products2[1].level, 74500) check("S2 capacity", products2[1].capacity, 12000000) -- S3: objektlager 20 m fra markøren local source3 = ADSources.identify(adEnv, 3, 1) check("S3 objektlager", source3.kind, "objectStorage") local products3 = ADSources.getProducts(adEnv, source3, 1) check("S3 to varer", #products3, 2) check("S3 kurve available", products3[1].available, 8000) check("S3 kurve er palle", products3[1].isPallet, true) check("S3 level = antal objekter", products3[1].level, 10) check("S3 capacity", products3[1].capacity, 4000) check("S3 baller tæller som palle", products3[2].isPallet, true) local index, count = ADSources.getObjectInfoIndex(source3, BASKET) check("S3 objectInfo-indeks", index, 1) check("S3 antal", count, 8) check("S3 countStored", ADSources.countStored(source3, FORAGE), 2) check("S3 ukendt vare", ADSources.getObjectInfoIndex(source3, SUGAR), nil) -- S4: paller ved fabrikkens spawner tæller med (inden for 25 m af spawn-noden) addPallet(BASKET, 1000, 112, 100) addPallet(BASKET, 1000, 115, 105) addPallet(BASKET, 1000, 160, 100) -- 50 m væk: tæller ikke addPallet(BASKET, 1000, 112, 102, 2) -- anden farm: tæller ikke local products4 = ADSources.getProducts(adEnv, source1, 1) check("S4 paller ved spawner", products4[2].available, 2000) check("S4 bulk uændret", products4[1].available, 150000) -- S5: salgssteder = kun markører hvis aflæsningsstation er et rigtigt salgssted local sells = ADSources.getSellMarkers(adEnv, 1) check("S5 ét salgssted", #sells, 1) check("S5 markør", sells[1].marker.id, 5) check("S5 tager SUGAR", ADSources.stationAccepts(sells[1].station, SUGAR), true) check("S5 tager ikke FORAGE", ADSources.stationAccepts(sells[1].station, FORAGE), false) -- S6: spawnOne kalder spillets udtag med 1 objekt og uden klientforbindelse check("S6 spawn ok", ADSources.spawnOne(source3, 1), true) check("S6 indeks", spawnCalls[1][1], 1) check("S6 antal", spawnCalls[1][2], 1) check("S6 connection nil", spawnCalls[1][3], nil) check("S6 fejl -> false", ADSources.spawnOne({kind = "objectStorage", placeable = {removeAbstractObjectsFromStorage = function() error("x") end}}, 1), false) -- S7: markør uden station og >40 m fra objektlager -> nil check("S7 ingen kilde", ADSources.identify(adEnv, 4, 1), nil) check("S7 ukendt waypoint", ADSources.identify(adEnv, 99, 1), nil) -- S8: hjælpere check("S8 isPalletFillType palle", ADSources.isPalletFillType(BASKET), true) check("S8 isPalletFillType palletype uden fil", ADSources.isPalletFillType(SUGAR), true) check("S8 isPalletFillType bulk", ADSources.isPalletFillType(FORAGE), false) -- C1: palle/bulk afgøres pr. kilde: fabrik med læssestation der tager varen = bulk; silo = altid bulk; objektlager = palle check("S8 sukker ved fabrik med læssestation = bulk", ADSources.isPalletAt(source1, SUGAR), false) check("S8 kurve ved fabrik (station tager dem ikke) = palle", ADSources.isPalletAt(source1, BASKET), true) check("S8 silo altid bulk", ADSources.isPalletAt(source2, SUGAR), false) check("S8 objektlager altid palle", ADSources.isPalletAt(source3, FORAGE), true) -- S9 (I1): ren pallefabrik uden læssestation genkendes via afstand til spawner/rootNode local basketFactory = { rootNode = {x = 600, z = 600}, getName = function() return "Straw factory" end, spec_productionPoint = {productionPoint = {productions = {{outputs = {{type = BASKET, amount = 1}}}}, storage = storageOf({[BASKET] = 0}, {[BASKET] = 8000}), palletSpawner = {spawnPlaces = {{startNode = {x = 610, z = 600}}}}}}, } table.insert(g_currentMission.placeableSystem.placeables, basketFactory) addMarker(7, "Straw factory pickup", 605, 590) local source9 = ADSources.identify(adEnv, 7, 1) check("S9 pallefabrik uden station = factory", source9.kind, "factory") check("S9 station nil", source9.station, nil) local products9 = ADSources.getProducts(adEnv, source9, 1) check("S9 kurve er palle", products9[1].isPallet, true) check("S9 fabrik foretrækkes frem for objektlager i nærheden", ADSources.identify(adEnv, 7, 1).placeable, basketFactory) local excluded = ADSources.getSourcePlaceables({source1, source2}) check("S8 udelukkede placeables", excluded[factory] and excluded[silo], true) -- S10 (paller til lager): objektlager med plads der tager varen; nærmeste nåelige fra traktoren objectStorage.getObjectStorageSupportsFillType = function(self, ft) return ft == BASKET or ft == FORAGE end check("S10 tager kurve", ADSources.objectStorageAccepts(objectStorage, BASKET), true) check("S10 tager ikke sukker", ADSources.objectStorageAccepts(objectStorage, SUGAR), false) check("S10 fri pladser", ADSources.objectStorageFreeSlots(objectStorage), 3990) local fullShed = {rootNode = {x = 700, z = 700}, spec_objectStorage = {capacity = 10, numStoredObjects = 10, objectInfos = {}}, getName = function() return "Fuldt skur" end} table.insert(g_currentMission.placeableSystem.placeables, fullShed) addMarker(8, "Fuldt skur", 700, 690) check("S10 fuldt skur 0 pladser", ADSources.objectStorageFreeSlots(fullShed), 0) local truck = {rootNode = {x = 650, z = 650}} local store = ADSources.findPalletStore(adEnv, truck, BASKET, {}, function() return true end, 1) check("S10 nærmeste med plads = Objektlager (skuret er fuldt)", store.wayPointId, 3) check("S10 navn", store.name, "Objektlager") check("S10 udelukket kilde -> nil", ADSources.findPalletStore(adEnv, truck, BASKET, {[objectStorage] = true}, function() return true end, 1), nil) check("S10 uopnåelig -> nil", ADSources.findPalletStore(adEnv, truck, BASKET, {}, function() return false end, 1), nil) check("S10 sukker: intet skur tager det", ADSources.findPalletStore(adEnv, truck, SUGAR, {}, function() return true end, 1), nil) -- S11 (reserve): gårdens forbrug det næste døgn af en vare, minus hvad forbrugerne allerede har local BEET, STRAW, WATER = 9, 10, 11 DESC[BEET] = {index = BEET, name = "SUGARBEET", title = "Roer"}; DESC[STRAW] = {index = STRAW, name = "STRAW", title = "Halm"}; DESC[WATER] = {index = WATER, name = "WATER", title = "Vand"} local tmrStorage = storageOf({[BEET] = 20000, [FORAGE] = 500000}, {[BEET] = 2500000, [FORAGE] = 3500000}) local tmrFactory = { rootNode = {x = 800, z = 800}, ownerFarmId = 1, getOwnerFarmId = function() return 1 end, getName = function() return "TMR-fabrik" end, spec_productionPoint = {productionPoint = { productions = {{id = "tmr3", cyclesPerMinute = 200 / 60, inputs = {{type = BEET, amount = 45}}, outputs = {{type = FORAGE, amount = 240}}}}, storage = tmrStorage, getIsProductionEnabled = function(self, id) return id == "tmr3" end, }}, } local cowBarn = { rootNode = {x = 900, z = 900}, ownerFarmId = 1, getOwnerFarmId = function() return 1 end, getName = function() return "Kostald" end, spec_husbandryStraw = {inputLitersPerHour = 500}, spec_husbandryWater = {litersPerHour = 100, automaticWaterSupply = true}, getHusbandryIsFillTypeSupported = function(self, ft) return ft == STRAW or ft == WATER end, getHusbandryFillLevel = function(self, ft) return ft == STRAW and 2000 or 0 end, getHusbandryFreeCapacity = function(self, ft) return ft == STRAW and 19375 or 0 end, } local otherFarmBarn = {rootNode = {x = 950, z = 950}, ownerFarmId = 2, getOwnerFarmId = function() return 2 end, spec_husbandryStraw = {inputLitersPerHour = 500}, getHusbandryIsFillTypeSupported = function() return true end, getHusbandryFillLevel = function() return 0 end, getHusbandryFreeCapacity = function() return 99999 end} table.insert(g_currentMission.placeableSystem.placeables, tmrFactory) table.insert(g_currentMission.placeableSystem.placeables, cowBarn) table.insert(g_currentMission.placeableSystem.placeables, otherFarmBarn) local demand = ADSources.getFarmDemand(adEnv, BEET, 1, {}) -- 45 l × (200/60) pr. min × 1440 min = 216.000 l/døgn; har 20.000 -> 196.000 check("S11 roer: døgnforbrug minus beholdning", demand.liters, 196000) check("S11 forbruger-navn", demand.consumers[1], "TMR-fabrik") local strawDemand = ADSources.getFarmDemand(adEnv, STRAW, 1, {}) -- 500 l/t × 24 = 12.000; har 2.000 -> 10.000 (under fri plads 19.375) check("S11 halm: stald", strawDemand.liters, 10000) check("S11 anden farm tæller ikke", #strawDemand.consumers, 1) check("S11 automatisk vand = intet behov", ADSources.getFarmDemand(adEnv, WATER, 1, {}).liters, 0) check("S11 output er ikke behov", ADSources.getFarmDemand(adEnv, FORAGE, 1, {}).liters, 0) check("S11 udelukket forbruger", ADSources.getFarmDemand(adEnv, BEET, 1, {[tmrFactory] = true}).liters, 0) tmrStorage.fillLevels[BEET] = 300000 check("S11 nok på lager -> 0", ADSources.getFarmDemand(adEnv, BEET, 1, {}).liters, 0) tmrStorage.fillLevels[BEET] = 20000 tmrFactory.spec_productionPoint.productionPoint.getIsProductionEnabled = function() return false end check("S11 slukket produktion -> 0", ADSources.getFarmDemand(adEnv, BEET, 1, {}).liters, 0) -- S12 (inventar): alt gården har liggende, pr. bygning, kun bygninger med indhold factory.ownerFarmId = 1; silo.ownerFarmId = 1; objectStorage.ownerFarmId = 1 local inventory = ADSources.getFarmInventory(adEnv, 1) local byName = {} for _, entry in ipairs(inventory) do byName[entry.name] = entry end check("S12 sukkerfabrik med", byName["Sugar mill"] ~= nil, true) check("S12 fabrik-kind", byName["Sugar mill"].kind, "factory") check("S12 fabrik sukker", byName["Sugar mill"].entries[1].level, 150000) check("S12 fabrik sukker er output", byName["Sugar mill"].entries[1].isOutput, true) local beetEntry for _, e in ipairs(byName["TMR-fabrik"].entries) do if e.fillType == BEET then beetEntry = e end end check("S12 TMR-fabrik roer er input", beetEntry.isOutput, false) check("S12 outputs først", byName["TMR-fabrik"].entries[1].isOutput, true) check("S12 silo", byName["Farma"].entries[1].fillType, FORAGE) check("S12 objektlager kurve som antal", byName["Objektlager"].entries[1].count, 8) check("S12 stald halm", byName["Kostald"].entries[1].level, 2000) check("S12 stald kapacitet = niveau + fri", byName["Kostald"].entries[1].capacity, 21375) check("S12 tomt skur udeladt", byName["Fuldt skur"], nil) check("S12 anden farm udeladt", #inventory, 5) print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)