8c76790ece
Lagervarer tømmes stadig mindst-fyldte-først; varer hvis tanke løber fulde og stopper produktionen hentes fyldest-først (ADSupplyPlanner.FULLEST_FIRST_FILL_TYPES). Strategien vises i loggen. fix: en trigger foran næsen tæller aldrig som markørens egen (tolerance 2 -> 0). Markør-drift i ensilage-rækken lod næste tanks rør (1,9 m foran) vinde. Verificeret mod savegame: 40/40 siloer, 7/7 ensilage-tanke, 0 dropof.
507 lines
23 KiB
Lua
507 lines
23 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.
|
|
--
|
|
-- Behovsstyret forsyning: er der valgt FLERE varer i AutoDrive, ses de som
|
|
-- ingredienser til aflæsningsmålet (fx en TMR-blander). Pr. tur hentes den
|
|
-- ingrediens målet mangler mest, fra en silo hvor som helst på kortet.
|
|
|
|
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
|
|
-- I tætte silo-rækker ligger nabosiloens trigger FORAN næsen og er ofte nærmere
|
|
-- end markørens egen. Vognen holder altid BAG næsen, så en trigger foran næsen
|
|
-- hører til naboen (en ensom silo med markøren ved røret fanges af nødløsningen).
|
|
ADSmartPickup.MAX_TRIGGER_AHEAD_OF_MARKER = 0
|
|
-- Vognen følger trækkerens spor: en trigger længere til siden end dette er en
|
|
-- anden bane (fx aflæsningssiden af samme tank), ikke markørens læsseplads.
|
|
ADSmartPickup.MAX_TRIGGER_LATERAL_OFFSET = 6
|
|
-- Kendes køreretningen, men ligger ingen trigger i sporet bag næsen (kurve ind til
|
|
-- markøren), accepteres kun en trigger helt tæt på.
|
|
ADSmartPickup.MAX_FALLBACK_TRIGGER_DISTANCE = 12
|
|
-- Under dette niveau regnes siloen som tom (undgår ture efter støv-rester).
|
|
ADSmartPickup.MIN_USEFUL_LITERS = 1
|
|
-- Sikkerhedsventil: står køretøjet ved markøren så længe uden at vognen fyldes
|
|
-- (forkert markør↔silo-kobling, røret når ikke vognen), slippes grebet.
|
|
ADSmartPickup.MAX_WAIT_WITHOUT_LOADING_MS = 30000
|
|
ADSmartPickup.LOG_PREFIX = "ADSmartPickup: "
|
|
|
|
local LOAD_TRIGGER_NODE_FIELDS = {"triggerNode"}
|
|
local UNLOAD_TRIGGER_NODE_FIELDS = {"exactFillRootNode", "triggerNode", "aiNode"}
|
|
|
|
local isHooked = false
|
|
-- køretøj -> {ingredients, narrowedTo}. AutoDrive roterer selv vare ved triggeren
|
|
-- når flere er valgt, så valget indsnævres til én pr. tur; listen huskes her.
|
|
local supplyState = setmetatable({}, {__mode = "k"})
|
|
-- køretøj -> {station, fillTypes}: siloen moden sendte køretøjet til på denne tur.
|
|
local activePickups = setmetatable({}, {__mode = "k"})
|
|
-- køretøj -> senest loggede valg (AutoDrive spørger flere gange pr. start).
|
|
local lastLogged = setmetatable({}, {__mode = "k"})
|
|
|
|
-- 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
|
|
|
|
-- Ankomstretning ved markøren (enhedsvektor) ud fra forrige waypoint, ellers nil.
|
|
local function getArrivalDirection(adEnv, wayPoint)
|
|
local previousId = wayPoint.incoming ~= nil and wayPoint.incoming[1] or nil
|
|
local previous = previousId ~= nil and adEnv.ADGraphManager:getWayPointById(previousId) or nil
|
|
if previous == nil then
|
|
return nil
|
|
end
|
|
local dx, dz = wayPoint.x - previous.x, wayPoint.z - previous.z
|
|
local length = math.sqrt(dx * dx + dz * dz)
|
|
if length < 0.01 then
|
|
return nil
|
|
end
|
|
return {x = dx / length, z = dz / length}
|
|
end
|
|
|
|
-- Nærmeste trigger-afstand for en station, samlet og blandt triggere BAG næsen.
|
|
local function getDistancesToTriggers(triggers, nodeFields, wayPoint, direction)
|
|
local closest, closestBehind = math.huge, math.huge
|
|
for _, trigger in pairs(triggers or {}) do
|
|
for _, field in ipairs(nodeFields) do
|
|
local node = trigger[field]
|
|
if node ~= nil and entityExists(node) then
|
|
local tx, _, tz = getWorldTranslation(node)
|
|
local distance = MathUtil.vector2Length(wayPoint.x - tx, wayPoint.z - tz)
|
|
closest = math.min(closest, distance)
|
|
if direction ~= nil then
|
|
-- behind > 0: bag næsen set i køreretningen; lateral: afstand fra sporet
|
|
local behind = (wayPoint.x - tx) * direction.x + (wayPoint.z - tz) * direction.z
|
|
local lateral = math.abs((wayPoint.x - tx) * direction.z - (wayPoint.z - tz) * direction.x)
|
|
if behind >= -ADSmartPickup.MAX_TRIGGER_AHEAD_OF_MARKER and lateral <= ADSmartPickup.MAX_TRIGGER_LATERAL_OFFSET then
|
|
closestBehind = math.min(closestBehind, distance)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return closest, closestBehind
|
|
end
|
|
|
|
-- Markørens station: nærmeste trigger i sporet bag næsen. Findes ingen, bruges
|
|
-- nærmeste trigger overhovedet — helt tæt på hvis retningen kendes, ellers som før.
|
|
local function getClosestStation(adEnv, candidateStations, triggersField, nodeFields, wayPoint, farmId)
|
|
local direction = getArrivalDirection(adEnv, wayPoint)
|
|
local maxFallbackDistance = direction ~= nil and ADSmartPickup.MAX_FALLBACK_TRIGGER_DISTANCE or ADSmartPickup.MAX_MARKER_TO_TRIGGER_DISTANCE
|
|
local best = {any = {distance = maxFallbackDistance}, behind = {distance = ADSmartPickup.MAX_MARKER_TO_TRIGGER_DISTANCE}}
|
|
for _, station in pairs(candidateStations) do
|
|
if g_currentMission.accessHandler:canFarmAccess(farmId, station) then
|
|
local closest, closestBehind = getDistancesToTriggers(station[triggersField], nodeFields, wayPoint, direction)
|
|
if closest < best.any.distance then
|
|
best.any = {distance = closest, station = station}
|
|
end
|
|
if closestBehind < best.behind.distance then
|
|
best.behind = {distance = closestBehind, station = station}
|
|
end
|
|
end
|
|
end
|
|
return best.behind.station or best.any.station
|
|
end
|
|
|
|
local function getLoadStationAtMarker(adEnv, wayPoint, farmId)
|
|
return getClosestStation(adEnv, g_currentMission.storageSystem:getLoadingStations(), "loadTriggers", LOAD_TRIGGER_NODE_FIELDS, wayPoint, farmId)
|
|
end
|
|
|
|
local function getUnloadStationAtMarker(adEnv, wayPoint, farmId)
|
|
return getClosestStation(adEnv, g_currentMission.storageSystem:getUnloadingStations(), "unloadTriggers", UNLOAD_TRIGGER_NODE_FIELDS, wayPoint, farmId)
|
|
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
|
|
|
|
-- Parrer markører med deres læssestation (markører uden station udelades).
|
|
-- excludedPlaceable: aflæsningsmålets egen placeable må ikke være kilde.
|
|
local function collectMarkerStations(adEnv, candidateMarkers, farmId, excludedPlaceable)
|
|
local markerStations = {}
|
|
for _, marker in pairs(candidateMarkers) do
|
|
local wayPoint = adEnv.ADGraphManager:getWayPointById(marker.id)
|
|
local station = wayPoint ~= nil and getLoadStationAtMarker(adEnv, wayPoint, farmId) or nil
|
|
local isExcluded = station ~= nil and excludedPlaceable ~= nil and station.owningPlaceable == excludedPlaceable
|
|
if station ~= nil and not isExcluded then
|
|
table.insert(markerStations, {marker = marker, station = station})
|
|
end
|
|
end
|
|
return markerStations
|
|
end
|
|
|
|
local function getFillTypeNames(fillTypes)
|
|
local names = {}
|
|
for _, fillType in ipairs(fillTypes) do
|
|
local name = g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(fillType) or nil
|
|
if name ~= nil then
|
|
table.insert(names, name)
|
|
end
|
|
end
|
|
return names
|
|
end
|
|
|
|
-- Bedste kilde der har varen: mindst fyldte først for lagervarer, fyldeste først for
|
|
-- varer hvis tanke løber fulde (ADSupplyPlanner). Siloer med varen i EGET lager går
|
|
-- altid forud for dem der kun kan låne fra naboer. Returnerer et valg eller nil.
|
|
local function findBestSource(markerStations, fillTypes, farmId)
|
|
local prefersFullest = ADSupplyPlanner.prefersFullestSource(getFillTypeNames(fillTypes))
|
|
local best = {[true] = {}, [false] = {}}
|
|
for _, entry in ipairs(markerStations) do
|
|
local level, isOwn = getStationFillLevel(entry.station, fillTypes, farmId)
|
|
if level >= ADSmartPickup.MIN_USEFUL_LITERS and ADSupplyPlanner.isBetterSource(level, best[isOwn].level, prefersFullest) then
|
|
best[isOwn] = {level = level, marker = entry.marker, station = entry.station, isOwn = isOwn}
|
|
end
|
|
end
|
|
|
|
local choice = best[true].marker ~= nil and best[true] or best[false]
|
|
if choice.marker == nil then
|
|
return nil
|
|
end
|
|
return {
|
|
markerIndex = choice.marker.markerIndex,
|
|
markerName = choice.marker.name,
|
|
group = choice.marker.group,
|
|
level = choice.level,
|
|
station = choice.station,
|
|
isOwn = choice.isOwn,
|
|
fillTypes = fillTypes,
|
|
prefersFullest = prefersFullest,
|
|
}
|
|
end
|
|
|
|
-- Mappe-logik: mappens mindst fyldte silo der har den/de valgte varer, 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
|
|
local groupMarkers = adEnv.ADGraphManager:getMapMarkersInGroup(firstMarker.group)
|
|
return findBestSource(collectMarkerStations(adEnv, groupMarkers, farmId, nil), fillTypes, farmId)
|
|
end
|
|
|
|
-- Lagrene bag aflæsningsstationen; produktionspunkter uden targetStorages læses direkte.
|
|
local function getTargetStorages(station)
|
|
if station.targetStorages ~= nil and next(station.targetStorages) ~= nil then
|
|
return station.targetStorages
|
|
end
|
|
local placeable = station.owningPlaceable
|
|
local productionPoint = placeable ~= nil and placeable.spec_productionPoint ~= nil and placeable.spec_productionPoint.productionPoint or nil
|
|
if productionPoint ~= nil and productionPoint.storage ~= nil then
|
|
return {productionPoint.storage}
|
|
end
|
|
return {}
|
|
end
|
|
|
|
-- Niveau + kapacitet pr. ingrediens i aflæsningsmålets lager.
|
|
local function getTargetNeeds(station, ingredients, farmId)
|
|
local needs = {}
|
|
local storages = getTargetStorages(station)
|
|
for _, fillType in ipairs(ingredients) do
|
|
if station.getIsFillTypeSupported == nil or station:getIsFillTypeSupported(fillType) then
|
|
local level, freeSpace = 0, 0
|
|
for _, storage in pairs(storages) do
|
|
level = level + (storage:getFillLevel(fillType) or 0)
|
|
freeSpace = freeSpace + (storage:getFreeCapacity(fillType) or 0)
|
|
end
|
|
if level + freeSpace <= 0 and station.getFreeCapacity ~= nil then
|
|
-- ukendt lagertype: brug stationens egen opgørelse af fri plads
|
|
level, freeSpace = 0, station:getFreeCapacity(fillType, farmId) or 0
|
|
end
|
|
table.insert(needs, {fillType = fillType, level = level, capacity = level + freeSpace})
|
|
end
|
|
end
|
|
return needs
|
|
end
|
|
|
|
-- Behovsstyret: den ingrediens aflæsningsmålet mangler mest, som også findes i en silo.
|
|
function ADSmartPickup.findSupplyPickup(adEnv, driver, ingredients)
|
|
local secondMarker = driver.ad.stateModule:getSecondMarker()
|
|
local farmId = getFarmId(driver)
|
|
if secondMarker == nil or farmId == nil then
|
|
return nil
|
|
end
|
|
local targetWayPoint = adEnv.ADGraphManager:getWayPointById(secondMarker.id)
|
|
local target = targetWayPoint ~= nil and getUnloadStationAtMarker(adEnv, targetWayPoint, farmId) or nil
|
|
if target == nil then
|
|
return nil
|
|
end
|
|
|
|
local allMarkers = adEnv.ADGraphManager:getMapMarkers()
|
|
local markerStations = collectMarkerStations(adEnv, allMarkers, farmId, target.owningPlaceable)
|
|
for _, need in ipairs(ADSupplyPlanner.rankNeeds(getTargetNeeds(target, ingredients, farmId))) do
|
|
local choice = findBestSource(markerStations, {need.fillType}, farmId)
|
|
if choice ~= nil then
|
|
return {
|
|
markerIndex = choice.markerIndex,
|
|
markerName = choice.markerName,
|
|
group = choice.group,
|
|
level = choice.level,
|
|
station = choice.station,
|
|
isOwn = choice.isOwn,
|
|
fillTypes = choice.fillTypes,
|
|
prefersFullest = choice.prefersFullest,
|
|
fillType = need.fillType,
|
|
need = need,
|
|
}
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function applySupplyChoice(driver, ingredients, choice)
|
|
local stateModule = driver.ad.stateModule
|
|
stateModule:setFillType(choice.fillType)
|
|
stateModule.selectedFillTypes = {choice.fillType}
|
|
stateModule:raiseDirtyFlag()
|
|
supplyState[driver] = {ingredients = ingredients, narrowedTo = choice.fillType}
|
|
end
|
|
|
|
-- Samlet valg: behovsstyret når flere varer er valgt, ellers mappe-logik. nil = AutoDrives original.
|
|
function ADSmartPickup.choosePickup(adEnv, driver)
|
|
local selected = driver.ad.stateModule:getSelectedFillTypes()
|
|
local ingredients = ADSupplyPlanner.resolveIngredients(supplyState[driver], selected)
|
|
if ingredients == nil then
|
|
supplyState[driver] = nil
|
|
return ADSmartPickup.findLeastFilledPickup(adEnv, driver)
|
|
end
|
|
|
|
local choice = ADSmartPickup.findSupplyPickup(adEnv, driver, ingredients)
|
|
if choice == nil then
|
|
return ADSmartPickup.findLeastFilledPickup(adEnv, driver)
|
|
end
|
|
applySupplyChoice(driver, ingredients, choice)
|
|
return choice
|
|
end
|
|
|
|
local function describeChoice(choice)
|
|
local source = choice.isOwn and "eget lager" or "nabo-lager"
|
|
source = source .. (choice.prefersFullest and ", fyldeste først" or ", mindst fyldte først")
|
|
if choice.fillType == nil then
|
|
return string.format("%.0f l, %s", choice.level, source)
|
|
end
|
|
local fillTypeName = g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(choice.fillType) or tostring(choice.fillType)
|
|
return string.format("%s: kilde %.0f l, %s, målet har %.0f/%.0f l", tostring(fillTypeName), choice.level, source, choice.need.level, choice.need.capacity)
|
|
end
|
|
|
|
local function logChoiceOnce(driver, choice)
|
|
local message = string.format("'%s' -> %s (%s)", tostring(driver:getName()), tostring(choice.markerName), describeChoice(choice))
|
|
if lastLogged[driver] ~= message then
|
|
lastLogged[driver] = message
|
|
Logging.info(ADSmartPickup.LOG_PREFIX .. "%s", message)
|
|
end
|
|
end
|
|
|
|
-- Sendte moden køretøjet til en silo der stadig har varen? Så skal AutoDrive
|
|
-- stå og læsse dér; løber kilden tør, slippes grebet og turen fortsætter.
|
|
function ADSmartPickup.shouldWaitForLoading(vehicle)
|
|
local pickup = activePickups[vehicle]
|
|
if pickup == nil then
|
|
return false
|
|
end
|
|
-- kun FREMME ved markøren: undervejs ruller vognen gennem nabosiloers triggere,
|
|
-- og dér skal AutoDrives rotation gælde (kør videre ved tom trigger).
|
|
local drivePathModule = vehicle.ad ~= nil and vehicle.ad.drivePathModule or nil
|
|
if drivePathModule == nil or not drivePathModule:isTargetReached() then
|
|
return false
|
|
end
|
|
local level = getStationFillLevel(pickup.station, pickup.fillTypes, getFarmId(vehicle))
|
|
return level >= ADSmartPickup.MIN_USEFUL_LITERS
|
|
end
|
|
|
|
-- Med "Rotér mål" + mapper afslutter AutoDrive pålæsningen i det øjeblik markøren
|
|
-- nås (LoadAtDestinationTask: rotation = "prøv næste silo", aldrig vente). Moden
|
|
-- har valgt en silo der HAR varen, så under opgavens update ser AutoDrive
|
|
-- rotationen som slået fra for netop det køretøj -> den står og læsser som normalt.
|
|
local function installLoadWaitHook(adEnv)
|
|
local task, autoDrive = adEnv.LoadAtDestinationTask, adEnv.AutoDrive
|
|
if task == nil or task.update == nil or autoDrive == nil or autoDrive.getSetting == nil then
|
|
Logging.warning(ADSmartPickup.LOG_PREFIX .. "kunne ikke koble på pålæsningsopgaven; traktoren venter ikke ved siloen")
|
|
return
|
|
end
|
|
|
|
local originalGetSetting, originalUpdate = autoDrive.getSetting, task.update
|
|
local waitingVehicle = nil
|
|
|
|
autoDrive.getSetting = function(settingName, vehicle)
|
|
if waitingVehicle ~= nil and vehicle == waitingVehicle and settingName == "rotateTargets" then
|
|
return autoDrive.RT_NONE
|
|
end
|
|
return originalGetSetting(settingName, vehicle)
|
|
end
|
|
|
|
-- true når vognen ikke er blevet fyldt i MAX_WAIT_WITHOUT_LOADING_MS ved markøren
|
|
local function hasLoadingStalled(loadTask, dt)
|
|
local pickup = activePickups[loadTask.vehicle]
|
|
local fillLevel = autoDrive.getAllFillLevels ~= nil and autoDrive.getAllFillLevels(loadTask.trailers) or 0
|
|
if pickup.lastFillLevel == nil or fillLevel > pickup.lastFillLevel then
|
|
pickup.lastFillLevel, pickup.stalledMs = fillLevel, 0
|
|
return false
|
|
end
|
|
pickup.stalledMs = pickup.stalledMs + dt
|
|
return pickup.stalledMs > ADSmartPickup.MAX_WAIT_WITHOUT_LOADING_MS
|
|
end
|
|
|
|
task.update = function(self, dt)
|
|
local ok, shouldWait = pcall(ADSmartPickup.shouldWaitForLoading, self.vehicle)
|
|
shouldWait = ok and shouldWait
|
|
if shouldWait then
|
|
local stalledOk, isStalled = pcall(hasLoadingStalled, self, dt)
|
|
if not stalledOk or isStalled then
|
|
Logging.warning(ADSmartPickup.LOG_PREFIX .. "'%s' læsser ikke ved markøren; slipper grebet så AutoDrive kører videre", tostring(self.vehicle:getName()))
|
|
activePickups[self.vehicle] = nil
|
|
shouldWait = false
|
|
end
|
|
end
|
|
waitingVehicle = shouldWait and self.vehicle or nil
|
|
local updateOk, err = pcall(originalUpdate, self, dt)
|
|
waitingVehicle = nil
|
|
if not updateOk then
|
|
error(err, 0)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function installHook(adEnv)
|
|
local manager = adEnv.ADMultipleTargetsManager
|
|
local originalGetNextPickup = manager.getNextPickup
|
|
|
|
manager.getNextPickup = function(self, driver, forcedSkip)
|
|
local ok, choice = pcall(ADSmartPickup.choosePickup, adEnv, driver)
|
|
if not ok then
|
|
Logging.warning(ADSmartPickup.LOG_PREFIX .. "fejl i silo-opslag, bruger AutoDrives rotation: %s", tostring(choice))
|
|
activePickups[driver] = nil
|
|
return originalGetNextPickup(self, driver, forcedSkip)
|
|
end
|
|
if choice == nil then
|
|
activePickups[driver] = nil
|
|
return originalGetNextPickup(self, driver, forcedSkip)
|
|
end
|
|
activePickups[driver] = {station = choice.station, fillTypes = choice.fillTypes}
|
|
|
|
-- hold AutoDrives egen rotations-tilstand konsistent med vores valg
|
|
self.pickups[choice.group] = self.pickups[choice.group] or {}
|
|
self.pickups[choice.group].lastTarget = choice.markerIndex
|
|
self.pickups[choice.group].lastVehicle = driver
|
|
|
|
logChoiceOnce(driver, choice)
|
|
return choice.markerIndex
|
|
end
|
|
|
|
installLoadWaitHook(adEnv)
|
|
|
|
Logging.info(ADSmartPickup.LOG_PREFIX .. "aktiv (mindst fyldte silo med varen; flere valgte varer = behovsstyret forsyning)")
|
|
end
|
|
|
|
-- Når AutoDrive stopper, får køretøjet hele ingredienslisten tilbage i varevalget
|
|
-- (AutoDrive gemmer valget i savegame, så listen overlever også en genindlæsning).
|
|
local function restoreStoppedVehicles()
|
|
-- en standset traktor må ikke tage "stå og læs"-grebet med over i næste opgave
|
|
for vehicle in pairs(activePickups) do
|
|
local stateModule = vehicle.ad ~= nil and vehicle.ad.stateModule or nil
|
|
if stateModule == nil or stateModule.isActive == nil or not stateModule:isActive() then
|
|
activePickups[vehicle] = nil
|
|
end
|
|
end
|
|
for vehicle, state in pairs(supplyState) do
|
|
local stateModule = vehicle.ad ~= nil and vehicle.ad.stateModule or nil
|
|
if stateModule == nil or not stateModule:isActive() then
|
|
supplyState[vehicle] = nil
|
|
local selected = stateModule ~= nil and stateModule:getSelectedFillTypes() or nil
|
|
if type(selected) == "table" and #selected == 1 and selected[1] == state.narrowedTo then
|
|
stateModule.selectedFillTypes = ADSupplyPlanner.copyList(state.ingredients)
|
|
stateModule:raiseDirtyFlag()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function ADSmartPickup:update(dt)
|
|
if not isHooked then
|
|
local adEnv = getAutoDriveEnv()
|
|
if adEnv ~= nil and g_currentMission ~= nil and g_currentMission.storageSystem ~= nil then
|
|
installHook(adEnv)
|
|
isHooked = true
|
|
end
|
|
return
|
|
end
|
|
|
|
local ok, err = pcall(restoreStoppedVehicles)
|
|
if not ok then
|
|
Logging.warning(ADSmartPickup.LOG_PREFIX .. "kunne ikke gendanne varevalg: %s", tostring(err))
|
|
supplyState = setmetatable({}, {__mode = "k"})
|
|
end
|
|
end
|
|
|
|
addModEventListener(ADSmartPickup)
|