diff --git a/FS25_ADSmartPickup/adFieldJobs.lua b/FS25_ADSmartPickup/adFieldJobs.lua index f8ca234..a3d73fc 100644 --- a/FS25_ADSmartPickup/adFieldJobs.lua +++ b/FS25_ADSmartPickup/adFieldJobs.lua @@ -44,6 +44,28 @@ local function cropGone(stage, info) return (info.readyRatio or 0) < ADFieldModel.CUT_RATIO end +-- Ligger (x, z) inden for omridset? (lige-ulige-reglen; kanten tæller som inde) +local function insidePolygon(x, z, verts) + local inside, j = false, #verts + for i = 1, #verts do + local a, b = verts[i], verts[j] + if (a.z > z) ~= (b.z > z) and x <= (b.x - a.x) * (z - a.z) / (b.z - a.z) + a.x then inside = not inside end + j = i + end + return inside +end + +-- Efter genindlæsning: marken en CP-maskine står på, hvis markens aktuelle trin er maskinens rolle (ellers nil). +-- fields = {{id, stage, verts = {{x, z}}}} +function ADFieldJobs.resumeField(x, z, role, fields) + for _, field in ipairs(fields or {}) do + if #(field.verts or {}) >= 3 and ADFieldModel.roleFor(field.stage) == role and insidePolygon(x, z, field.verts) then + return field.id + end + end + return nil +end + -- Beslutning for én mark. assignments = sæt på marken ({phase, stage, helper}); kun sæt på det aktuelle -- trin tæller (en aflæsningsvogn på vej hjem fra høsten forstyrrer ikke kultiveringen). -- env = {raining = bool}: vejrfølsomme trin (høst/slå/vend/riv/hø) sendes ikke ud i regn. @@ -340,6 +362,12 @@ function ADFieldJobs.tick(adEnv, dt) -- lidt efter start, og indtil da ligner de maskiner ledige (in-game 26/9: kultivatoren blev taget midt -- i spillerens eget CP-job 45 s efter indlæsning) if ADFieldJobs.now < ADFieldJobs.STARTUP_GRACE_MS then return end + if not ADFieldJobs.resumedAfterLoad then + -- én gang, før noget nyt sendes ud: CP-maskiner der var midt i et job, fortsætter (og tæller som aktive) + ADFieldJobs.resumedAfterLoad = true + local okResume, errResume = pcall(ADFieldWork.resumeAfterLoad, adEnv) + if not okResume then Logging.warning(ADFieldJobs.LOG_PREFIX .. "genstart efter indlæsning fejlede: %s", tostring(errResume)) end + end local env = ADFieldJobs.environment() local slice = ADFieldJobs.tickCount % ADFieldJobs.PASSIVE_SLICES local stepped = 0 diff --git a/FS25_ADSmartPickup/adFieldWork.lua b/FS25_ADSmartPickup/adFieldWork.lua index 0ea5d93..c19f671 100644 --- a/FS25_ADSmartPickup/adFieldWork.lua +++ b/FS25_ADSmartPickup/adFieldWork.lua @@ -811,6 +811,15 @@ local function followWorking(adEnv, field, assignment) end if ADCpBridge.isWorking(vehicle) or isAdActive(vehicle) then assignment.idleSince = nil + assignment.seenWorking = true + return + end + -- genoptaget efter indlæsning: CP starter asynkront — vent til den kører, før "står stille" betyder færdig + if assignment.resumed and not assignment.seenWorking then + if now() - assignment.since > ADFieldWork.START_TIMEOUT_MS then + ADFieldWork.release(assignment) + fail(assignment.fieldId, string.format("'%s': Courseplay fortsatte ikke efter genindlæsning", ADRuns.vehicleLabel(vehicle))) + end return end assignment.idleSince = assignment.idleSince or now() @@ -827,6 +836,42 @@ local function followWorking(adEnv, field, assignment) finishAssignment(adEnv, assignment) end +-- Efter genindlæsning: spillet stopper alle arbejdere ved gem, og Courseplay genstarter dem ikke (in-game 27/9). +-- En maskine med CP-kursus og et husket waypoint (CP nulstiller det når kurset er kørt færdigt) var midt i et job: +-- start den fra sidste waypoint med CP's eget "fortsæt"-job (HUD'ens eget startpunkt røres ikke). Står den på en +-- mark hvis aktuelle trin er maskinens rolle, meldes den ind på marken igen, så den følges og trinnet afsluttes. +function ADFieldWork.resumeAfterLoad(adEnv) + local fields, stageById = {}, {} + for _, info in ipairs(ADFields.list()) do + local field = ADFieldJobs.getField(info.id) + if field ~= nil and ADFieldModel.isWorking(field) then + table.insert(fields, {id = info.id, stage = field.stage, verts = info.verts}) + stageById[info.id] = field.stage + end + end + for _, rig in ipairs(ADFieldWork.getRigs(adEnv)) do + local vehicle = rig.vehicle + local wasWorking = vehicle.startCpAtLastWp ~= nil and vehicle.hasCpCourse ~= nil and vehicle:hasCpCourse() + and vehicle.getCpLastRememberedWaypointIx ~= nil and vehicle:getCpLastRememberedWaypointIx() ~= nil + if wasWorking and not rig.busy and not rig.controlled then + local ok, started = pcall(vehicle.startCpAtLastWp, vehicle) + if not ok then + Logging.warning(ADFieldJobs.LOG_PREFIX .. "'%s' kunne ikke genstartes: %s", tostring(rig.label), tostring(started)) + elseif started then + local fieldId = ADFieldJobs.resumeField(rig.x, rig.z, rig.role, fields) + if fieldId ~= nil then + ADFieldJobs.state.assignments[rig.id] = {vehicleId = rig.id, vehicle = vehicle, fieldId = fieldId, role = rig.role, + stage = stageById[fieldId], helper = false, phase = "working", since = now(), width = rig.width, + groupSize = 1, leader = true, resumed = true} + log(fieldId, "'%s' fortsætter efter genindlæsning (Courseplay fra sidste waypoint)", tostring(rig.label)) + else + Logging.info(ADFieldJobs.LOG_PREFIX .. "'%s' genstartet fra sidste waypoint efter genindlæsning", tostring(rig.label)) + end + end + end + end +end + function ADFieldWork.follow(adEnv, assignment) local vehicle = assignment.vehicle -- Kun når spilleren selv KØRER (bevæger sig uden Courseplay/AutoDrive) slippes sættet — at sidde i diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index a13e63b..20cae25 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.36.7.0 + 1.37.0.0 <en>AD Smart Pickup</en> diff --git a/tests/test_adFieldJobs.lua b/tests/test_adFieldJobs.lua index 43d4c58..d9ab0f0 100644 --- a/tests/test_adFieldJobs.lua +++ b/tests/test_adFieldJobs.lua @@ -111,5 +111,22 @@ check("J14 uhøstet -> spol tilbage", J.decide(cultivatingField, {id = 16, marke check("J14 høstrest 30 % -> kultiver", J.decide(cultivatingField, {id = 16, markerId = 9, kind = "harvest", phase = "ready", readyRatio = 0.3}, {}, 0).action ~= "rewind", true) check("J14 stub -> kultiver", J.decide(cultivatingField, {id = 16, markerId = 9, kind = "bare", phase = "cut", readyRatio = 0}, {}, 0).action ~= "rewind", true) + +-- resumeField: efter genindlæsning stopper spillet alle arbejdere; en CP-maskine der står PÅ en mark hvis +-- aktuelle trin passer til maskinens rolle, meldes ind på den mark igen (in-game 27/9) +do + local square = {{x = 0, z = 0}, {x = 100, z = 0}, {x = 100, z = 100}, {x = 0, z = 100}} + local fields = { + {id = 90, stage = "collecting", verts = square}, + {id = 111, stage = "harvesting", verts = {{x = 200, z = 0}, {x = 300, z = 0}, {x = 300, z = 100}, {x = 200, z = 100}}}, + } + check("RF høster på F111", ADFieldJobs.resumeField(250, 50, "harvester", fields), 111) + check("RF læssevogn på F90", ADFieldJobs.resumeField(50, 50, "collector", fields), 90) + check("RF forkert rolle til trinnet -> nil", ADFieldJobs.resumeField(250, 50, "collector", fields), nil) + check("RF uden for alle marker -> nil", ADFieldJobs.resumeField(150, 50, "harvester", fields), nil) + check("RF mark uden omrids -> nil", ADFieldJobs.resumeField(50, 50, "collector", {{id = 1, stage = "collecting", verts = {}}}), nil) + check("RF på kanten tæller som indenfor", ADFieldJobs.resumeField(0.5, 50, "collector", fields), 90) +end + print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)