-- 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 = {} 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 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 -- 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 == "silo" then return false elseif source.kind == "objectStorage" then return true end if stationSupports(source.station, fillType) then return false end return ADSources.isPalletFillType(fillType) end local function getProductionPoint(placeable) return placeable ~= nil and placeable.spec_productionPoint ~= nil and placeable.spec_productionPoint.productionPoint or nil end -- Placeable'ns "nærmeste punkt" for kildegenkendelse: pallespawnerens første plads (fabrik) -- eller rodnoden. local function getPlaceableAnchor(placeable) local productionPoint = getProductionPoint(placeable) local spawner = productionPoint ~= nil and productionPoint.palletSpawner or nil 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) 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 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 -- 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 return {kind = "factory", placeable = factory, station = nil, 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 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). local function getPalletsNearSpawner(placeable, fillType, farmId) local productionPoint = getProductionPoint(placeable) local spawner = productionPoint ~= nil and productionPoint.palletSpawner or nil local node = spawner ~= nil and spawner.spawnPlaces ~= nil and spawner.spawnPlaces[1] ~= nil and spawner.spawnPlaces[1].startNode or placeable.rootNode local sx, sz = getNodePosition(node) if sx == nil then return 0 end local vehicles = g_currentMission ~= nil and g_currentMission.vehicleSystem ~= nil and g_currentMission.vehicleSystem.vehicles or {} local total = 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 then total = total + level end end end return total end 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} 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 available = level + (isPallet and getPalletsNearSpawner(source.placeable, fillType, farmId) or 0) table.insert(products, newProduct(fillType, available, level, capacity, isPallet)) 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 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 table.insert(products, newProduct(fillType, count * perObject, spec.numStoredObjects or count, spec.capacity or 0, true)) end 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 == "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 function ADSources.stationAccepts(station, fillType) if station.acceptedFillTypes ~= nil then return station.acceptedFillTypes[fillType] == true end if station.getIsFillTypeSupported ~= nil then return station:getIsFillTypeSupported(fillType) == true end return station.supportedFillTypes ~= nil and station.supportedFillTypes[fillType] == true end -- Markører hvis aflæsningsstation er et rigtigt salgssted: { {marker, station} }. function ADSources.getSellMarkers(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 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