-- AD Buildings -- Gårdens bygninger som brugeren vælger imellem (i stedet for markører): fabrik, silo, stald, -- objektlager og salgssted, hver med den AD-markør der står nærmest bygningens læsse- og -- aflæsningstrigger (≤ MAX_MARKER_DISTANCE; Wait-markører aldrig; siloers læssemarkør via det -- strenge opslag, så tætte rækker ikke blandes). Spil-adapter med 1 s cache. ADBuildings = {} ADBuildings.LOG_PREFIX = "ADSmartPickup: " ADBuildings.MAX_MARKER_DISTANCE = 40 ADBuildings.CACHE_MS = 1000 local cache = {list = nil, farmId = nil, time = -math.huge} local function now() if g_currentMission ~= nil and type(g_currentMission.time) == "number" then return g_currentMission.time end return os.clock() * 1000 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 getName(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 function ADBuildings.getId(placeable) if placeable.uniqueId ~= nil then return tostring(placeable.uniqueId) end local x, z = getNodePosition(placeable.rootNode) if x == nil then return nil end return string.format("pos:%d:%d", math.floor(x + 0.5), math.floor(z + 0.5)) end local function getProductionPoint(placeable) return placeable.spec_productionPoint ~= nil and placeable.spec_productionPoint.productionPoint or nil end local function isOwnedBy(placeable, farmId) 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 getKind(placeable) if getProductionPoint(placeable) ~= nil then return "factory" end if placeable.spec_silo ~= nil then return "silo" end if placeable.getHusbandryFillLevel ~= nil then return "husbandry" end if placeable.spec_objectStorage ~= nil then return "objectStorage" end return nil end -- Trigger-noder pr. bygning: læsse (loadTriggers.triggerNode, pallespawner, objektlager) og -- aflæsning (unloadTriggers exactFillRootNode/triggerNode/aiNode). local function collectNodes(placeable, loadStations, unloadStations) local loadNodes, unloadNodes = {}, {} for _, station in pairs(loadStations) do if station.owningPlaceable == placeable then for _, trigger in pairs(station.loadTriggers or {}) do if trigger.triggerNode ~= nil then table.insert(loadNodes, trigger.triggerNode) end end end end for _, station in pairs(unloadStations) do if station.owningPlaceable == placeable then for _, trigger in pairs(station.unloadTriggers or {}) do for _, field in ipairs({"exactFillRootNode", "triggerNode", "aiNode"}) do if trigger[field] ~= nil then table.insert(unloadNodes, trigger[field]) end end end end end local productionPoint = getProductionPoint(placeable) local spawner = productionPoint ~= nil and productionPoint.palletSpawner or nil if spawner ~= nil then for _, place in ipairs(spawner.spawnPlaces or {}) do if place.startNode ~= nil then table.insert(loadNodes, place.startNode) end end end if placeable.spec_objectStorage ~= nil and placeable.rootNode ~= nil then table.insert(loadNodes, placeable.rootNode) table.insert(unloadNodes, placeable.rootNode) end return loadNodes, unloadNodes end -- Nærmeste ikke-Wait-markør til en af noderne, ≤ MAX_MARKER_DISTANCE. local function nearestMarker(adEnv, nodes) local best, bestDistance = nil, ADBuildings.MAX_MARKER_DISTANCE if #nodes == 0 then return nil end local positions = {} for _, node in ipairs(nodes) do local x, z = getNodePosition(node) if x ~= nil then table.insert(positions, {x = x, z = z}) end end for _, marker in pairs(adEnv.ADGraphManager:getMapMarkers()) do if not ADSupplyPlanner.isWaitMarker(marker) then local wayPoint = adEnv.ADGraphManager:getWayPointById(marker.id) if wayPoint ~= nil then for _, position in ipairs(positions) do local dx, dz = wayPoint.x - position.x, wayPoint.z - position.z local distance = math.sqrt(dx * dx + dz * dz) if distance < bestDistance then best, bestDistance = marker, distance end end end end end if best == nil then return nil end return {id = best.id, name = tostring(best.name), distance = math.floor(bestDistance + 0.5)} end -- Silo: den markør hvis STRENGE opslag (trigger bag næsen) rammer siloens læssestation. local function siloLoadMarker(adEnv, farmId, placeable, loadStations) local own = {} for _, station in pairs(loadStations) do if station.owningPlaceable == placeable then own[station] = true end end local best = nil for _, marker in pairs(adEnv.ADGraphManager:getMapMarkers()) do if not ADSupplyPlanner.isWaitMarker(marker) then local station = ADSmartPickup.getLoadStationAtWayPointId(adEnv, marker.id, farmId) if station ~= nil and own[station] then best = {id = marker.id, name = tostring(marker.name), distance = 0} break end end end return best end local function isSellPlaceable(placeable, unloadStations) for _, station in pairs(unloadStations) do if station.owningPlaceable == placeable and ADPrices.isSellingStation(station) then return true end end return false end local ORDER = {factory = 1, husbandry = 2, objectStorage = 3, silo = 4, sell = 5} local function build(adEnv, farmId) local placeables = g_currentMission ~= nil and g_currentMission.placeableSystem ~= nil and g_currentMission.placeableSystem.placeables or {} local storage = g_currentMission ~= nil and g_currentMission.storageSystem or nil local loadStations = storage ~= nil and storage:getLoadingStations() or {} local unloadStations = storage ~= nil and storage:getUnloadingStations() or {} local result = {} for _, placeable in pairs(placeables) do local kind = getKind(placeable) local isSell = kind == nil and isSellPlaceable(placeable, unloadStations) if isSell then kind = "sell" end if kind ~= nil and (kind == "sell" or isOwnedBy(placeable, farmId)) then local id = ADBuildings.getId(placeable) if id ~= nil then local loadNodes, unloadNodes = collectNodes(placeable, loadStations, unloadStations) local loadMarker if kind == "silo" then loadMarker = siloLoadMarker(adEnv, farmId, placeable, loadStations) else loadMarker = nearestMarker(adEnv, loadNodes) end table.insert(result, { id = id, name = getName(placeable, id), kind = kind, placeable = placeable, loadMarker = loadMarker, unloadMarker = nearestMarker(adEnv, unloadNodes), canBeSource = kind ~= "sell", canBeTarget = kind == "husbandry" or kind == "factory", canSell = kind == "sell", }) end end end 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 ADBuildings.invalidate() cache.list, cache.time = nil, -math.huge end function ADBuildings.list(adEnv, farmId) local time = now() if cache.list ~= nil and cache.farmId == farmId and (time - cache.time) < ADBuildings.CACHE_MS then return cache.list end cache.list, cache.farmId, cache.time = build(adEnv, farmId), farmId, time return cache.list end function ADBuildings.find(adEnv, farmId, id) for _, building in ipairs(ADBuildings.list(adEnv, farmId)) do if building.id == id then return building end end return nil end -- role "load": bygningen hvis læssemarkør er wayPointId; "unload": aflæsningsmarkør. function ADBuildings.byMarker(adEnv, farmId, wayPointId, role) for _, building in ipairs(ADBuildings.list(adEnv, farmId)) do local marker = role == "unload" and building.unloadMarker or building.loadMarker if marker ~= nil and marker.id == wayPointId then return building end end return nil end