fix: stå og læs ved den valgte silo — AutoDrive kørte tom videre (v1.2.0.0)
Med rotateTargets + mapper afslutter AutoDrives LoadAtDestinationTask pålæsningen i det øjeblik markøren nås; traktoren kørte derfor lige igennem silo-markøren og tom til aflæsning. Når moden har valgt siloen, ses rotationen som slået fra for netop det køretøj under opgavens update, så AutoDrive står og læsser. Grebet slippes når kilden er tom eller AD stopper. Desuden: samme valg logges kun én gang, og loggen viser eget/nabo-lager.
This commit is contained in:
@@ -24,6 +24,10 @@ local isHooked = false
|
|||||||
-- køretøj -> {ingredients, narrowedTo}. AutoDrive roterer selv vare ved triggeren
|
-- 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.
|
-- når flere er valgt, så valget indsnævres til én pr. tur; listen huskes her.
|
||||||
local supplyState = setmetatable({}, {__mode = "k"})
|
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
|
-- Cross-mod adgang: FS25-mods er sandboxede; AutoDrives globals ligger
|
||||||
-- i den globale tabel FS25_AutoDrive (zip-navnet).
|
-- i den globale tabel FS25_AutoDrive (zip-navnet).
|
||||||
@@ -155,7 +159,7 @@ local function findLeastFilled(markerStations, fillTypes, farmId)
|
|||||||
for _, entry in ipairs(markerStations) do
|
for _, entry in ipairs(markerStations) do
|
||||||
local level, isOwn = getStationFillLevel(entry.station, fillTypes, farmId)
|
local level, isOwn = getStationFillLevel(entry.station, fillTypes, farmId)
|
||||||
if level >= ADSmartPickup.MIN_USEFUL_LITERS and level < best[isOwn].level then
|
if level >= ADSmartPickup.MIN_USEFUL_LITERS and level < best[isOwn].level then
|
||||||
best[isOwn] = {level = level, marker = entry.marker}
|
best[isOwn] = {level = level, marker = entry.marker, station = entry.station, isOwn = isOwn}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -168,6 +172,9 @@ local function findLeastFilled(markerStations, fillTypes, farmId)
|
|||||||
markerName = choice.marker.name,
|
markerName = choice.marker.name,
|
||||||
group = choice.marker.group,
|
group = choice.marker.group,
|
||||||
level = choice.level,
|
level = choice.level,
|
||||||
|
station = choice.station,
|
||||||
|
isOwn = choice.isOwn,
|
||||||
|
fillTypes = fillTypes,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -243,6 +250,9 @@ function ADSmartPickup.findSupplyPickup(adEnv, driver, ingredients)
|
|||||||
markerName = choice.markerName,
|
markerName = choice.markerName,
|
||||||
group = choice.group,
|
group = choice.group,
|
||||||
level = choice.level,
|
level = choice.level,
|
||||||
|
station = choice.station,
|
||||||
|
isOwn = choice.isOwn,
|
||||||
|
fillTypes = choice.fillTypes,
|
||||||
fillType = need.fillType,
|
fillType = need.fillType,
|
||||||
need = need,
|
need = need,
|
||||||
}
|
}
|
||||||
@@ -277,11 +287,63 @@ function ADSmartPickup.choosePickup(adEnv, driver)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function describeChoice(choice)
|
local function describeChoice(choice)
|
||||||
|
local source = choice.isOwn and "eget lager" or "nabo-lager"
|
||||||
if choice.fillType == nil then
|
if choice.fillType == nil then
|
||||||
return string.format("%.0f l", choice.level)
|
return string.format("%.0f l, %s", choice.level, source)
|
||||||
end
|
end
|
||||||
local fillTypeName = g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(choice.fillType) or tostring(choice.fillType)
|
local fillTypeName = g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(choice.fillType) or tostring(choice.fillType)
|
||||||
return string.format("%s: kilde %.0f l, målet har %.0f/%.0f l", tostring(fillTypeName), choice.level, choice.need.level, choice.need.capacity)
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
task.update = function(self, dt)
|
||||||
|
local ok, shouldWait = pcall(ADSmartPickup.shouldWaitForLoading, self.vehicle)
|
||||||
|
waitingVehicle = ok and 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
|
end
|
||||||
|
|
||||||
local function installHook(adEnv)
|
local function installHook(adEnv)
|
||||||
@@ -292,27 +354,39 @@ local function installHook(adEnv)
|
|||||||
local ok, choice = pcall(ADSmartPickup.choosePickup, adEnv, driver)
|
local ok, choice = pcall(ADSmartPickup.choosePickup, adEnv, driver)
|
||||||
if not ok then
|
if not ok then
|
||||||
Logging.warning(ADSmartPickup.LOG_PREFIX .. "fejl i silo-opslag, bruger AutoDrives rotation: %s", tostring(choice))
|
Logging.warning(ADSmartPickup.LOG_PREFIX .. "fejl i silo-opslag, bruger AutoDrives rotation: %s", tostring(choice))
|
||||||
|
activePickups[driver] = nil
|
||||||
return originalGetNextPickup(self, driver, forcedSkip)
|
return originalGetNextPickup(self, driver, forcedSkip)
|
||||||
end
|
end
|
||||||
if choice == nil then
|
if choice == nil then
|
||||||
|
activePickups[driver] = nil
|
||||||
return originalGetNextPickup(self, driver, forcedSkip)
|
return originalGetNextPickup(self, driver, forcedSkip)
|
||||||
end
|
end
|
||||||
|
activePickups[driver] = {station = choice.station, fillTypes = choice.fillTypes}
|
||||||
|
|
||||||
-- hold AutoDrives egen rotations-tilstand konsistent med vores valg
|
-- hold AutoDrives egen rotations-tilstand konsistent med vores valg
|
||||||
self.pickups[choice.group] = self.pickups[choice.group] or {}
|
self.pickups[choice.group] = self.pickups[choice.group] or {}
|
||||||
self.pickups[choice.group].lastTarget = choice.markerIndex
|
self.pickups[choice.group].lastTarget = choice.markerIndex
|
||||||
self.pickups[choice.group].lastVehicle = driver
|
self.pickups[choice.group].lastVehicle = driver
|
||||||
|
|
||||||
Logging.info(ADSmartPickup.LOG_PREFIX .. "'%s' -> %s (%s)", tostring(driver:getName()), tostring(choice.markerName), describeChoice(choice))
|
logChoiceOnce(driver, choice)
|
||||||
return choice.markerIndex
|
return choice.markerIndex
|
||||||
end
|
end
|
||||||
|
|
||||||
|
installLoadWaitHook(adEnv)
|
||||||
|
|
||||||
Logging.info(ADSmartPickup.LOG_PREFIX .. "aktiv (mindst fyldte silo med varen; flere valgte varer = behovsstyret forsyning)")
|
Logging.info(ADSmartPickup.LOG_PREFIX .. "aktiv (mindst fyldte silo med varen; flere valgte varer = behovsstyret forsyning)")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Når AutoDrive stopper, får køretøjet hele ingredienslisten tilbage i varevalget
|
-- 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).
|
-- (AutoDrive gemmer valget i savegame, så listen overlever også en genindlæsning).
|
||||||
local function restoreStoppedVehicles()
|
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
|
for vehicle, state in pairs(supplyState) do
|
||||||
local stateModule = vehicle.ad ~= nil and vehicle.ad.stateModule or nil
|
local stateModule = vehicle.ad ~= nil and vehicle.ad.stateModule or nil
|
||||||
if stateModule == nil or not stateModule:isActive() then
|
if stateModule == nil or not stateModule:isActive() then
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<modDesc descVersion="107">
|
<modDesc descVersion="107">
|
||||||
<author>masterdraco</author>
|
<author>masterdraco</author>
|
||||||
<version>1.1.0.0</version>
|
<version>1.2.0.0</version>
|
||||||
<title>
|
<title>
|
||||||
<en>AD Smart Pickup</en>
|
<en>AD Smart Pickup</en>
|
||||||
</title>
|
</title>
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ Slå fra ved at vælge en anden enkelt vare.
|
|||||||
- Siloens eget lager rangeres før nabo-lån (udvidelses-siloer deler lager inden for `storageRadius`).
|
- Siloens eget lager rangeres før nabo-lån (udvidelses-siloer deler lager inden for `storageRadius`).
|
||||||
- Intet fund, intet behov eller fejl i et spil-API → AutoDrives originale rotation. Valg logges som `ADSmartPickup:` i log.txt.
|
- Intet fund, intet behov eller fejl i et spil-API → AutoDrives originale rotation. Valg logges som `ADSmartPickup:` i log.txt.
|
||||||
- Kræver i AutoDrive: "Brug mapper" + "Rotér mål: pålæsning" på køretøjet.
|
- Kræver i AutoDrive: "Brug mapper" + "Rotér mål: pålæsning" på køretøjet.
|
||||||
|
- **Står og læsser (v1.2):** med rotation + mapper afslutter AutoDrive normalt pålæsningen i det øjeblik
|
||||||
|
markøren nås (læsning skal være startet på vej ind, ellers kører den tom videre). Når moden har valgt
|
||||||
|
siloen, ser AutoDrive under `LoadAtDestinationTask.update` rotationen som slået fra for netop det
|
||||||
|
køretøj, så den holder ved markøren og læsser som uden rotation. Løber kilden tør, slippes grebet og
|
||||||
|
turen fortsætter med det der er i vognen.
|
||||||
|
|
||||||
Begrænsninger: AD læsser fuld vogn — er der mindre plads i målet end ét læs, venter traktoren ved
|
Begrænsninger: AD læsser fuld vogn — er der mindre plads i målet end ét læs, venter traktoren ved
|
||||||
aflæsning. Målet skal være en placeable med aflæsningstrigger (ikke en blandevogn).
|
aflæsning. Målet skal være en placeable med aflæsningstrigger (ikke en blandevogn).
|
||||||
|
|||||||
@@ -71,7 +71,15 @@ local function resetWorld()
|
|||||||
FS25_AutoDrive.ADMultipleTargetsManager.pickups = {}
|
FS25_AutoDrive.ADMultipleTargetsManager.pickups = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local RT_NONE, RT_ONLYPICKUP = 1, 2
|
||||||
FS25_AutoDrive = {
|
FS25_AutoDrive = {
|
||||||
|
AutoDrive = {RT_NONE = RT_NONE, getSetting = function(settingName) return settingName == "rotateTargets" and RT_ONLYPICKUP or "andet" end},
|
||||||
|
-- opgaven noterer hvad AutoDrive "ser" af indstillinger mens den opdaterer
|
||||||
|
LoadAtDestinationTask = {update = function(self)
|
||||||
|
self.seenRotate = FS25_AutoDrive.AutoDrive.getSetting("rotateTargets", self.vehicle)
|
||||||
|
self.seenOther = FS25_AutoDrive.AutoDrive.getSetting("useFolders", self.vehicle)
|
||||||
|
if self.shouldFail then error("task boom") end
|
||||||
|
end},
|
||||||
ADMultipleTargetsManager = {pickups = {}, getNextPickup = function() return "ORIGINAL" end},
|
ADMultipleTargetsManager = {pickups = {}, getNextPickup = function() return "ORIGINAL" end},
|
||||||
ADGraphManager = {
|
ADGraphManager = {
|
||||||
getMapMarkersInGroup = function(_, group)
|
getMapMarkersInGroup = function(_, group)
|
||||||
@@ -263,5 +271,63 @@ bareStation.owningPlaceable = {spec_productionPoint = {productionPoint = {storag
|
|||||||
feeder = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS, SILAGE})
|
feeder = makeSupplyDriver(markerByIndex(1), markerByIndex(10), {HAY, GRASS, SILAGE})
|
||||||
check("S10 lager læses fra produktionspunktet", manager:getNextPickup(feeder, false), 3)
|
check("S10 lager læses fra produktionspunktet", manager:getNextPickup(feeder, false), 3)
|
||||||
|
|
||||||
|
-- L1: moden valgte siloen -> AutoDrive skal stå og læsse (rotation skjules under opgaven)
|
||||||
|
resetWorld()
|
||||||
|
local loadLevels = {[SUGARBEET] = 5000}
|
||||||
|
addMarker(1, "Silo 1", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, loadLevels))
|
||||||
|
local loader = makeDriver(markers[1], {SUGARBEET})
|
||||||
|
manager:getNextPickup(loader, false)
|
||||||
|
local task = {vehicle = loader}
|
||||||
|
FS25_AutoDrive.LoadAtDestinationTask.update(task, 16)
|
||||||
|
check("L1 rotation skjult mens kilden har varen", task.seenRotate, RT_NONE)
|
||||||
|
check("L1 andre indstillinger uberørt", task.seenOther, "andet")
|
||||||
|
check("L1 rotation synlig igen efter opgaven", FS25_AutoDrive.AutoDrive.getSetting("rotateTargets", loader), RT_ONLYPICKUP)
|
||||||
|
|
||||||
|
-- L2: andre køretøjer (ikke sendt af moden) beholder AutoDrives rotation
|
||||||
|
local strangerTask = {vehicle = makeDriver(markers[1], {SUGARBEET})}
|
||||||
|
FS25_AutoDrive.LoadAtDestinationTask.update(strangerTask, 16)
|
||||||
|
check("L2 fremmed køretøj uberørt", strangerTask.seenRotate, RT_ONLYPICKUP)
|
||||||
|
|
||||||
|
-- L3: kilden løber tør -> grebet slippes, AutoDrive kører videre med det den har
|
||||||
|
loadLevels[SUGARBEET] = 0
|
||||||
|
FS25_AutoDrive.LoadAtDestinationTask.update(task, 16)
|
||||||
|
check("L3 tom kilde -> rotation synlig igen", task.seenRotate, RT_ONLYPICKUP)
|
||||||
|
|
||||||
|
-- L4: fejl i AutoDrives opgave må ikke efterlade rotationen skjult
|
||||||
|
loadLevels[SUGARBEET] = 5000
|
||||||
|
task.shouldFail = true
|
||||||
|
check("L4 fejl i opgaven sendes videre", pcall(FS25_AutoDrive.LoadAtDestinationTask.update, task, 16), false)
|
||||||
|
check("L4 rotation synlig efter fejl", FS25_AutoDrive.AutoDrive.getSetting("rotateTargets", loader), RT_ONLYPICKUP)
|
||||||
|
|
||||||
|
-- L5: AutoDrives original valgte (intet fund) -> ingen ventetid
|
||||||
|
task.shouldFail = false
|
||||||
|
loader.ad.stateModule.getSelectedFillTypes = function() return {WHEAT} end
|
||||||
|
manager:getNextPickup(loader, false)
|
||||||
|
FS25_AutoDrive.LoadAtDestinationTask.update(task, 16)
|
||||||
|
check("L5 AutoDrives eget valg -> rotation uberørt", task.seenRotate, RT_ONLYPICKUP)
|
||||||
|
|
||||||
|
-- L7: AD stopper -> grebet ryddes, næste opgave venter ikke på en gammel silo
|
||||||
|
resetWorld()
|
||||||
|
addMarker(1, "Silo 1", "Andet", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[HAY] = 5000}))
|
||||||
|
addMarker(2, "Blander", "Fabrik", 5000, 0)
|
||||||
|
table.insert(unloadStations, makeUnloadStation(201, 5000, 15, {[HAY] = 0}, {[HAY] = 1000000, [GRASS] = 1000000}, {}))
|
||||||
|
local stopper = makeSupplyDriver(markers[1], markers[2], {HAY, GRASS})
|
||||||
|
manager:getNextPickup(stopper, false)
|
||||||
|
local stopperTask = {vehicle = stopper}
|
||||||
|
FS25_AutoDrive.LoadAtDestinationTask.update(stopperTask, 16)
|
||||||
|
check("L7 venter mens AD kører", stopperTask.seenRotate, RT_NONE)
|
||||||
|
stopper.ad.stateModule.active = false
|
||||||
|
listener:update(16)
|
||||||
|
FS25_AutoDrive.LoadAtDestinationTask.update(stopperTask, 16)
|
||||||
|
check("L7 greb ryddet når AD stopper", stopperTask.seenRotate, RT_ONLYPICKUP)
|
||||||
|
|
||||||
|
-- L6: samme valg logges kun én gang
|
||||||
|
resetWorld()
|
||||||
|
addMarker(1, "Silo 1", "Siloer", 0, 0); table.insert(stations, makeStation(101, 0, 15, {[SUGARBEET] = 5000}, false, {[SUGARBEET] = 5000}))
|
||||||
|
local chatty = makeDriver(markers[1], {SUGARBEET})
|
||||||
|
manager:getNextPickup(chatty, false); manager:getNextPickup(chatty, false)
|
||||||
|
check("L6 gentaget valg logges én gang", #logLines, 1)
|
||||||
|
check("L6 loggen viser eget lager", logLines[1]:find("eget lager", 1, true) ~= nil, true)
|
||||||
|
|
||||||
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
||||||
os.exit(failures == 0 and 0 or 1)
|
os.exit(failures == 0 and 0 or 1)
|
||||||
|
|||||||
Reference in New Issue
Block a user