-- AD Smart Pickup -- Gør AutoDrives mappe-rotation for pålæsning vare-bevidst: i stedet for -- "næste markør i mappen" vælges den markør, hvis silo har den valgte vare — -- og blandt dem den med MINDST i (så rester tømmes først og siloer frigives). -- Findes ingen silo med varen, bruges AutoDrives originale rotation uændret. ADSmartPickup = {} -- Markøren er dér hvor trækkerens næse stopper; læssetriggeren ligger et -- vogntog bagved. Siloer tættere end dette regnes som "markørens silo". ADSmartPickup.MAX_MARKER_TO_TRIGGER_DISTANCE = 40 -- Under dette niveau regnes siloen som tom (undgår ture efter støv-rester). ADSmartPickup.MIN_USEFUL_LITERS = 1 ADSmartPickup.LOG_PREFIX = "ADSmartPickup: " local isHooked = false -- Cross-mod adgang: FS25-mods er sandboxede; AutoDrives globals ligger -- i den globale tabel FS25_AutoDrive (zip-navnet). local function getAutoDriveEnv() if FS25_AutoDrive ~= nil and FS25_AutoDrive.ADMultipleTargetsManager ~= nil then return FS25_AutoDrive end return nil end local function getFarmId(vehicle) if vehicle.getOwnerFarmId ~= nil then local farmId = vehicle:getOwnerFarmId() if farmId ~= nil and farmId > 0 then return farmId end end if vehicle.spec_aiVehicle ~= nil and vehicle.spec_aiVehicle.startedFarmId ~= nil and vehicle.spec_aiVehicle.startedFarmId > 0 then return vehicle.spec_aiVehicle.startedFarmId end return nil end local function getWantedFillTypes(driver) local wanted = {} local selected = driver.ad.stateModule:getSelectedFillTypes() if type(selected) == "table" then for _, fillType in pairs(selected) do table.insert(wanted, fillType) end end if #wanted == 0 then local fillType = driver.ad.stateModule:getFillType() if fillType ~= nil then table.insert(wanted, fillType) end end return wanted end local function getDistanceToStation(station, x, z) local closest = math.huge for _, loadTrigger in pairs(station.loadTriggers or {}) do local node = loadTrigger.triggerNode if node ~= nil and entityExists(node) then local tx, _, tz = getWorldTranslation(node) closest = math.min(closest, MathUtil.vector2Length(x - tx, z - tz)) end end return closest end local function getStationAtMarker(wayPoint, farmId) local bestStation = nil local bestDistance = ADSmartPickup.MAX_MARKER_TO_TRIGGER_DISTANCE for _, station in pairs(g_currentMission.storageSystem:getLoadingStations()) do if g_currentMission.accessHandler:canFarmAccess(farmId, station) then local distance = getDistanceToStation(station, wayPoint.x, wayPoint.z) if distance < bestDistance then bestDistance = distance bestStation = station end end end return bestStation end -- Siloens EGET indhold. Udvidelses-siloer (isExtension) deler lager med naboer -- inden for storageRadius, så stationens niveau er en nabo-sum — eget lager er -- det tal spilleren ser på siloen og giver en præcis "mindst først"-rangering. local function getOwnStorageLevel(station, fillTypes) local placeable = station.owningPlaceable local storages = placeable ~= nil and placeable.spec_silo ~= nil and placeable.spec_silo.storages or nil if storages == nil then return 0 end local total = 0 for _, storage in pairs(storages) do for _, fillType in ipairs(fillTypes) do total = total + (storage:getFillLevel(fillType) or 0) end end return total end -- Returnerer niveau + om det er siloens eget lager (true) eller kun nabo-lagre (false). local function getStationFillLevel(station, fillTypes, farmId) local ownLevel = getOwnStorageLevel(station, fillTypes) if ownLevel >= ADSmartPickup.MIN_USEFUL_LITERS then return ownLevel, true end -- intet i eget lager: stationen kan stadig læsse fra nabo-lagre local total = 0 for _, fillType in ipairs(fillTypes) do total = total + (station:getFillLevel(fillType, farmId) or 0) end return total, false end -- Returnerer markerIndex for mappens mindst fyldte silo der har varen, ellers nil. function ADSmartPickup.findLeastFilledPickup(adEnv, driver) local firstMarker = driver.ad.stateModule:getFirstMarker() if firstMarker == nil or firstMarker.group == nil or firstMarker.group == "All" then return nil end local farmId = getFarmId(driver) local fillTypes = getWantedFillTypes(driver) if farmId == nil or #fillTypes == 0 then return nil end -- siloer med varen i EGET lager går altid forud for dem der kun kan låne fra naboer local best = {[true] = {level = math.huge}, [false] = {level = math.huge}} for _, marker in ipairs(adEnv.ADGraphManager:getMapMarkersInGroup(firstMarker.group)) do local wayPoint = adEnv.ADGraphManager:getWayPointById(marker.id) local station = wayPoint ~= nil and getStationAtMarker(wayPoint, farmId) or nil if station ~= nil then local level, isOwn = getStationFillLevel(station, fillTypes, farmId) if level >= ADSmartPickup.MIN_USEFUL_LITERS and level < best[isOwn].level then best[isOwn] = {level = level, marker = marker} end end end local choice = best[true].marker ~= nil and best[true] or best[false] if choice.marker == nil then return nil end return choice.marker.markerIndex, choice.marker.name, choice.level end local function installHook(adEnv) local manager = adEnv.ADMultipleTargetsManager local originalGetNextPickup = manager.getNextPickup manager.getNextPickup = function(self, driver, forcedSkip) local ok, markerIndex, markerName, level = pcall(ADSmartPickup.findLeastFilledPickup, adEnv, driver) if not ok then Logging.warning(ADSmartPickup.LOG_PREFIX .. "fejl i silo-opslag, bruger AutoDrives rotation: %s", tostring(markerIndex)) return originalGetNextPickup(self, driver, forcedSkip) end if markerIndex == nil then return originalGetNextPickup(self, driver, forcedSkip) end -- hold AutoDrives egen rotations-tilstand konsistent med vores valg local groupName = driver.ad.stateModule:getFirstMarker().group self.pickups[groupName] = self.pickups[groupName] or {} self.pickups[groupName].lastTarget = markerIndex self.pickups[groupName].lastVehicle = driver Logging.info(ADSmartPickup.LOG_PREFIX .. "'%s' -> %s (%.0f l)", tostring(driver:getName()), tostring(markerName), level) return markerIndex end Logging.info(ADSmartPickup.LOG_PREFIX .. "aktiv (mindst fyldte silo med varen vælges i mappe-rotation)") end function ADSmartPickup:update(dt) if isHooked then return end local adEnv = getAutoDriveEnv() if adEnv ~= nil and g_currentMission ~= nil and g_currentMission.storageSystem ~= nil then installHook(adEnv) isHooked = true end end addModEventListener(ADSmartPickup)