Files
fs25-adsmartpickup/FS25_ADSmartPickup/adSources.lua
T

1089 lines
53 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- AD Sources
-- Kilder til udkørsel: hvad står der ved en AD-markør (fabrik, silo eller objektlager),
-- hvilke varer rummer/producerer den, hvor meget, og hvordan tages paller ud af et
-- objektlager. Spil-adapter — de rene beslutninger ligger i adOutboundPlanner.
ADSources = {}
-- ADCache-opslag der også virker uden adCache (tests)
local function cacheGet(bucket, key, ttlMs, fn)
if ADCacheGet == nil then return fn() end
return ADCacheGet(bucket, key, ttlMs, fn)
end
ADSources.LOG_PREFIX = "ADSmartPickup: "
-- Objektlager tættere end dette på markøren regnes som markørens (samme som triggere).
ADSources.MAX_DISTANCE = 40
-- Paller der står inden for denne afstand af fabrikkens pallespawner tæller som fabrikkens output.
ADSources.PALLET_RADIUS = 25
-- Staldens egne pallespawnere (hønsehus: æg, fårestald: uld): {[fillType] = spawner}.
local function getHusbandryPalletSpawners(placeable)
local spec = placeable ~= nil and placeable.spec_husbandryPallets or nil
return spec ~= nil and spec.fillTypeIndexToPalletSpawner or nil
end
local function firstSpawnerNode(spawner)
return spawner ~= nil and spawner.spawnPlaces ~= nil and spawner.spawnPlaces[1] ~= nil and spawner.spawnPlaces[1].startNode or nil
end
local function distance2(ax, az, bx, bz)
local dx, dz = ax - bx, az - bz
return dx * dx + dz * dz
end
local function getNodePosition(node)
if node == nil or (entityExists ~= nil and not entityExists(node)) then
return nil
end
local x, _, z = getWorldTranslation(node)
return x, z
end
local function getPlaceableName(placeable, fallback)
if placeable ~= nil and placeable.getName ~= nil then
local ok, name = pcall(placeable.getName, placeable)
if ok and name ~= nil then
return tostring(name)
end
end
return fallback
end
local function getProductionPoint(placeable)
return placeable ~= nil and placeable.spec_productionPoint ~= nil and placeable.spec_productionPoint.productionPoint or nil
end
-- Varer der KAN stå på paller (spillets fillType-definition: isPalletType eller pallefil).
-- Sukker og mel er palletyper, men læsses som bulk ved en fabrik med læssestation — så om
-- varen er palle eller bulk afgøres pr. kilde i isPalletAt, ikke her.
function ADSources.isPalletFillType(fillType)
local desc = g_fillTypeManager ~= nil and g_fillTypeManager.getFillTypeByIndex ~= nil and g_fillTypeManager:getFillTypeByIndex(fillType) or nil
return desc ~= nil and (desc.isPalletType == true or desc.palletFilename ~= nil)
end
local function stationSupports(station, fillType)
if station == nil then
return false
end
if station.supportedFillTypes ~= nil then
return station.supportedFillTypes[fillType] == true
end
if station.getIsFillTypeSupported ~= nil then
local ok, value = pcall(station.getIsFillTypeSupported, station, fillType)
return ok and value == true
end
return false
end
-- Palle eller bulk ved DENNE kilde: silo = bulk; objektlager = palle; fabrik = bulk hvis
-- markørens læssestation udleverer varen, ellers palle hvis varen er en palletype.
function ADSources.isPalletAt(source, fillType)
if source.kind == "husbandry" then
local spawners = getHusbandryPalletSpawners(source.placeable)
return spawners ~= nil and spawners[fillType] ~= nil
elseif source.kind == "silo" then
return false
elseif source.kind == "objectStorage" then
return true
end
local productionPoint = getProductionPoint(source.placeable)
local loadingStation = productionPoint ~= nil and productionPoint.loadingStation or nil
if stationSupports(source.station, fillType) or stationSupports(loadingStation, fillType) then
return false
end
return ADSources.isPalletFillType(fillType)
end
-- Placeable'ns "nærmeste punkt" for kildegenkendelse: pallespawnerens første plads (fabrik,
-- stald med paller) eller rodnoden.
local function getPlaceableAnchor(placeable)
local productionPoint = getProductionPoint(placeable)
local spawner = productionPoint ~= nil and productionPoint.palletSpawner or nil
if spawner == nil then
for _, husbandrySpawner in pairs(getHusbandryPalletSpawners(placeable) or {}) do spawner = husbandrySpawner break end
end
local place = spawner ~= nil and spawner.spawnPlaces ~= nil and spawner.spawnPlaces[1] or nil
local node = place ~= nil and place.startNode or placeable.rootNode
local x, z = getNodePosition(node)
if x == nil and node ~= placeable.rootNode then
x, z = getNodePosition(placeable.rootNode)
end
return x, z
end
-- Nærmeste placeable inden for MAX_DISTANCE af waypointet som farmen må bruge og predicate godkender.
local function findPlaceableNear(wayPoint, farmId, predicate)
local placeables = g_currentMission ~= nil and g_currentMission.placeableSystem ~= nil and g_currentMission.placeableSystem.placeables or {}
local access = g_currentMission ~= nil and g_currentMission.accessHandler or nil
local best, bestDistance = nil, ADSources.MAX_DISTANCE * ADSources.MAX_DISTANCE
for _, placeable in pairs(placeables) do
if predicate(placeable) and (access == nil or access:canFarmAccess(farmId, placeable)) then
local x, z = getPlaceableAnchor(placeable)
if x ~= nil then
local d = distance2(wayPoint.x, wayPoint.z, x, z)
if d < bestDistance then
best, bestDistance = placeable, d
end
end
end
end
return best
end
local function isObjectStorage(placeable)
return placeable.spec_objectStorage ~= nil
end
local function isProductionPoint(placeable)
return getProductionPoint(placeable) ~= nil
end
-- {kind = "factory"|"silo"|"objectStorage", placeable, station|nil, wayPointId, name} eller nil.
function ADSources.identify(adEnv, wayPointId, farmId)
return cacheGet("identify", tostring(wayPointId) .. ":" .. tostring(farmId), 10000, function()
return ADSources.identifyUncached(adEnv, wayPointId, farmId)
end)
end
function ADSources.identifyUncached(adEnv, wayPointId, farmId)
local wayPoint = adEnv.ADGraphManager:getWayPointById(wayPointId)
if wayPoint == nil then
return nil
end
local marker = adEnv.ADGraphManager.getMapMarkerByWayPointId ~= nil and adEnv.ADGraphManager:getMapMarkerByWayPointId(wayPointId) or nil
-- Wait-punkter er ventepladser, aldrig kilder eller lagermål — også når de står tæt på et lager
if marker ~= nil and ADSupplyPlanner ~= nil and ADSupplyPlanner.isWaitMarker ~= nil and ADSupplyPlanner.isWaitMarker(marker) then
return nil
end
local markerName = marker ~= nil and tostring(marker.name) or tostring(wayPointId)
local station = ADSmartPickup.getLoadStationAtWayPointId(adEnv, wayPointId, farmId)
local placeable = station ~= nil and station.owningPlaceable or nil
if placeable ~= nil and getProductionPoint(placeable) ~= nil then
return {kind = "factory", placeable = placeable, station = station, wayPointId = wayPointId, name = getPlaceableName(placeable, markerName)}
end
if placeable ~= nil and placeable.spec_silo ~= nil then
return {kind = "silo", placeable = placeable, station = station, wayPointId = wayPointId, name = getPlaceableName(placeable, markerName)}
end
-- stald: mælketank, gyllebeholder m.m. (læssestation på en husbandry-placeable)
if placeable ~= nil and placeable.getHusbandryFillLevel ~= nil then
return {kind = "husbandry", placeable = placeable, station = station, wayPointId = wayPointId, name = getPlaceableName(placeable, markerName)}
end
-- generelt lager: anden bygning med læssestation (fx separatorhal med digestat); varer fra stationen
if placeable ~= nil and placeable.spec_objectStorage == nil then
return {kind = "storage", placeable = placeable, station = station, wayPointId = wayPointId, name = getPlaceableName(placeable, markerName)}
end
-- Det strenge opslag (trigger bag næsen) passer til silorækker; en mælketank eller fabrik ligger
-- ofte ved siden af markøren. Løst opslag inden for 40 m — men aldrig for siloer.
if placeable == nil and ADSmartPickup.getLoadStationNearWayPointId ~= nil then
local loose = ADSmartPickup.getLoadStationNearWayPointId(adEnv, wayPointId, farmId, ADSources.MAX_DISTANCE)
local loosePlaceable = loose ~= nil and loose.owningPlaceable or nil
if loosePlaceable ~= nil and loosePlaceable.spec_silo == nil then
if getProductionPoint(loosePlaceable) ~= nil then
return {kind = "factory", placeable = loosePlaceable, station = loose, wayPointId = wayPointId, name = getPlaceableName(loosePlaceable, markerName)}
elseif loosePlaceable.getHusbandryFillLevel ~= nil then
return {kind = "husbandry", placeable = loosePlaceable, station = loose, wayPointId = wayPointId, name = getPlaceableName(loosePlaceable, markerName)}
end
end
end
-- stald med egne pallespawnere (hønsehus: æg) — markøren står ved spawneren, ingen læssestation
local palletBarn = findPlaceableNear(wayPoint, farmId, function(placeable) return placeable.getHusbandryFillLevel ~= nil and getHusbandryPalletSpawners(placeable) ~= nil end)
if palletBarn ~= nil then
return {kind = "husbandry", placeable = palletBarn, station = nil, wayPointId = wayPointId, name = getPlaceableName(palletBarn, markerName)}
end
-- Rene pallefabrikker (halmvarer, bageri ...) har ingen læssestation: find dem på afstand
-- til pallespawneren; dernæst objektlagre.
local factory = findPlaceableNear(wayPoint, farmId, isProductionPoint)
if factory ~= nil then
-- produktionspunktets egen læssestation (bulk-outputs), også når markøren ikke rammer triggeren
local loadingStation = getProductionPoint(factory).loadingStation
return {kind = "factory", placeable = factory, station = loadingStation, wayPointId = wayPointId, name = getPlaceableName(factory, markerName)}
end
local storage = findPlaceableNear(wayPoint, farmId, isObjectStorage)
if storage ~= nil then
return {kind = "objectStorage", placeable = storage, station = nil, wayPointId = wayPointId, name = getPlaceableName(storage, markerName)}
end
return nil
end
-- Til advarslen "kilde-markør X er ikke …": hvad opslaget faktisk ser ved markøren.
function ADSources.explainMarker(adEnv, wayPointId, farmId)
local wayPoint = adEnv.ADGraphManager:getWayPointById(wayPointId)
if wayPoint == nil then
return "markøren har intet waypoint (slettet?)"
end
local station = ADSmartPickup.getLoadStationAtWayPointId(adEnv, wayPointId, farmId)
if station == nil then
local loose = ADSmartPickup.getLoadStationNearWayPointId ~= nil and ADSmartPickup.getLoadStationNearWayPointId(adEnv, wayPointId, farmId, ADSources.MAX_DISTANCE) or nil
if loose ~= nil and loose.owningPlaceable ~= nil and loose.owningPlaceable.spec_silo ~= nil then
return string.format("nærmeste læssestation inden for 40 m er en silo ('%s'), som kræver at markøren står i køresporet med triggeren bag næsen", getPlaceableName(loose.owningPlaceable, "?"))
end
local factory = findPlaceableNear(wayPoint, farmId, isProductionPoint)
local storage = findPlaceableNear(wayPoint, farmId, isObjectStorage)
if factory == nil and storage == nil then
return "ingen læssestation ved markøren, og ingen fabrik/objektlager inden for 40 m"
end
return string.format("ingen læssestation ved markøren; nærmeste bygning: %s", getPlaceableName(factory or storage, "?"))
end
local placeable = station.owningPlaceable
if placeable == nil then
return "læssestation ved markøren uden bygning (owningPlaceable = nil)"
end
local kind = "ukendt bygningstype"
if getProductionPoint(placeable) ~= nil then kind = "fabrik"
elseif placeable.spec_silo ~= nil then kind = "silo"
elseif placeable.getHusbandryFillLevel ~= nil then kind = "stald"
elseif placeable.spec_objectStorage ~= nil then kind = "objektlager" end
return string.format("læssestation ved markøren hører til '%s' (%s)", getPlaceableName(placeable, "?"), kind)
end
local function isPalletVehicle(vehicle)
return vehicle ~= nil and (vehicle.spec_pallet ~= nil or vehicle.isPallet == true)
end
local function getPalletContent(vehicle)
if vehicle.getFillUnitFillType ~= nil and vehicle.getFillUnitFillLevel ~= nil then
return vehicle:getFillUnitFillType(1), vehicle:getFillUnitFillLevel(1) or 0
end
local units = vehicle.spec_fillUnit ~= nil and vehicle.spec_fillUnit.fillUnits or nil
if units ~= nil and units[1] ~= nil then
return units[1].fillType, units[1].fillLevel or 0
end
return nil, 0
end
-- Liter af varen på paller der står ved fabrikkens spawner (pallespawneren tømmer lageret
-- ud på jorden løbende, så det er dér varen fysisk er).
-- Alle andre pallespawnere der kan lave varen (stalde med spawner for fillType, fabrikkers
-- pallespawner): {{x, z}, ...}. Står bygningerne tættere end PALLET_RADIUS (hønsehuse på række),
-- tilhører en palle den nærmeste spawner — ellers tæller et tomt hus naboernes paller med.
local function findRivalSpawners(fillType)
local rivals = {}
local placeables = g_currentMission ~= nil and g_currentMission.placeableSystem ~= nil and g_currentMission.placeableSystem.placeables or {}
for _, placeable in pairs(placeables) do
local husbandrySpawners = getHusbandryPalletSpawners(placeable)
local productionPoint = getProductionPoint(placeable)
local node = firstSpawnerNode(husbandrySpawners ~= nil and husbandrySpawners[fillType] or nil)
or firstSpawnerNode(productionPoint ~= nil and productionPoint.palletSpawner or nil)
local x, z = getNodePosition(node)
if x ~= nil then table.insert(rivals, {x = x, z = z}) end
end
return rivals
end
local function isNearestSpawner(rivals, sx, sz, x, z)
local own = distance2(sx, sz, x, z)
for _, rival in ipairs(rivals) do
if distance2(rival.x, rival.z, x, z) < own then return false end
end
return true
end
local function getPalletsNearSpawner(placeable, fillType, farmId, spawnerNode)
local productionPoint = getProductionPoint(placeable)
local spawner = productionPoint ~= nil and productionPoint.palletSpawner or nil
local node = spawnerNode or (spawner ~= nil and spawner.spawnPlaces ~= nil and spawner.spawnPlaces[1] ~= nil and spawner.spawnPlaces[1].startNode) or placeable.rootNode
local sx, sz = getNodePosition(node)
if sx == nil then
return 0
end
local rivals = cacheGet("palletSpawners", fillType, 10000, function() return findRivalSpawners(fillType) end)
local vehicles = g_currentMission ~= nil and g_currentMission.vehicleSystem ~= nil and g_currentMission.vehicleSystem.vehicles or {}
local total, count = 0, 0
local radius2 = ADSources.PALLET_RADIUS * ADSources.PALLET_RADIUS
for _, vehicle in pairs(vehicles) do
if isPalletVehicle(vehicle) and (vehicle.getOwnerFarmId == nil or vehicle:getOwnerFarmId() == farmId) then
local palletFillType, level = getPalletContent(vehicle)
local x, z = getNodePosition(vehicle.rootNode)
if palletFillType == fillType and x ~= nil and distance2(sx, sz, x, z) <= radius2 and isNearestSpawner(rivals, sx, sz, x, z) then
total = total + level
count = count + 1
end
end
end
return total, count
end
-- canPallet: varen er en palletype (kan stå på paller); palletLiters: liter på paller ved
-- kilden når varen ELLERS læsses som bulk (fabrik med både læssestation og pallespawner).
local function newProduct(fillType, available, level, capacity, isPallet)
local name = g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(fillType) or tostring(fillType)
local title = g_fillTypeManager ~= nil and g_fillTypeManager.getFillTypeTitleByIndex ~= nil and g_fillTypeManager:getFillTypeTitleByIndex(fillType) or nil
return {fillType = fillType, name = name, title = title or name, available = available, level = level, capacity = capacity, isPallet = isPallet, canPallet = isPallet or ADSources.isPalletFillType(fillType), palletLiters = 0, palletCount = 0}
end
local function getFactoryProducts(source, farmId)
local productionPoint = getProductionPoint(source.placeable)
local storage = productionPoint ~= nil and productionPoint.storage or nil
local seen, products = {}, {}
for _, production in ipairs(productionPoint ~= nil and productionPoint.productions or {}) do
for _, output in ipairs(production.outputs or {}) do
local fillType = output.type
if fillType ~= nil and not seen[fillType] then
seen[fillType] = true
local level = storage ~= nil and storage.getFillLevel ~= nil and (storage:getFillLevel(fillType) or 0) or 0
local capacity = storage ~= nil and storage.getCapacity ~= nil and (storage:getCapacity(fillType) or 0) or 0
local isPallet = ADSources.isPalletAt(source, fillType)
local palletLiters, palletCount = 0, 0
if ADSources.isPalletFillType(fillType) then
palletLiters, palletCount = getPalletsNearSpawner(source.placeable, fillType, farmId)
end
local available = level + (isPallet and palletLiters or 0)
local product = newProduct(fillType, available, level, capacity, isPallet)
product.palletLiters, product.palletCount = palletLiters, palletCount
table.insert(products, product)
end
end
end
return products
end
local function getSiloProducts(source)
local storages = source.placeable.spec_silo ~= nil and source.placeable.spec_silo.storages or {}
local levels, capacities = {}, {}
for _, storage in pairs(storages) do
for fillType, level in pairs(storage.fillLevels or {}) do
if (level or 0) > 0 then
levels[fillType] = (levels[fillType] or 0) + level
capacities[fillType] = (capacities[fillType] or 0) + (storage.getCapacity ~= nil and (storage:getCapacity(fillType) or 0) or 0)
end
end
end
-- Udvidelses-lagre (fx mælketanke med isExtension) deles af alle læssestationer inden for
-- storageRadius: tanken selv kan være tom mens puljen er fuld. Læssestationens tal er det man kan hente.
local station = source.station
if station ~= nil and station.getAllFillLevels ~= nil then
local farmId = g_currentMission ~= nil and g_currentMission.getFarmId ~= nil and g_currentMission:getFarmId() or 1
local ok, pooled, pooledCapacity = pcall(station.getAllFillLevels, station, farmId)
if ok and type(pooled) == "table" then
for fillType, level in pairs(pooled) do
if (level or 0) > (levels[fillType] or 0) then
levels[fillType] = level
capacities[fillType] = math.max(capacities[fillType] or 0, tonumber(pooledCapacity) or 0, level)
end
end
end
end
local products = {}
for fillType, level in pairs(levels) do
table.insert(products, newProduct(fillType, level, level, capacities[fillType] or 0, false))
end
table.sort(products, function(a, b) return a.fillType < b.fillType end)
return products
end
-- Et objektlagers post: {fillType, fillLevel pr. objekt} for paller og baller.
local function getObjectInfoContent(objectInfo)
local object = objectInfo.objects ~= nil and objectInfo.objects[1] or nil
if object == nil then
return nil, 0
end
local attributes = object.palletAttributes or object.baleAttributes
if attributes ~= nil then
return attributes.fillType, attributes.fillLevel or 0
end
return object.fillType, object.fillLevel or 0
end
local function getObjectStorageProducts(source)
local spec = source.placeable.spec_objectStorage
local products = {}
for _, objectInfo in ipairs(spec ~= nil and spec.objectInfos or {}) do
local fillType, perObject = getObjectInfoContent(objectInfo)
local count = objectInfo.numObjects or 0
if fillType ~= nil and count > 0 then
local product = newProduct(fillType, count * perObject, spec.numStoredObjects or count, spec.capacity or 0, true)
product.palletCount = count
table.insert(products, product)
end
end
return products
end
-- Stald: det læssestationen udleverer (mælk, gylle ...) med staldens eget niveau/kapacitet.
-- Varerne det læssepunkt udleverer som står nærmest markøren (en stald kan have mælk i den ene ende
-- og gylle i den anden på samme station). nil når punktet ikke opgiver sine varer.
local function getNearestTriggerFillTypes(adEnv, source)
local station = source.station
local wayPoint = adEnv.ADGraphManager:getWayPointById(source.wayPointId)
if station == nil or wayPoint == nil or station.loadTriggers == nil then
return nil
end
local best, bestDistance = nil, math.huge
for _, trigger in pairs(station.loadTriggers) do
local x, z = getNodePosition(trigger.triggerNode)
if x ~= nil then
local d = distance2(wayPoint.x, wayPoint.z, x, z)
if d < bestDistance then best, bestDistance = trigger, d end
end
end
if best == nil or type(best.fillTypes) ~= "table" or next(best.fillTypes) == nil then
return nil
end
local list = {}
for key, value in pairs(best.fillTypes) do
-- FS25 gemmer som sæt {[ft] = true}; nogle mods som liste {ft, ...}
if value == true then table.insert(list, key) elseif type(value) == "number" then table.insert(list, value) end
end
return #list > 0 and list or nil
end
local function getHusbandryPalletProducts(source, farmId)
local products = {}
local spec = source.placeable.spec_husbandryPallets
for fillType, spawner in pairs(getHusbandryPalletSpawners(source.placeable) or {}) do
local liters, count = getPalletsNearSpawner(source.placeable, fillType, farmId, firstSpawnerNode(spawner))
local capacity = spec ~= nil and spec.capacities ~= nil and spec.capacities[fillType] or 0
local product = newProduct(fillType, liters, liters, math.max(capacity or 0, liters), true)
product.palletLiters, product.palletCount = liters, count
table.insert(products, product)
end
table.sort(products, function(a, b) return a.fillType < b.fillType end)
return products
end
local function getHusbandryProducts(adEnv, source)
local placeable, station = source.placeable, source.station
local farmId = g_currentMission ~= nil and g_currentMission.getFarmId ~= nil and g_currentMission:getFarmId() or 1
if station == nil then
-- kilde via staldens pallespawner (hønsehus: æg): kun pallevarerne
return getHusbandryPalletProducts(source, farmId)
end
local fillTypes = getNearestTriggerFillTypes(adEnv, source) or {}
if #fillTypes == 0 then
for fillType, supported in pairs(station ~= nil and station.supportedFillTypes or {}) do
if supported == true then table.insert(fillTypes, fillType) end
end
end
if #fillTypes == 0 and station ~= nil and station.getIsFillTypeSupported ~= nil and g_fillTypeManager ~= nil then
for _, name in ipairs({"MILK", "GOATMILK", "BUFFALOMILK", "SHEEPMILK", "LIQUIDMANURE", "MANURE", "DIGESTATE"}) do
local fillType = g_fillTypeManager.getFillTypeIndexByName ~= nil and g_fillTypeManager:getFillTypeIndexByName(name) or nil
if fillType ~= nil then
local ok, value = pcall(station.getIsFillTypeSupported, station, fillType)
if ok and value == true then table.insert(fillTypes, fillType) end
end
end
end
table.sort(fillTypes)
-- Læssestationen trækker fra staldens beholder OG tilknyttede udvidelser (fx Milk Tank Extension,
-- en siloExtension inden for storageRadius). getHusbandryFillLevel ser kun staldens egen beholder.
local stationLevels = nil
if station ~= nil and station.getAllFillLevels ~= nil then
local ok, levels = pcall(station.getAllFillLevels, station, g_currentMission ~= nil and g_currentMission.getFarmId ~= nil and g_currentMission:getFarmId() or 1)
if ok and type(levels) == "table" then stationLevels = levels end
end
local products = {}
for _, fillType in ipairs(fillTypes) do
local level = math.max(placeable:getHusbandryFillLevel(fillType) or 0, stationLevels ~= nil and stationLevels[fillType] or 0)
local capacity = placeable.getHusbandryCapacity ~= nil and (placeable:getHusbandryCapacity(fillType) or 0) or 0
if capacity <= 0 and placeable.getHusbandryFreeCapacity ~= nil then
capacity = level + (placeable:getHusbandryFreeCapacity(fillType) or 0)
end
table.insert(products, newProduct(fillType, level, level, math.max(capacity, level), false))
end
return products
end
-- Generelt lager: det læssestationen har af de varer den udleverer (supportedFillTypes/triggeres fillTypes;
-- ukendt = alt med niveau). Kapacitet = stationens samlede.
local function getStationProducts(source)
local station = source.station
if station == nil or station.getAllFillLevels == nil then
return {}
end
local farmId = g_currentMission ~= nil and g_currentMission.getFarmId ~= nil and g_currentMission:getFarmId() or 1
local ok, levels, capacity = pcall(station.getAllFillLevels, station, farmId)
if not ok or type(levels) ~= "table" then
return {}
end
local supported = station.supportedFillTypes
local products = {}
for fillType, level in pairs(levels) do
local isSupported = supported == nil or next(supported) == nil or supported[fillType] == true
if isSupported and (level or 0) > 0 then
table.insert(products, newProduct(fillType, level, level, math.max(tonumber(capacity) or 0, level), false))
end
end
table.sort(products, function(a, b) return a.fillType < b.fillType end)
return products
end
-- Varer ved kilden med tilgængelig mængde: { {fillType, name, title, available, level, capacity, isPallet} }.
function ADSources.getProducts(adEnv, source, farmId)
if source.kind == "factory" then
return getFactoryProducts(source, farmId)
elseif source.kind == "silo" then
return getSiloProducts(source)
elseif source.kind == "husbandry" then
return getHusbandryProducts(adEnv, source)
elseif source.kind == "storage" then
return getStationProducts(source)
elseif source.kind == "objectStorage" then
return getObjectStorageProducts(source)
end
return {}
end
-- Objektlager: objectInfo-indeks + antal for varen; nil når lageret ikke har den.
function ADSources.getObjectInfoIndex(source, fillType)
local spec = source.placeable ~= nil and source.placeable.spec_objectStorage or nil
for index, objectInfo in ipairs(spec ~= nil and spec.objectInfos or {}) do
local infoFillType = getObjectInfoContent(objectInfo)
if infoFillType == fillType and (objectInfo.numObjects or 0) > 0 then
return index, objectInfo.numObjects
end
end
return nil
end
function ADSources.countStored(source, fillType)
local _, count = ADSources.getObjectInfoIndex(source, fillType)
return count or 0
end
-- Bed lageret spawne ÉT objekt (det knappen i spillet gør). connection = nil, så
-- AutomaticBaleStorage-moddens kø (der kun fanger klient-kald) ikke blander sig.
function ADSources.spawnOne(source, objectInfoIndex)
local placeable = source.placeable
if placeable == nil or placeable.removeAbstractObjectsFromStorage == nil then
return false
end
local ok = pcall(placeable.removeAbstractObjectsFromStorage, placeable, objectInfoIndex, 1, nil)
return ok == true
end
-- acceptedFillTypes rummer kun bulk-triggerens varer; palletriggerens varer ligger i
-- supportedFillTypes (det AutoDrive selv slår op i). Begge tæller.
function ADSources.stationAccepts(station, fillType)
if station.acceptedFillTypes ~= nil and station.acceptedFillTypes[fillType] == true then
return true
end
if station.supportedFillTypes ~= nil and station.supportedFillTypes[fillType] == true then
return true
end
if station.getIsFillTypeSupported ~= nil then
local ok, value = pcall(station.getIsFillTypeSupported, station, fillType)
return ok and value == true
end
return false
end
-- Til loggen: navnene på de varer stationen tager (sorteret), fx "SUGAR, STRAW_BASKET".
function ADSources.describeAccepted(station)
local set = {}
for _, field in ipairs({"acceptedFillTypes", "supportedFillTypes"}) do
for fillType, accepted in pairs(station[field] or {}) do
if accepted == true then set[fillType] = true end
end
end
local names = {}
for fillType in pairs(set) do
table.insert(names, g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(fillType) or tostring(fillType))
end
table.sort(names, function(a, b)
-- stabil rækkefølge efter indeks hvor det kendes, ellers navn
return tostring(a) < tostring(b)
end)
return table.concat(names, ", ")
end
-- Markører hvis aflæsningsstation er et rigtigt salgssted: { {marker, station} }.
function ADSources.getSellMarkers(adEnv, farmId)
return cacheGet("sellMarkers", tostring(farmId), 10000, function() return ADSources.findSellMarkers(adEnv, farmId) end)
end
function ADSources.findSellMarkers(adEnv, farmId)
local result = {}
for _, marker in pairs(adEnv.ADGraphManager:getMapMarkers()) do
local station = ADSmartPickup.getUnloadStationAtWayPointId(adEnv, marker.id, farmId)
if station ~= nil and ADPrices.isSellingStation(station) then
table.insert(result, {marker = marker, station = station})
end
end
return result
end
-- ---- paller til lager: objektlager (palleskur) som mål ----
function ADSources.objectStorageAccepts(placeable, fillType)
local spec = placeable ~= nil and placeable.spec_objectStorage or nil
if spec == nil or spec.supportsPallets == false then
return false
end
if placeable.getObjectStorageSupportsFillType ~= nil then
local ok, value = pcall(placeable.getObjectStorageSupportsFillType, placeable, fillType)
return ok and value == true
end
return true
end
-- Ledige pladser (objekter); uendeligt når lageret ingen kapacitet opgiver.
function ADSources.objectStorageFreeSlots(placeable)
local spec = placeable ~= nil and placeable.spec_objectStorage or nil
if spec == nil then
return 0
end
local capacity = spec.capacity or 0
if capacity <= 0 then
return math.huge
end
local stored = spec.storedObjects ~= nil and #spec.storedObjects or (spec.numStoredObjects or 0)
return math.max(0, capacity - stored)
end
-- Nærmeste markør ved et objektlager der tager varen og har plads, med rute fra traktoren;
-- kilderne selv udelukkes. {wayPointId, name, free = pladser} eller nil.
function ADSources.findPalletStore(adEnv, vehicle, fillType, excludedPlaceables, isReachable, farmId)
local x, z = getNodePosition(vehicle.rootNode)
if x == nil then
return nil
end
local candidates = {}
for _, marker in pairs(adEnv.ADGraphManager:getMapMarkers()) do
local source = ADSources.identify(adEnv, marker.id, farmId)
if source ~= nil and source.kind == "objectStorage" and not (excludedPlaceables ~= nil and excludedPlaceables[source.placeable])
and ADSources.objectStorageAccepts(source.placeable, fillType) and ADSources.objectStorageFreeSlots(source.placeable) >= 1 then
local wayPoint = adEnv.ADGraphManager:getWayPointById(marker.id)
table.insert(candidates, {marker = marker, source = source, distance = distance2(x, z, wayPoint.x, wayPoint.z)})
end
end
table.sort(candidates, function(a, b) return a.distance < b.distance end)
for _, candidate in ipairs(candidates) do
if isReachable == nil or isReachable(candidate.marker.id) then
return {wayPointId = candidate.marker.id, name = tostring(candidate.marker.name), free = ADSources.objectStorageFreeSlots(candidate.source.placeable)}
end
end
return nil
end
-- ---- reserve: hvad gårdens egne fabrikker og stalde skal bruge det næste døgn ----
ADSources.RESERVE_HOURS = 24
-- Fælles ejerregel (ADPrices): egen ejer eller jord-bundet kortanlæg på gårdens jord.
local function isOwnedBy(placeable, farmId)
if ADPrices ~= nil and ADPrices.isOwnedBy ~= nil then
return ADPrices.isOwnedBy(placeable, farmId)
end
if placeable.getOwnerFarmId ~= nil then
local ok, owner = pcall(placeable.getOwnerFarmId, placeable)
return ok and owner == farmId
end
return placeable.ownerFarmId == farmId
end
local function isProductionWanted(productionPoint, production)
local isEnabled = false
if productionPoint.getIsProductionEnabled ~= nil then
isEnabled = productionPoint:getIsProductionEnabled(production.id) == true
else
for _, active in ipairs(productionPoint.activeProductions or {}) do
if active == production then isEnabled = true end
end
end
local missingInputs = ProductionPoint ~= nil and ProductionPoint.PROD_STATUS ~= nil and ProductionPoint.PROD_STATUS.MISSING_INPUTS or nil
return isEnabled or (missingInputs ~= nil and production.status == missingInputs)
end
-- Fabrik: døgnforbrug af varen i ønskede produktioner minus det den har, højst fri plads.
local function getProductionReserve(placeable, fillType)
local productionPoint = getProductionPoint(placeable)
if productionPoint == nil or productionPoint.storage == nil then
return 0
end
local perMinute = 0
for _, production in ipairs(productionPoint.productions or {}) do
if isProductionWanted(productionPoint, production) then
for _, input in ipairs(production.inputs or {}) do
if input.type == fillType then
local cyclesPerMinute = production.cyclesPerMinute or ((production.cyclesPerHour or 0) / 60)
perMinute = perMinute + (input.amount or 0) * cyclesPerMinute
end
end
end
end
if perMinute <= 0 then
return 0
end
local storage = productionPoint.storage
local level = storage.getFillLevel ~= nil and (storage:getFillLevel(fillType) or 0) or ((storage.fillLevels or {})[fillType] or 0)
local free = storage.getFreeCapacity ~= nil and storage:getFreeCapacity(fillType) or nil
if free == nil then
local capacity = storage.getCapacity ~= nil and (storage:getCapacity(fillType) or 0) or 0
free = math.max(0, capacity - level)
end
local need = perMinute * 60 * ADSources.RESERVE_HOURS - level
return math.max(0, math.min(need, free))
end
-- Stald: foder/halm/vand-rate × timer minus beholdning, højst fri plads (spec-felter som
-- Distribution Redux læser dem); automatisk vandforsyning = intet behov.
-- Foder = varer i krybben ELLER i dyretypens foderdefinition (tom krybbe har ingen niveauer).
-- Mælk/gylle er staldens output og understøttes kun som lager.
local function isFoodFillType(placeable, fillType)
local food = placeable.spec_husbandryFood
if food == nil then return false end
if food.fillLevels ~= nil and food.fillLevels[fillType] ~= nil then return true end
local system = g_currentMission ~= nil and g_currentMission.animalFoodSystem or nil
local animalFood = system ~= nil and system.getAnimalFood ~= nil and system:getAnimalFood(food.animalTypeIndex) or nil
for _, group in pairs(animalFood ~= nil and animalFood.groups or {}) do
for _, candidate in pairs(group.fillTypes or {}) do
if candidate == fillType then return true end
end
end
return false
end
local function getHusbandryReserve(placeable, fillType)
if placeable.getHusbandryIsFillTypeSupported == nil or placeable.getHusbandryFillLevel == nil then
return 0
end
local okSupported, supported = pcall(placeable.getHusbandryIsFillTypeSupported, placeable, fillType)
if not okSupported or supported ~= true then
return 0
end
local name = g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(fillType) or ""
local rate, level = 0, placeable:getHusbandryFillLevel(fillType) or 0
local food, straw, water = placeable.spec_husbandryFood, placeable.spec_husbandryStraw, placeable.spec_husbandryWater
if name == "WATER" then
if water == nil or water.automaticWaterSupply then return 0 end
rate = water.litersPerHour or 0
elseif name == "STRAW" and straw ~= nil then
rate = straw.inputLitersPerHour or 0
elseif food ~= nil and isFoodFillType(placeable, fillType) then
-- kun varer i foderkrybben er foder; mælk/gylle er staldens OUTPUT (understøttet som lager)
rate = food.litersPerHour or 0
level = 0
for _, value in pairs(food.fillLevels or {}) do level = level + (value or 0) end
end
if rate <= 0 then
return 0
end
local free = placeable.getHusbandryFreeCapacity ~= nil and (placeable:getHusbandryFreeCapacity(fillType) or 0) or math.huge
return math.max(0, math.min(rate * ADSources.RESERVE_HOURS - level, free))
end
-- {liters, consumers = {navne}} — gårdens samlede behov for varen; udelukkede placeables (kilderne) tæller ikke.
function ADSources.getFarmDemand(adEnv, fillType, farmId, excludedPlaceables)
local placeables = g_currentMission ~= nil and g_currentMission.placeableSystem ~= nil and g_currentMission.placeableSystem.placeables or {}
local total, consumers = 0, {}
for _, placeable in pairs(placeables) do
if isOwnedBy(placeable, farmId) and not (excludedPlaceables ~= nil and excludedPlaceables[placeable]) then
local reserve = getProductionReserve(placeable, fillType) + getHusbandryReserve(placeable, fillType)
if reserve > 0 then
total = total + reserve
table.insert(consumers, getPlaceableName(placeable, "?"))
end
end
end
return {liters = total, consumers = consumers}
end
-- ---- forbrug og produktion pr. døgn (fanen Lagre) ----
local function addRate(rates, fillType, field, liters)
if fillType == nil or liters == nil or liters <= 0 then return end
rates[fillType] = rates[fillType] or {consumption = 0, production = 0}
rates[fillType][field] = rates[fillType][field] + liters
end
-- Statistik: kun produktioner der kører NU. "Ønsket" (tændt eller mangler input) bruges til reserve og
-- forsyning, men ville her tælle stillestående fabrikker med fuld hastighed — fx fermenteringssiloer
-- med 200.000 cyklusser i timen gav hundreder af millioner liter pr. døgn.
local function isProductionRunning(productionPoint, production)
local status = ProductionPoint ~= nil and ProductionPoint.PROD_STATUS ~= nil and ProductionPoint.PROD_STATUS.RUNNING or nil
if status ~= nil and production.status ~= nil then
return production.status == status
end
return isProductionWanted(productionPoint, production)
end
-- Spillets rater gælder pr. måned (én 24-timers dag pr. måned); faktisk pr. døgn = rate × timeAdjustment
-- (1 / dage pr. periode).
local function getTimeAdjustment()
local environment = g_currentMission ~= nil and g_currentMission.environment or nil
local value = environment ~= nil and tonumber(environment.timeAdjustment) or nil
return value ~= nil and value > 0 and value or 1
end
local function addFactoryRates(rates, placeable)
local productionPoint = getProductionPoint(placeable)
for _, production in ipairs(productionPoint ~= nil and productionPoint.productions or {}) do
if isProductionRunning(productionPoint, production) then
local perDay = (production.cyclesPerMinute or ((production.cyclesPerHour or 0) / 60)) * 1440
for _, input in ipairs(production.inputs or {}) do addRate(rates, input.type, "consumption", (input.amount or 0) * perDay) end
for _, output in ipairs(production.outputs or {}) do addRate(rates, output.type, "production", (output.amount or 0) * perDay) end
end
end
end
local function getFoodWeights(placeable)
local foodSpec = placeable.spec_husbandryFood
local system = g_currentMission ~= nil and g_currentMission.animalFoodSystem or nil
local weights = {}
local food = system ~= nil and system.getAnimalFood ~= nil and foodSpec ~= nil and system:getAnimalFood(foodSpec.animalTypeIndex) or nil
for _, group in pairs(food ~= nil and food.groups or {}) do
local weight = tonumber(group.productionWeight) or tonumber(group.eatWeight) or 1
for _, fillType in pairs(group.fillTypes or {}) do weights[fillType] = math.max(weights[fillType] or 0, weight) end
end
return weights
end
local function addHusbandryRates(rates, placeable)
local food, straw, water = placeable.spec_husbandryFood, placeable.spec_husbandryStraw, placeable.spec_husbandryWater
local byName = function(name) return g_fillTypeManager ~= nil and g_fillTypeManager.getFillTypeIndexByName ~= nil and g_fillTypeManager:getFillTypeIndexByName(name) or nil end
local nameToIndex = function(name)
local index = byName(name)
if index == nil and g_fillTypeManager ~= nil then
for i = 1, 256 do if g_fillTypeManager:getFillTypeNameByIndex(i) == name then return i end end
end
return index
end
if food ~= nil and (food.litersPerHour or 0) > 0 then
for fillType, liters in pairs(ADStorageStats.splitTroughConsumption(food.litersPerHour * 24, food.fillLevels, getFoodWeights(placeable))) do
addRate(rates, fillType, "consumption", liters)
end
end
if straw ~= nil and (straw.inputLitersPerHour or 0) > 0 then
addRate(rates, nameToIndex("STRAW"), "consumption", straw.inputLitersPerHour * 24)
end
if water ~= nil and not water.automaticWaterSupply and (water.litersPerHour or 0) > 0 then
addRate(rates, nameToIndex("WATER"), "consumption", water.litersPerHour * 24)
end
end
-- Staldens produktion: dyrenes output-kurver (liter pr. dyr pr. døgn efter alder) × antal dyr pr.
-- klynge. Output-navne uden fillType: milk -> MILK, manure -> MANURE, liquidManure -> LIQUIDMANURE.
local OUTPUT_DEFAULTS = {milk = "MILK", manure = "MANURE", liquidManure = "LIQUIDMANURE"}
local function resolveFillType(value, outputName)
if type(value) == "number" then return value end
local name = type(value) == "string" and value or OUTPUT_DEFAULTS[outputName]
if name == nil or g_fillTypeManager == nil or g_fillTypeManager.getFillTypeIndexByName == nil then return nil end
return g_fillTypeManager:getFillTypeIndexByName(name)
end
local function curveValue(def, age)
local curve = type(def) == "table" and def.curve or def
if curve == nil or curve.get == nil then return nil end
local ok, value = pcall(curve.get, curve, age)
return ok and type(value) == "number" and value or nil
end
local function addAnimalProduction(rates, placeable)
local animalSystem = g_currentMission ~= nil and g_currentMission.animalSystem or nil
if placeable.getClusters == nil or animalSystem == nil or animalSystem.getSubTypeByIndex == nil then return end
local ok, clusters = pcall(placeable.getClusters, placeable)
if not ok or type(clusters) ~= "table" then return end
for _, cluster in ipairs(clusters) do
local subTypeIndex = cluster.getSubTypeIndex ~= nil and cluster:getSubTypeIndex() or cluster.subTypeIndex
local subType = subTypeIndex ~= nil and animalSystem:getSubTypeByIndex(subTypeIndex) or nil
local age = cluster.getAge ~= nil and cluster:getAge() or cluster.age
local count = cluster.getNumAnimals ~= nil and cluster:getNumAnimals() or cluster.numAnimals or 0
if subType ~= nil and type(subType.output) == "table" and age ~= nil and count > 0 then
for outputName, def in pairs(subType.output) do
local perAnimal = curveValue(def, age)
local fillType = resolveFillType(type(def) == "table" and (def.fillType or def.fillTypeIndex) or nil, outputName)
if perAnimal ~= nil and perAnimal > 0 then
addRate(rates, fillType, "production", perAnimal * count)
end
end
end
end
end
-- {[fillType] = {consumption, production}} i liter pr. døgn for gårdens fabrikker og stalde.
function ADSources.getFarmRates(adEnv, farmId)
local placeables = g_currentMission ~= nil and g_currentMission.placeableSystem ~= nil and g_currentMission.placeableSystem.placeables or {}
local rates = {}
for _, placeable in pairs(placeables) do
if isOwnedBy(placeable, farmId) then
if getProductionPoint(placeable) ~= nil then
addFactoryRates(rates, placeable)
elseif placeable.getHusbandryFillLevel ~= nil then
addHusbandryRates(rates, placeable)
pcall(addAnimalProduction, rates, placeable)
end
end
end
local adjustment = getTimeAdjustment()
for _, rate in pairs(rates) do
rate.consumption, rate.production = rate.consumption * adjustment, rate.production * adjustment
end
return rates
end
-- Bedste pris lige nu blandt alle spillets salgssteder der tager varen (uanset markør/vejnet):
-- {price, top} pr. liter; top = prisen ved årets højeste periode. nil når ingen tager varen.
function ADSources.getMarketQuote(fillType)
local storage = g_currentMission ~= nil and g_currentMission.storageSystem or nil
local best = nil
for _, station in pairs(storage ~= nil and storage:getUnloadingStations() or {}) do
if ADPrices.isSellingStation(station) and ADSources.stationAccepts(station, fillType) then
local quote = ADPrices.getQuote(station, fillType)
if quote ~= nil and (best == nil or quote.price > best.price) then
best = {price = quote.price, top = quote.top}
end
end
end
return best
end
-- ---- inventar: alt gården har liggende, pr. bygning ----
local function addEntry(entries, fillType, level, capacity, extra)
local entry = newProduct(fillType, level, level, capacity, false)
entry.isOutput = extra ~= nil and extra.isOutput or false
entry.count = extra ~= nil and extra.count or nil
entry.slots = extra ~= nil and extra.slots or nil
table.insert(entries, entry)
end
local function factoryEntries(placeable)
local productionPoint = getProductionPoint(placeable)
local storage = productionPoint ~= nil and productionPoint.storage or nil
local entries = {}
if storage == nil then return entries end
local outputs = {}
for _, production in ipairs(productionPoint.productions or {}) do
for _, output in ipairs(production.outputs or {}) do outputs[output.type] = true end
end
for fillType, level in pairs(storage.fillLevels or {}) do
if (level or 0) > 0 then
local capacity = storage.getCapacity ~= nil and (storage:getCapacity(fillType) or 0) or 0
addEntry(entries, fillType, level, capacity, {isOutput = outputs[fillType] == true})
end
end
return entries
end
local function siloEntries(placeable)
local entries, levels, capacities = {}, {}, {}
for _, storage in pairs(placeable.spec_silo.storages or {}) do
for fillType, level in pairs(storage.fillLevels or {}) do
if (level or 0) > 0 then
levels[fillType] = (levels[fillType] or 0) + level
capacities[fillType] = (capacities[fillType] or 0) + (storage.getCapacity ~= nil and (storage:getCapacity(fillType) or 0) or 0)
end
end
end
for fillType, level in pairs(levels) do addEntry(entries, fillType, level, capacities[fillType] or 0) end
return entries
end
local function objectStorageEntries(placeable)
local entries = {}
local spec = placeable.spec_objectStorage
for _, objectInfo in ipairs(spec.objectInfos or {}) do
local fillType, perObject = getObjectInfoContent(objectInfo)
local count = objectInfo.numObjects or 0
if fillType ~= nil and count > 0 then
-- spec.capacity er PLADSER (delt mellem alle varer); i liter: (egne + ledige) paller x indhold
local free = ADSources.objectStorageFreeSlots(placeable)
local slots = free ~= math.huge and count + free or nil -- skur uden grænse: ingen procent
addEntry(entries, fillType, count * perObject, slots ~= nil and slots * perObject or 0, {count = count, slots = slots})
end
end
return entries
end
-- Stald + udvidelsestanke: højeste niveau pr. vare blandt staldens læssestationer (de deler beholdere).
local function getHusbandryStationLevels(placeable)
local storage = g_currentMission ~= nil and g_currentMission.storageSystem or nil
local result = {}
if storage == nil or storage.getLoadingStations == nil then return result end
for _, station in pairs(storage:getLoadingStations()) do
if station.owningPlaceable == placeable and station.getAllFillLevels ~= nil then
local ok, levels = pcall(station.getAllFillLevels, station, g_currentMission:getFarmId())
for fillType, level in pairs(ok and type(levels) == "table" and levels or {}) do
result[fillType] = math.max(result[fillType] or 0, level or 0)
end
end
end
return result
end
local function husbandryEntries(placeable)
local entries, seen = {}, {}
local stationLevels = getHusbandryStationLevels(placeable)
local food = placeable.spec_husbandryFood
if food ~= nil then
local total = 0
for _, level in pairs(food.fillLevels or {}) do total = total + (level or 0) end
for fillType, level in pairs(food.fillLevels or {}) do
if (level or 0) > 0 then
seen[fillType] = true
addEntry(entries, fillType, level, level + math.max(0, (food.capacity or 0) - total))
end
end
end
for _, name in ipairs({"STRAW", "WATER", "MILK", "LIQUIDMANURE", "MANURE"}) do
local fillType = g_fillTypeManager ~= nil and g_fillTypeManager.getFillTypeIndexByName ~= nil and g_fillTypeManager:getFillTypeIndexByName(name) or nil
if fillType == nil and g_fillTypeManager ~= nil then
-- test-fakes uden getFillTypeIndexByName: slå navnet op baglæns
for index = 1, 64 do
if g_fillTypeManager:getFillTypeNameByIndex(index) == name then fillType = index break end
end
end
if fillType ~= nil and not seen[fillType] and placeable.getHusbandryIsFillTypeSupported ~= nil and placeable:getHusbandryIsFillTypeSupported(fillType) then
local own = placeable.getHusbandryFillLevel ~= nil and (placeable:getHusbandryFillLevel(fillType) or 0) or 0
local level = math.max(own, stationLevels[fillType] or 0)
if level > 0 then
local free = placeable.getHusbandryFreeCapacity ~= nil and (placeable:getHusbandryFreeCapacity(fillType) or 0) or 0
addEntry(entries, fillType, level, math.max(level, own + free))
end
end
end
return entries
end
-- { {name, kind = "factory"|"silo"|"objectStorage"|"husbandry", entries = {{fillType, name, title, level, capacity, isOutput, count}}} }
-- kun gårdens bygninger med indhold; sorteret på slags, dernæst navn.
function ADSources.getFarmInventory(adEnv, farmId)
return cacheGet("inventory", tostring(farmId), 2000, function() return ADSources.buildFarmInventory(adEnv, farmId) end)
end
function ADSources.buildFarmInventory(adEnv, farmId)
local placeables = g_currentMission ~= nil and g_currentMission.placeableSystem ~= nil and g_currentMission.placeableSystem.placeables or {}
local result = {}
for _, placeable in pairs(placeables) do
if isOwnedBy(placeable, farmId) then
local kind, entries = nil, nil
if getProductionPoint(placeable) ~= nil then
kind, entries = "factory", factoryEntries(placeable)
elseif placeable.spec_silo ~= nil then
kind, entries = "silo", siloEntries(placeable)
elseif placeable.spec_objectStorage ~= nil then
kind, entries = "objectStorage", objectStorageEntries(placeable)
elseif placeable.getHusbandryFillLevel ~= nil then
kind, entries = "husbandry", husbandryEntries(placeable)
end
if entries ~= nil and #entries > 0 then
table.sort(entries, function(a, b)
if a.isOutput ~= b.isOutput then return a.isOutput end
return tostring(a.title) < tostring(b.title)
end)
table.insert(result, {name = getPlaceableName(placeable, "?"), kind = kind, entries = entries, placeable = placeable})
end
end
end
local order = {factory = 1, husbandry = 2, objectStorage = 3, silo = 4}
table.sort(result, function(a, b)
if order[a.kind] ~= order[b.kind] then return order[a.kind] < order[b.kind] end
return a.name < b.name
end)
return result
end
function ADSources.getSourcePlaceables(sources)
local set = {}
for _, source in ipairs(sources or {}) do
if source.placeable ~= nil then
set[source.placeable] = true
end
end
return set
end