187 lines
7.1 KiB
Lua
187 lines
7.1 KiB
Lua
-- 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)
|