fix: slut-review — pcall om start/dialoger, Auto-varer fra aktivt AD-mål, nil-guards, README

This commit is contained in:
2026-09-22 23:35:47 +02:00
parent b74d1325c6
commit 856b949749
7 changed files with 99 additions and 27 deletions
+3
View File
@@ -53,6 +53,8 @@ function ADLoadSwap.findDumpMarker(adEnv, vehicle, fillType, rest, homeWayPointI
local station = ADSmartPickup.getUnloadStationAtWayPointId(adEnv, marker.id, farmId)
if station ~= nil and isSilo(station, homeStation) and acceptsRest(station, fillType, rest, farmId) then
local wayPoint = adEnv.ADGraphManager:getWayPointById(marker.id)
-- En markør uden waypoint (stale AD-graf) skal springes over, ikke crashe hele opslaget.
if wayPoint ~= nil then
local dx, dz = wayPoint.x - x, wayPoint.z - z
table.insert(candidates, {
marker = marker,
@@ -61,6 +63,7 @@ function ADLoadSwap.findDumpMarker(adEnv, vehicle, fillType, rest, homeWayPointI
})
end
end
end
table.sort(candidates, function(a, b)
if a.hasFillType ~= b.hasFillType then
return a.hasFillType
+29 -5
View File
@@ -112,7 +112,20 @@ end
-- findSupplyPickup selv til de ønskede produktioners inputs bagefter.
function ADRunsController.getTargetFillTypes(adEnv, run, vehicle)
local farmId = ADSmartPickup.getFarmId(vehicle)
local target = run.targetWayPointId ~= nil and ADSmartPickup.getUnloadStationAtWayPointId(adEnv, run.targetWayPointId, farmId) or nil
-- En kørende traktor følger AD's eget mål (stateModule:getSecondMarker(), det samme
-- choosePickup/findSupplyPickup bruger) indtil næste Start — IKKE run'ets gemte
-- targetWayPointId. Redigerer brugeren run'ets Mål mens tildelte traktorer kører,
-- skal Auto-listen derfor følge det LIVE mål, ellers kan den blive tom og fejlagtigt
-- smide traktoren over i mappe-logikken midt i turen.
local wayPointId = run.targetWayPointId
local stateModule = vehicle.ad ~= nil and vehicle.ad.stateModule or nil
if stateModule ~= nil and stateModule.isActive ~= nil and stateModule:isActive() and stateModule.getSecondMarker ~= nil then
local secondMarker = stateModule:getSecondMarker()
if secondMarker ~= nil and secondMarker.id ~= nil then
wayPointId = secondMarker.id
end
end
local target = wayPointId ~= nil and ADSmartPickup.getUnloadStationAtWayPointId(adEnv, wayPointId, farmId) or nil
if target == nil then
return {}
end
@@ -251,6 +264,9 @@ local function findNearestLoadMarker(adEnv, vehicle, farmId)
for _, marker in pairs(adEnv.ADGraphManager:getMapMarkers()) do
if ADSmartPickup.getLoadStationAtWayPointId(adEnv, marker.id, farmId) ~= nil then
local wayPoint = adEnv.ADGraphManager:getWayPointById(marker.id)
-- En stale markør uden waypoint skal springes over — ellers fejler Start med "error"
-- for ALLE tildelte traktorer, ikke kun den der ramte den ugyldige markør.
if wayPoint ~= nil then
local dx, dz = wayPoint.x - x, wayPoint.z - z
local distance = dx * dx + dz * dz
if distance < bestDistance then
@@ -258,6 +274,7 @@ local function findNearestLoadMarker(adEnv, vehicle, farmId)
end
end
end
end
return best
end
@@ -284,6 +301,16 @@ local function isVehicleActive(vehicle)
return stateModule ~= nil and stateModule.isActive ~= nil and stateModule:isActive()
end
-- Anvend run-tilstand OG start traktoren i ét pcall'et kald: kaster vehicle:startAutoDrive()
-- (fx AD's egen validering), skal den ene traktor fejle uden at hele startRun-loopet
-- afbrydes — ellers når de øvrige tildelte traktorer aldrig at blive startet.
local function applyAndStart(adEnv, run, vehicle, ingredients)
applyRunToVehicle(adEnv, run, vehicle, ingredients)
if not isVehicleActive(vehicle) then
vehicle:startAutoDrive()
end
end
-- Pr. tildelt traktor: validér, skriv AD-tilstand, start. ✖-traktorer springes over.
function ADRunsController.startRun(adEnv, runId)
local run = ADRuns.getRun(ADRunsController.store, runId)
@@ -299,11 +326,8 @@ function ADRunsController.startRun(adEnv, runId)
local result = {vehicle = vehicle, ok = validation.ok, reason = validation.reason}
if validation.ok then
local ingredients = ADRunsController.getRunIngredients(adEnv, run, vehicle)
local ok, err = pcall(applyRunToVehicle, adEnv, run, vehicle, ingredients)
local ok, err = pcall(applyAndStart, adEnv, run, vehicle, ingredients)
if ok then
if not isVehicleActive(vehicle) then
vehicle:startAutoDrive()
end
Logging.info(ADRunsController.LOG_PREFIX .. "run '%s': '%s' startet mod markør %s", tostring(run.name), tostring(vehicle:getName()), tostring(run.targetWayPointId))
else
result.ok, result.reason = false, "error"
+11 -4
View File
@@ -424,9 +424,13 @@ function SmartPickupFrame:onClickNew()
end
function SmartPickupFrame:onClickRename()
local run = self:getRun()
-- Dialogen kan kaste (fx manglende i18n-nøgle) — fang det som resten af GUI'et, i stedet
-- for at lade fejlen undslippe klik-callbacken uden statuslinje-besked.
self:safe(function(frame)
local run = frame:getRun()
if run == nil then return end
TextInputDialog.show(SmartPickupFrame.onRenameResult, self, run.name, self.i18n:getText("spu_renameTitle"), nil, 30, g_i18n:getText("button_ok"))
TextInputDialog.show(SmartPickupFrame.onRenameResult, frame, run.name, frame.i18n:getText("spu_renameTitle"), nil, 30, g_i18n:getText("button_ok"))
end)
end
function SmartPickupFrame:onRenameResult(value, clickOk)
@@ -440,9 +444,12 @@ function SmartPickupFrame:onRenameResult(value, clickOk)
end
function SmartPickupFrame:onClickDelete()
local run = self:getRun()
-- Samme begrundelse som onClickRename: dialog-opkaldet skal ikke kunne undslippe uden om safe().
self:safe(function(frame)
local run = frame:getRun()
if run == nil then return end
YesNoDialog.show(SmartPickupFrame.onDeleteResult, self, string.format(self.i18n:getText("spu_deleteConfirm"), run.name), self.i18n:getText("spu_button_delete"))
YesNoDialog.show(SmartPickupFrame.onDeleteResult, frame, string.format(frame.i18n:getText("spu_deleteConfirm"), run.name), frame.i18n:getText("spu_button_delete"))
end)
end
function SmartPickupFrame:onDeleteResult(yes)
-1
View File
@@ -22,7 +22,6 @@
<text name="spu_vehicle_tooltip" text="Ja = traktoren er med i dette run."/>
<text name="spu_waitPools" text="Wait-puljer"/>
<text name="spu_waitPoolRow" text="%s — %d punkter — %d optaget%s"/>
<text name="spu_status" text="Status"/>
<text name="spu_status_running" text="Kører (%d/%d)"/>
<text name="spu_status_stopped" text="Stoppet (%d traktorer)"/>
<text name="spu_status_started" text="Startet %d, sprunget over %d"/>
-1
View File
@@ -22,7 +22,6 @@
<text name="spu_vehicle_tooltip" text="Yes = the tractor belongs to this run."/>
<text name="spu_waitPools" text="Wait pools"/>
<text name="spu_waitPoolRow" text="%s — %d points — %d occupied%s"/>
<text name="spu_status" text="Status"/>
<text name="spu_status_running" text="Running (%d/%d)"/>
<text name="spu_status_stopped" text="Stopped (%d tractors)"/>
<text name="spu_status_started" text="Started %d, skipped %d"/>
+7 -4
View File
@@ -97,7 +97,9 @@ startes/stoppes samlet for alle traktorer i det.
- **Ture** — loop-tælleren (0–99, 0 = uendeligt), som normalt i AutoDrives Pickup&Deliver.
- **Traktorer** — rækker med Ja/Nej (er traktoren med i run'et), kapacitet og et ✔/✖-ikon. En
**✖-traktor springes over ved Start**, med en grund vist ved siden af (fx "ingen vogn", "vognen
kan ikke bære run'ets varer", "for langt fra vejnettet" eller "intet mål").
kan ikke bære run'ets varer", "for langt fra vejnettet" eller "intet mål"). Sættes en traktor til
Nej (fjernet fra run'et), rører det IKKE dens AutoDrive-vareliste — run'ets varer forbliver valgt
i traktorens AD-vareliste (synlig/redigerbar i AD's eget HUD) indtil de ændres dér manuelt.
**Start/Stop:** "Start run" sætter AutoDrive op på hver ✔-traktor: mode Pickup&Deliver, målmarkør,
nærmeste pålæsningsmarkør som start, run'ets varer valgt, run'ets tur-tæller, mapper ("Brug mapper")
@@ -107,8 +109,8 @@ traktorens hidtidige manuelle AutoDrive-indstillinger.** "Stop run" stopper Auto
i run'et (medlemskabet i run'et bevares, så det kan startes igen). Traktorer der ikke er del af noget
run, kører som før — bortset fra at "Brug mapper" kan være blevet slået til globalt af et run.
**Wait-puljer-fanen** viser hver Wait-mappe skrivebeskyttet: antal punkter og hvilken traktor der
holder hvor.
**Wait-puljer-afsnittet** nederst på siden viser hver Wait-mappe skrivebeskyttet: antal punkter og
hvilken traktor der holder hvor.
**Gemning:** Runs gemmes i `savegameN/ADSmartPickup_runs.xml` (ved gem og ved Start/Stop). De
**genstartes ikke automatisk** ved indlæsning af et savegame — kør "Start run" igen efter load, hvis
@@ -144,7 +146,8 @@ aldrig fejlagtigt bliver opfattet som et fuldt mål.
- Wrapper `ADMultipleTargetsManager.getNextPickup` (via den globale `FS25_AutoDrive`-tabel); rører ikke AutoDrive-zip'en.
- 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.
- Kræver i AutoDrive: "Brug mapper" + "Rotér mål: pålæsning" på køretøjet.
- Kræver i AutoDrive: "Brug mapper" (global AutoDrive-indstilling, gælder hele spillet) +
"Rotér mål: pålæsning" på køretøjet (pr. køretøj).
- **Markør ↔ silo (v1.3):** markøren er dér hvor trækkerens næse stopper; vognen holder et vogntog
bagved. Markørens silo er derfor nærmeste trigger **bag næsen** set i ankomstretningen (forrige
waypoint), højst 2 m foran. I tætte rækker (15 m mellem siloerne) er nabosiloens trigger ellers
+37
View File
@@ -78,6 +78,8 @@ local function makeVehicle(uniqueId, name, carriable, capacity, x, z)
stateModule.raiseDirtyFlag = function(self) self.dirtyCount = self.dirtyCount + 1 end
stateModule.setLoopsDone = function(self, n) self.loopsDone = n end
stateModule.isActive = function(self) return self.active end
-- Bruges af C13: getTargetFillTypes skal følge AD's LIVE mål (mock'er AD's eget getSecondMarker).
stateModule.getSecondMarker = function(self) return self.secondMarkerId ~= nil and {id = self.secondMarkerId} or nil end
local vehicle = {
uniqueId = uniqueId, farmId = 1, rootNode = uniqueId,
carriable = carriable, trailers = {level = 0, capacity = capacity},
@@ -253,6 +255,41 @@ check("C11 global rotateTargets urørt", adEnv.AutoDrive.settings.rotateTargets.
check("C11 useFolders global stadig skrevet", adEnv.AutoDrive.settings.useFolders.current, 2)
check("C11 køretøjet startet", noSettings.started, 1)
-- C12: startAutoDrive() kaster for én traktor -> pcall'et applyAndStart fanger fejlen,
-- resultatet markeres ok=false/reason="error", og de ØVRIGE traktorer i run'et startes stadig
-- (fejlen må ikke undslippe startRun-loopet).
buildWorld()
local brokenRun = ADRunsController.createRun("Broken Start")
ADRunsController.updateRun(brokenRun.id, {targetWayPointId = 94, loops = 2})
local broken = makeVehicle("vehicleBroken", "Fejl-traktor", {STRAW, TMR}, 500000, 100, 0)
broken.startAutoDrive = function() error("boom") end
local normal = makeVehicle("vehicleNormal", "Normal-traktor", {STRAW, TMR}, 500000, 200, 0)
ADRunsController.assignVehicle(brokenRun.id, broken)
ADRunsController.assignVehicle(brokenRun.id, normal)
local c12Results = ADRunsController.startRun(adEnv, brokenRun.id)
check("C12 to resultater", #c12Results, 2)
check("C12 fejl-traktor ok=false", c12Results[1].ok, false)
check("C12 fejl-traktor reason=error", c12Results[1].reason, "error")
check("C12 fejl-traktor ikke startet", broken.started, nil)
check("C12 normal-traktor ok=true", c12Results[2].ok, true)
check("C12 normal-traktor startet", normal.started, 1)
-- C13: Auto-listen følger traktorens LIVE AD-mål (stateModule:getSecondMarker()) mens den kører,
-- IKKE run'ets gemte targetWayPointId — ellers afviger listen fra det choosePickup/findSupplyPickup
-- faktisk bruger, hvis brugeren redigerer run'ets Mål mens traktoren kører.
addMarker(95, "Alt Cow Food", "Cow Food", 5300, 0)
unloadAt[95] = {accepts = {[STRAW] = 1000}}
local liveRun = ADRunsController.createRun("Live Target")
ADRunsController.updateRun(liveRun.id, {targetWayPointId = 94})
local liveRunData = ADRuns.getRun(ADRunsController.store, liveRun.id)
local liveVehicle = makeVehicle("vehicleLive", "Live-traktor", {STRAW, TMR}, 500000, 300, 0)
check("C13 inaktiv: auto-liste fra run'ets mål (94)", table.concat(ADRunsController.getRunIngredients(adEnv, liveRunData, liveVehicle), ","), STRAW .. "," .. TMR)
liveVehicle.ad.stateModule.active = true
liveVehicle.ad.stateModule.secondMarkerId = 95
check("C13 aktiv: auto-liste fra AD's live mål (95)", table.concat(ADRunsController.getRunIngredients(adEnv, liveRunData, liveVehicle), ","), tostring(STRAW))
liveVehicle.ad.stateModule.active = false
liveVehicle.ad.stateModule.secondMarkerId = nil
-- C10: målets accepterede varer (til afkrydsning i GUI'et) + køretøjs-label
buildWorld()
local labelA = makeVehicle("vehicleA", "Fastrac A", {STRAW, TMR}, 1000000)