diff --git a/FS25_ADSmartPickup/adCpBridge.lua b/FS25_ADSmartPickup/adCpBridge.lua
index 5e6e6c9..71fe46d 100644
--- a/FS25_ADSmartPickup/adCpBridge.lua
+++ b/FS25_ADSmartPickup/adCpBridge.lua
@@ -159,6 +159,23 @@ function ADCpBridge.isPlantableNow(fruitIndex)
return ADCpBridge.isPlantableIn(fruit, environment ~= nil and environment.currentPeriod or 0)
end
+-- Hvilken vare sættet bruger og kan få fyldt på af AutoDrive: frø (såmaskine) eller sprøjtens/sprederens
+-- nuværende fyld (gødning, kalk, sprøjtemiddel). nil = intet at fylde på (strigle, plov ...).
+function ADCpBridge.getRefillFillType(vehicle)
+ for _, object in ipairs(collect(vehicle)) do
+ if object.spec_sowingMachine ~= nil and g_fillTypeManager ~= nil then
+ return g_fillTypeManager:getFillTypeIndexByName("SEEDS")
+ end
+ end
+ for _, object in ipairs(collect(vehicle)) do
+ if object.spec_sprayer ~= nil and object.getSprayerFillUnitIndex ~= nil then
+ local ok, fillType = pcall(function() return object:getFillUnitFillType(object:getSprayerFillUnitIndex()) end)
+ if ok and fillType ~= nil and fillType ~= 0 then return fillType end
+ end
+ end
+ return nil
+end
+
-- Stop køretøjets AI-job (Courseplay/hjælper).
function ADCpBridge.stop(vehicle)
pcall(function()
diff --git a/FS25_ADSmartPickup/adFieldJobs.lua b/FS25_ADSmartPickup/adFieldJobs.lua
index c03233c..99d62e0 100644
--- a/FS25_ADSmartPickup/adFieldJobs.lua
+++ b/FS25_ADSmartPickup/adFieldJobs.lua
@@ -31,12 +31,19 @@ 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).
--- Svar: {action = "start"|"care"|"advance"|"dispatch"|"wait"|"none", role, count, helper, stage, care}.
-function ADFieldJobs.decide(field, info, assignments, now)
+-- env = {raining = bool}: vejrfølsomme trin (høst/slå/vend/riv/hø) sendes ikke ud i regn.
+-- Svar: {action = "start"|"care"|"advance"|"dispatch"|"wait"|"none", role, count, helper, stage, care, reason}.
+function ADFieldJobs.decide(field, info, assignments, now, env)
local hasMarker = info ~= nil and info.markerId ~= nil
+ local raining = env ~= nil and env.raining == true
+ local kind = field.kind or (info ~= nil and info.kind) or "mowable"
if field.stage == "ready" then
if not ADFieldModel.shouldStart(field) then return {action = "none"} end
if not hasMarker then return {action = "wait"} end
+ local first = ADFieldModel.workStages(field.plan, info.kind, info.needs)[1]
+ if raining and first ~= nil and ADFieldModel.isWeatherSensitive(first, field.plan, info.kind) then
+ return {action = "wait", reason = "rain"}
+ end
return {action = "start"}
end
if not ADFieldModel.isWorking(field) then
@@ -68,6 +75,9 @@ function ADFieldJobs.decide(field, info, assignments, now)
end
if not hasMarker then return {action = "wait"} end
if (field.retryAt or 0) > now then return {action = "none"} end
+ if raining and ADFieldModel.isWeatherSensitive(field.stage, field.plan, kind) then
+ return {action = "wait", reason = "rain"}
+ end
return {action = "dispatch", role = ADFieldModel.roleFor(field.stage), count = ADFieldModel.maxRigs(field.stage, field.plan), helper = false}
end
-- hovedsættene arbejder: fyld op med hjælpere (aflæsningsvogne) en ad gangen
@@ -166,6 +176,25 @@ function ADFieldJobs.stopField(adEnv, id)
ADFieldJobs.log(id, "stoppet fra menuen")
end
+-- Besked i spillet (højre side). critical = rød. Samme tekst vises kun én gang i træk.
+function ADFieldJobs.notify(text, critical)
+ if ADFieldJobs.lastNotice == text then return end
+ ADFieldJobs.lastNotice = text
+ pcall(function()
+ local kind = critical and FSBaseMission.INGAME_NOTIFICATION_CRITICAL or FSBaseMission.INGAME_NOTIFICATION_OK
+ g_currentMission:addIngameNotification(kind, "Smart Pickup: " .. text)
+ end)
+end
+
+-- Vejret lige nu (regn), læst en gang pr. tick.
+function ADFieldJobs.environment()
+ local ok, raining = pcall(function()
+ local weather = g_currentMission.environment.weather
+ return weather ~= nil and weather.getIsRaining ~= nil and weather:getIsRaining() or false
+ end)
+ return {raining = ok and raining == true}
+end
+
-- Seneste beslutning pr. mark (GUI viser pleje-behov og grunde).
ADFieldJobs.lastDecision = {}
@@ -174,7 +203,7 @@ local function stepField(adEnv, info)
ADFieldJobs.setField(field)
local assignments = ADFieldJobs.assignmentsFor(info.id)
for _, done in ipairs(finishedFor(info.id)) do table.insert(assignments, done) end
- local decision = ADFieldJobs.decide(field, info, assignments, ADFieldJobs.now)
+ local decision = ADFieldJobs.decide(field, info, assignments, ADFieldJobs.now, ADFieldJobs.environment())
ADFieldJobs.lastDecision[info.id] = decision
if decision.action == "start" then
local started = ADFieldModel.start(field, info.kind, info.needs)
@@ -192,7 +221,14 @@ local function stepField(adEnv, info)
ADFieldJobs.forgetFinished(info.id)
ADFieldWork.windDownHelpers(adEnv, info.id, field.stage)
ADFieldJobs.log(info.id, "%s -> %s", field.stage, nextField.stage)
+ if nextField.stage == "done" or (ADFieldModel.isCare(field.stage) and nextField.stage == "idle") then
+ ADFieldJobs.notify(string.format("F%d %s", info.id, g_i18n ~= nil and g_i18n:getText("spu_field_notice_done") or "done"), false)
+ end
ADFieldJobs.save()
+ elseif decision.action == "wait" and decision.reason == "rain" then
+ if field.reason == nil or field.reason.key ~= "spu_field_reason_rain" then
+ ADFieldJobs.note(info.id, {reason = {key = "spu_field_reason_rain"}})
+ end
elseif decision.action == "dispatch" then
ADFieldWork.dispatch(adEnv, field, info, decision)
end
diff --git a/FS25_ADSmartPickup/adFieldModel.lua b/FS25_ADSmartPickup/adFieldModel.lua
index d85a30a..897459f 100644
--- a/FS25_ADSmartPickup/adFieldModel.lua
+++ b/FS25_ADSmartPickup/adFieldModel.lua
@@ -178,7 +178,8 @@ function ADFieldModel.onReadiness(field, ratio)
local copy = copyField(field)
if (field.stage == "idle" or field.stage == "done") and ratio >= ADFieldModel.READY_RATIO then
copy.stage = "ready"
- elseif field.stage == "ready" and ratio < ADFieldModel.CUT_RATIO then
+ elseif field.stage == "ready" and ratio < ADFieldModel.CUT_RATIO and not field.startRequested then
+ -- (en Start nu-forespørgsel holder marken klar: bare marker har klar-andel 0)
copy.stage = "idle"
copy.startRequested = false
end
@@ -256,6 +257,14 @@ function ADFieldModel.stop(field)
return copy
end
+-- Vejrfølsomme trin: afgrøde og skår skal være tørre (høst, slåning, vending, rivning, hø-opsamling).
+-- Jordbehandling, såning, pleje og ensilage-opsamling kan køre i regn.
+local WEATHER_STAGES = {harvesting = true, mowing = true, tedding = true, raking = true}
+function ADFieldModel.isWeatherSensitive(stage, plan, kind)
+ if WEATHER_STAGES[stage] then return true end
+ return stage == "collecting" and ADFieldModel.product(plan, kind or "mowable") == "hay"
+end
+
-- Pleje under vækst: hvilket pleje-trin marken trænger til nu (eller nil). info fra adFields:
-- {kind, growing, fert, fertMax, lime, weed}. Højst MAX_CARE_RUNS pr. pleje pr. cyklus.
function ADFieldModel.careNeed(field, info)
diff --git a/FS25_ADSmartPickup/adFieldWork.lua b/FS25_ADSmartPickup/adFieldWork.lua
index c816242..52e3ce5 100644
--- a/FS25_ADSmartPickup/adFieldWork.lua
+++ b/FS25_ADSmartPickup/adFieldWork.lua
@@ -242,6 +242,7 @@ end
local function fail(fieldId, text)
note(fieldId, {retryAt = now() + ADFieldModel.RETRY_MS, reason = {text = tostring(text)}})
log(fieldId, "%s", tostring(text))
+ ADFieldJobs.notify(string.format("F%s: %s", tostring(fieldId), tostring(text)), true)
end
-- Hvad et sæt skal kunne ud over rollen (afgrøde, frø, ukrudtets størrelse).
@@ -339,6 +340,32 @@ local function prepareCollector(adEnv, field, info, assignment)
return true
end
+-- Påfyldning undervejs (såmaskine/sprøjte/spreder): AutoDrive LOAD med automatisk påfyldningsmål og
+-- hjælper = Courseplay. Når CP melder tom (onCpEmpty), kører AD til nærmeste påfyldning af varen, fylder
+-- op, kører tilbage til F-markøren og genstarter CP. Courseplay og AutoDrive er bygget til netop dette.
+local function prepareRefill(adEnv, field, info, assignment)
+ local vehicle = assignment.vehicle
+ local fillType = ADCpBridge.getRefillFillType(vehicle)
+ if fillType == nil then return end
+ local module = stateModule(vehicle)
+ local ok = pcall(function()
+ module:setMode(adEnv.AutoDrive.MODE_LOAD)
+ module:setFirstMarkerByWayPointId(info.markerId)
+ module:setSecondMarkerByWayPointId(info.markerId)
+ module:setAutomaticPickupTarget(true)
+ module:setFillType(fillType)
+ module:setStartHelper(true)
+ module.usedHelper = helperCp(adEnv)
+ enableParkAtJobFinished(vehicle)
+ module:raiseDirtyFlag()
+ end)
+ if ok then
+ assignment.adHandlesEnd = true
+ local name = g_fillTypeManager ~= nil and g_fillTypeManager:getFillTypeNameByIndex(fillType) or tostring(fillType)
+ log(field.id, "'%s' fyldes op med %s af AutoDrive når den er tom", ADRuns.vehicleLabel(vehicle), tostring(name))
+ end
+end
+
-- Aflæsningsvogn under høst: Courseplays combine-unloader med stationen fra silovalget.
local function beginUnloader(adEnv, field, info, assignment)
local vehicle = assignment.vehicle
@@ -407,7 +434,12 @@ local function beginWork(adEnv, field, info, assignment)
end
return
end
- if assignment.role == "collector" and not prepareCollector(adEnv, field, info, assignment) then return end
+ if assignment.role == "collector" then
+ if not prepareCollector(adEnv, field, info, assignment) then return end
+ assignment.adHandlesEnd = true
+ elseif assignment.role == "seeder" or assignment.role == "fertilizer" or assignment.role == "limer" or assignment.role == "weeder" then
+ prepareRefill(adEnv, field, info, assignment)
+ end
if assignment.role == "seeder" then
local okSeed, seedError = ADCpBridge.setSeedFruit(vehicle, ADFieldWork.sowFruitIndex(field))
if not okSeed then
@@ -462,8 +494,8 @@ end
local function finishAssignment(adEnv, assignment)
local vehicle = assignment.vehicle
log(assignment.fieldId, "%s '%s' færdig", assignment.role, ADRuns.vehicleLabel(vehicle))
- -- læssevognen parkeres af AutoDrive selv (onCpFinished med parkering sat)
- local park = assignment.role ~= "collector" and parkMarkerId(adEnv, vehicle) or nil
+ -- læssevogn/påfyldte sæt parkeres af AutoDrive selv (onCpFinished med parkering sat)
+ local park = not assignment.adHandlesEnd and parkMarkerId(adEnv, vehicle) or nil
if park ~= nil then startAd(adEnv, vehicle, adEnv.AutoDrive.MODE_DRIVETO, park, nil) end
restoreToolOffset(assignment)
assignment.phase = "finished"
diff --git a/FS25_ADSmartPickup/l10n/l10n_da.xml b/FS25_ADSmartPickup/l10n/l10n_da.xml
index 3d41ef3..7b47f7f 100644
--- a/FS25_ADSmartPickup/l10n/l10n_da.xml
+++ b/FS25_ADSmartPickup/l10n/l10n_da.xml
@@ -151,6 +151,8 @@
+
+
diff --git a/FS25_ADSmartPickup/l10n/l10n_en.xml b/FS25_ADSmartPickup/l10n/l10n_en.xml
index a3c2e1c..34fd83c 100644
--- a/FS25_ADSmartPickup/l10n/l10n_en.xml
+++ b/FS25_ADSmartPickup/l10n/l10n_en.xml
@@ -151,6 +151,8 @@
+
+
diff --git a/tests/test_adFieldJobs.lua b/tests/test_adFieldJobs.lua
index b3c5971..a56e6e0 100644
--- a/tests/test_adFieldJobs.lua
+++ b/tests/test_adFieldJobs.lua
@@ -75,5 +75,19 @@ check("J10 manuel -> intet, men behov vist", manualCare.action, "none"); check("
check("J10 Start nu -> pleje", J.decide(M.with(M.newField(60), {startRequested = true}), growingInfo, {}, 0).action, "care")
check("J10 uden markør -> vent", J.decide(autoField, {id = 60, kind = "harvest", growing = true, fert = 0, fertMax = 2}, {}, 0).action, "wait")
+-- J11: regn — vejrfølsomme trin sendes ikke ud (kørende sæt fortsætter); klargøring/pleje kører videre
+local rain = {raining = true}
+local dRain = J.decide(mowing, info, {}, 0, rain)
+check("J11 slåning venter i regn", dRain.action, "wait"); check("J11 grund", dRain.reason, "rain")
+check("J11 kørende slåmaskine fortsætter", J.decide(mowing, info, {{phase = "working"}}, 0, rain).action, "none")
+check("J11 høst venter", J.decide(harvesting, harvestInfo, {}, 0, rain).reason, "rain")
+check("J11 kultivering kører i regn", J.decide(cultivating, afterInfo, {}, 0, rain).role, "cultivator")
+check("J11 start af høst venter", J.decide(M.with(M.onReadiness(M.newField(51), 1), {startRequested = true, kind = "harvest"}), harvestInfo, {}, 0, rain).reason, "rain")
+local silageCollect = M.advance(mowing)
+check("J11 ensilage-opsamling kører i regn", J.decide(silageCollect, info, {}, 0, rain).action, "dispatch")
+local hayCollect = M.with(silageCollect, {plan = {product = "hay"}})
+check("J11 hø-opsamling venter", J.decide(hayCollect, info, {}, 0, rain).reason, "rain")
+check("J11 tørvejr", J.decide(mowing, info, {}, 0, {raining = false}).action, "dispatch")
+
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
os.exit(failures == 0 and 0 or 1)
diff --git a/tests/test_adFieldModel.lua b/tests/test_adFieldModel.lua
index ebaaebe..219df64 100644
--- a/tests/test_adFieldModel.lua
+++ b/tests/test_adFieldModel.lua
@@ -227,5 +227,12 @@ check("M17 etape 1-post: hø", legacy.plan.product, "hay")
check("M17 etape 1-post: standard kalk til", legacy.plan.lime, true)
check("M17 etape 1-post: 1 sæt", legacy.plan.rigs, 1)
+-- M18: Start nu på en bar mark (klar-andel 0) må ikke nulstilles af vækst-reglen før den starter
+local bare = M.with(M.forceReady(M.newField(70)), {startRequested = true})
+check("M18 bar mark forbliver klar", M.onReadiness(bare, 0).stage, "ready")
+check("M18 uden forespørgsel -> venter", M.onReadiness(M.forceReady(M.newField(71)), 0).stage, "idle")
+check("M18 vejrfølsom: høst", M.isWeatherSensitive("harvesting", {}, "harvest"), true)
+check("M18 vejrfølsom: kultivering ikke", M.isWeatherSensitive("cultivating", {}, "harvest"), false)
+
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
os.exit(failures == 0 and 0 or 1)