feat(markarbejde): manuel transport af brede høstere — flueben pr. høster i markflåden; moden beder spilleren køre den til marken og montere skærebordet (dialog + påmindelse hvert 5. min), overtager når den står på marken med passende skærebord og spilleren er steget ud (eller ved Start nu), tømmevogne først når høsten kører, ingen AD-parkering bagefter (v1.38.0.0)
This commit is contained in:
@@ -16,7 +16,8 @@ ADFieldJobs.tickCount = 0
|
||||
ADFieldJobs.MAX_PASSES = 2
|
||||
-- fleetMode: "all" = alle ledige maskiner (standard, "vælg og glem"), "ticked" = kun med flueben
|
||||
-- courseRecords: vehicleId -> post i kursus-registret (ADCourseCache)
|
||||
ADFieldJobs.state = {fields = {}, fleet = {}, assignments = {}, fleetMode = "all", courseRecords = {}}
|
||||
-- manual: {[vehicleId] = true} = høstere spilleren selv kører til marken (for brede til vejene)
|
||||
ADFieldJobs.state = {fields = {}, fleet = {}, assignments = {}, fleetMode = "all", courseRecords = {}, manual = {}}
|
||||
-- mark-id -> {mower = kursus, rake = kursus, <trin> = fælles multi-tool-kursus}: kopier (kun i hukommelsen;
|
||||
-- efter genindlæsning hentes slå-/rivekurset fra maskinen, som CP selv gemmer kurset på)
|
||||
ADFieldJobs.courses = {}
|
||||
@@ -26,7 +27,8 @@ ADFieldJobs.timer = 500
|
||||
ADFieldJobs.now = 0
|
||||
ADFieldJobs.loaded = false
|
||||
|
||||
ADFieldJobs.ACTIVE_PHASES = {driving = true, starting = true, waitingCourse = true, working = true}
|
||||
-- awaitingPlayer: høster med manuel transport, spilleren kører den til marken (tæller som i gang)
|
||||
ADFieldJobs.ACTIVE_PHASES = {driving = true, starting = true, waitingCourse = true, working = true, awaitingPlayer = true}
|
||||
|
||||
-- Andel græs i mellemhøjde (høstbart, men halv ydelse) der får en afbrudt slåning til at vente.
|
||||
ADFieldJobs.REGROW_RATIO = 0.1
|
||||
@@ -45,7 +47,7 @@ local function cropGone(stage, info)
|
||||
end
|
||||
|
||||
-- Ligger (x, z) inden for omridset? (lige-ulige-reglen; kanten tæller som inde)
|
||||
local function insidePolygon(x, z, verts)
|
||||
function ADFieldJobs.insidePolygon(x, z, verts)
|
||||
local inside, j = false, #verts
|
||||
for i = 1, #verts do
|
||||
local a, b = verts[i], verts[j]
|
||||
@@ -59,7 +61,7 @@ end
|
||||
-- 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
|
||||
if #(field.verts or {}) >= 3 and ADFieldModel.roleFor(field.stage) == role and ADFieldJobs.insidePolygon(x, z, field.verts) then
|
||||
return field.id
|
||||
end
|
||||
end
|
||||
@@ -104,7 +106,7 @@ function ADFieldJobs.decide(field, info, assignments, now, env)
|
||||
if not hasMarker then return {action = "wait", care = care} end
|
||||
return {action = "care", stage = care}
|
||||
end
|
||||
local active, finished, helpers = 0, 0, 0
|
||||
local active, finished, helpers, awaiting = 0, 0, 0, 0
|
||||
for _, assignment in ipairs(assignments) do
|
||||
if assignment.stage == nil or assignment.stage == field.stage then
|
||||
local isActive = ADFieldJobs.ACTIVE_PHASES[assignment.phase]
|
||||
@@ -112,6 +114,7 @@ function ADFieldJobs.decide(field, info, assignments, now, env)
|
||||
if isActive then helpers = helpers + 1 end
|
||||
else
|
||||
if isActive then active = active + 1 end
|
||||
if assignment.phase == "awaitingPlayer" then awaiting = awaiting + 1 end
|
||||
if assignment.phase == "finished" then finished = finished + 1 end
|
||||
end
|
||||
end
|
||||
@@ -138,7 +141,8 @@ function ADFieldJobs.decide(field, info, assignments, now, env)
|
||||
end
|
||||
-- hovedsættene arbejder: fyld op med hjælpere (aflæsningsvogne) en ad gangen
|
||||
local helperRole = ADFieldModel.helperFor(field.stage)
|
||||
if helperRole ~= nil and helpers < ADFieldModel.maxHelpers(field.stage, field.plan) and hasMarker and (field.retryAt or 0) <= now then
|
||||
-- tømmevogne først når en høster faktisk er i gang (ikke mens spilleren kører den derud)
|
||||
if helperRole ~= nil and active > awaiting and helpers < ADFieldModel.maxHelpers(field.stage, field.plan) and hasMarker and (field.retryAt or 0) <= now then
|
||||
return {action = "dispatch", role = helperRole, count = 1, helper = true}
|
||||
end
|
||||
return {action = "none"}
|
||||
@@ -182,6 +186,7 @@ function ADFieldJobs.load()
|
||||
ADFieldJobs.state.fields, ADFieldJobs.state.fleet = loaded.fields, loaded.fleet
|
||||
ADFieldJobs.state.fleetMode = loaded.fleetMode or "all"
|
||||
ADFieldJobs.state.courseRecords = loaded.courseRecords or {}
|
||||
ADFieldJobs.state.manual = loaded.manual or {}
|
||||
else
|
||||
Logging.warning(ADFieldJobs.LOG_PREFIX .. "marker kunne ikke indlæses: %s", tostring(loaded))
|
||||
end
|
||||
@@ -193,6 +198,20 @@ function ADFieldJobs.setPlan(id, changes)
|
||||
ADFieldJobs.save()
|
||||
end
|
||||
|
||||
-- "Start nu" på en mark hvor moden venter på spilleren (manuel transport): overtag uden at vente
|
||||
ADFieldJobs.takeoverRequested = {}
|
||||
function ADFieldJobs.requestTakeover(id)
|
||||
ADFieldJobs.takeoverRequested[id] = true
|
||||
end
|
||||
|
||||
-- Venter moden på at spilleren kører en høster til marken? (assignment eller nil)
|
||||
function ADFieldJobs.awaitingPlayer(fieldId)
|
||||
for _, assignment in pairs(ADFieldJobs.state.assignments) do
|
||||
if assignment.fieldId == fieldId and assignment.phase == "awaitingPlayer" then return assignment end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function ADFieldJobs.requestStart(id)
|
||||
ADFieldJobs.note(id, {startRequested = true})
|
||||
end
|
||||
@@ -212,6 +231,11 @@ function ADFieldJobs.setFleetMode(mode)
|
||||
ADFieldJobs.save()
|
||||
end
|
||||
|
||||
function ADFieldJobs.toggleManual(vehicleId)
|
||||
ADFieldJobs.state.manual = ADFieldFleet.toggle(ADFieldJobs.state.manual or {}, vehicleId)
|
||||
ADFieldJobs.save()
|
||||
end
|
||||
|
||||
function ADFieldJobs.toggleFleet(vehicleId)
|
||||
ADFieldJobs.state.fleet = ADFieldFleet.toggle(ADFieldJobs.state.fleet, vehicleId)
|
||||
ADFieldJobs.save()
|
||||
|
||||
@@ -73,6 +73,11 @@ function ADFieldStorage.save(state)
|
||||
xmlFile:setString(string.format("%s.fleet.vehicle(%d)#id", ADFieldStorage.ROOT, vehicleIndex), vehicleId)
|
||||
vehicleIndex = vehicleIndex + 1
|
||||
end
|
||||
local manualIndex = 0
|
||||
for vehicleId in pairs(state.manual or {}) do
|
||||
xmlFile:setString(string.format("%s.manual.vehicle(%d)#id", ADFieldStorage.ROOT, manualIndex), vehicleId)
|
||||
manualIndex = manualIndex + 1
|
||||
end
|
||||
local courseIndex = 0
|
||||
for _, record in pairs(state.courseRecords or {}) do
|
||||
ADFieldStorage.writeCourseRecord(xmlFile, string.format("%s.courses.course(%d)", ADFieldStorage.ROOT, courseIndex), record)
|
||||
@@ -84,7 +89,7 @@ function ADFieldStorage.save(state)
|
||||
end
|
||||
|
||||
function ADFieldStorage.load()
|
||||
local result = {fields = {}, fleet = {}, fleetMode = "all", courseRecords = {}}
|
||||
local result = {fields = {}, fleet = {}, fleetMode = "all", courseRecords = {}, manual = {}}
|
||||
local path = ADFieldStorage.getPath()
|
||||
if path == nil or not fileExists(path) then return result end
|
||||
local xmlFile = XMLFile.load("ADSmartPickupFields", path)
|
||||
@@ -98,6 +103,10 @@ function ADFieldStorage.load()
|
||||
local id = xmlFile:getString(key .. "#id")
|
||||
if id ~= nil and id ~= "" then result.fleet[id] = true end
|
||||
end)
|
||||
xmlFile:iterate(ADFieldStorage.ROOT .. ".manual.vehicle", function(_, key)
|
||||
local id = xmlFile:getString(key .. "#id")
|
||||
if id ~= nil and id ~= "" then result.manual[id] = true end
|
||||
end)
|
||||
xmlFile:iterate(ADFieldStorage.ROOT .. ".courses.course", function(_, key)
|
||||
local record = ADCourseCache ~= nil and ADCourseCache.newRecord(ADFieldStorage.readCourseRecord(xmlFile, key)) or nil
|
||||
if record ~= nil then result.courseRecords[record.vehicle] = record end
|
||||
|
||||
@@ -138,6 +138,8 @@ function ADFieldWork.getRigs(adEnv)
|
||||
busy = state.assignments[id] ~= nil or isAdActive(vehicle) or ADCpBridge.isWorking(vehicle)
|
||||
or (ADFieldSilos ~= nil and ADFieldSilos.isLoaned(vehicle)),
|
||||
controlled = isControlledByPlayer(vehicle),
|
||||
-- manuel transport: spilleren kører den til marken (for bred til vejene med skærebord)
|
||||
manual = (state.manual or {})[id] == true,
|
||||
width = ADCpBridge.getWorkWidth(vehicle), x = x, z = z,
|
||||
-- tom spreder kan flere ting (gødning og kalk): alle dens roller
|
||||
roles = specs.sprayRoles, filledRole = specs.filledRole, dumpable = specs.dumpable,
|
||||
@@ -300,7 +302,7 @@ end
|
||||
local function acceptFor(field, info, role)
|
||||
if role == "harvester" then
|
||||
local fruit = info.fruitIndex
|
||||
return function(candidate) return fruit ~= nil and candidate.fruits ~= nil and candidate.fruits[fruit] == true end
|
||||
return function(candidate) return ADManualTransport.acceptsHarvester(candidate, fruit) end
|
||||
elseif role == "seeder" then
|
||||
local fruitIndex = ADFieldWork.sowFruitIndex(field)
|
||||
return function(candidate) return candidate.hasSeeds and fruitIndex ~= nil and candidate.seedFruits[fruitIndex] == true end
|
||||
@@ -351,6 +353,19 @@ local function refillPlan(adEnv, fieldId, vehicle, role, x, z)
|
||||
return {fillType = fillType, empty = empty, refillId = refillId}
|
||||
end
|
||||
|
||||
-- Manuel transport: bed spilleren køre høsteren til marken (dialog første gang, ellers besked i hjørnet).
|
||||
local function askPlayer(field, info, assignment, withDialog)
|
||||
local text = string.format("F%s skal høstes: kør '%s' til %s, montér skærebordet og stig ud — så overtager Smart Pickup",
|
||||
tostring(field.id), ADRuns.vehicleLabel(assignment.vehicle), tostring(info.markerName or ("F" .. tostring(field.id))))
|
||||
assignment.remindedAt = now()
|
||||
log(field.id, "venter på spilleren: %s", text)
|
||||
if withDialog and InfoDialog ~= nil and InfoDialog.show ~= nil then
|
||||
pcall(InfoDialog.show, "Smart Pickup: " .. text)
|
||||
else
|
||||
ADFieldJobs.notify(text, true)
|
||||
end
|
||||
end
|
||||
|
||||
function ADFieldWork.dispatch(adEnv, field, info, decision)
|
||||
local role = decision.role
|
||||
local ok, reason = preflight(field, role)
|
||||
@@ -409,6 +424,13 @@ function ADFieldWork.dispatch(adEnv, field, info, decision)
|
||||
groupSize = decision.helper and 1 or count, lane = count > 1 and lanes[index] or nil, leader = index == 1,
|
||||
fillType = decision.helper and info.harvestFillType or nil,
|
||||
}
|
||||
if role == "harvester" and rig.manual and not assignment.helper then
|
||||
-- for bred til vejene: spilleren kører den selv til marken og monterer skærebordet
|
||||
assignment.phase, assignment.manual = "awaitingPlayer", true
|
||||
assignment.startX, assignment.startZ = rig.x, rig.z
|
||||
ADFieldJobs.state.assignments[rig.id] = assignment
|
||||
askPlayer(field, info, assignment, true)
|
||||
else
|
||||
local refill = refillPlan(adEnv, field.id, rig.vehicle, role, info.x, info.z)
|
||||
if refill ~= nil and refill.empty and refill.refillId == nil then
|
||||
-- tom og intet sted at fylde op: ud på marken ville den bare køre kurset uden at sprede
|
||||
@@ -431,6 +453,7 @@ function ADFieldWork.dispatch(adEnv, field, info, decision)
|
||||
assignment.lane ~= nil and string.format(", bane %d", assignment.lane) or "")
|
||||
end
|
||||
end
|
||||
end
|
||||
note(field.id, {reason = false})
|
||||
end
|
||||
|
||||
@@ -790,14 +813,53 @@ local function finishAssignment(adEnv, assignment)
|
||||
end
|
||||
log(assignment.fieldId, "%s '%s' færdig", assignment.role, ADRuns.vehicleLabel(vehicle))
|
||||
-- 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
|
||||
-- manuel transport: ALDRIG ud på vejen med AutoDrive (skærebordet sidder på) — spilleren henter den
|
||||
local park = not assignment.adHandlesEnd and not assignment.manual and parkMarkerId(adEnv, vehicle) or nil
|
||||
if park ~= nil then startAd(adEnv, vehicle, adEnv.AutoDrive.MODE_DRIVETO, park, nil) end
|
||||
if assignment.manual then
|
||||
ADFieldJobs.notify(string.format("F%s høstet — afmontér skærebordet og kør '%s' hjem", tostring(assignment.fieldId), ADRuns.vehicleLabel(vehicle)), true)
|
||||
end
|
||||
restoreToolOffset(assignment)
|
||||
assignment.phase = "finished"
|
||||
table.insert(ADFieldJobs.finished, assignment)
|
||||
ADFieldJobs.state.assignments[assignment.vehicleId] = nil
|
||||
end
|
||||
|
||||
local MANUAL_REASON_TEXT = {
|
||||
notThere = "kør den til marken",
|
||||
noHeader = "montér skærebordet",
|
||||
wrongHeader = "skærebordet kan ikke høste afgrøden — montér det rigtige",
|
||||
playerInside = "stig ud, så overtager Smart Pickup",
|
||||
}
|
||||
|
||||
-- Manuel transport: overtag når høsteren står på marken med passende skærebord og spilleren er steget ud
|
||||
-- (eller når spilleren trykker "Start nu"); påmind hvert 5. minut indtil da.
|
||||
local function followManual(adEnv, field, info, assignment)
|
||||
if field == nil or info == nil then return ADFieldWork.release(assignment) end
|
||||
local vehicle = assignment.vehicle
|
||||
local x, z = vehiclePosition(vehicle)
|
||||
local atField = #(info.verts or {}) >= 3 and ADFieldJobs.insidePolygon(x, z, info.verts)
|
||||
if not atField and info.markerId ~= nil then
|
||||
local wayPoint = adEnv.ADGraphManager:getWayPointById(info.markerId)
|
||||
atField = wayPoint ~= nil and math.sqrt((wayPoint.x - x) ^ 2 + (wayPoint.z - z) ^ 2) <= ADFieldWork.ARRIVE_DISTANCE
|
||||
end
|
||||
local forced = ADFieldJobs.takeoverRequested[field.id] == true
|
||||
ADFieldJobs.takeoverRequested[field.id] = nil
|
||||
local ok, why = ADManualTransport.readiness({atField = atField, fruits = ADCpBridge.getHarvestFruits(vehicle) or {},
|
||||
fruit = info.fruitIndex, controlled = isControlledByPlayer(vehicle), forced = forced})
|
||||
if ok then
|
||||
log(field.id, "overtager '%s'%s", ADRuns.vehicleLabel(vehicle), forced and " (Start nu)" or "")
|
||||
return beginWork(adEnv, field, info, assignment)
|
||||
end
|
||||
local reason = MANUAL_REASON_TEXT[why] or tostring(why)
|
||||
note(field.id, {reason = {text = string.format("venter på dig: '%s' — %s", ADRuns.vehicleLabel(vehicle), reason)}})
|
||||
if forced then
|
||||
ADFieldJobs.notify(string.format("F%s: kan ikke starte '%s' endnu — %s", tostring(field.id), ADRuns.vehicleLabel(vehicle), reason), true)
|
||||
elseif ADManualTransport.shouldRemind(assignment.remindedAt, now()) then
|
||||
askPlayer(field, info, assignment, false)
|
||||
end
|
||||
end
|
||||
|
||||
local function followWorking(adEnv, field, assignment)
|
||||
local vehicle = assignment.vehicle
|
||||
if assignment.role == "rake" and field.windrowOffset == nil and now() - assignment.since >= ADFieldWork.WINDROW_MEASURE_MS
|
||||
@@ -859,7 +921,7 @@ function ADFieldWork.resumeAfterLoad(adEnv)
|
||||
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}
|
||||
groupSize = 1, leader = true, resumed = true, manual = rig.manual == true}
|
||||
-- lager til læssevognens rest, som ved en normal start
|
||||
local assignment = ADFieldJobs.state.assignments[rig.id]
|
||||
local field, info = ADFieldJobs.getField(fieldId), ADFields.getInfo(fieldId)
|
||||
@@ -886,7 +948,7 @@ function ADFieldWork.follow(adEnv, assignment)
|
||||
-- førersædet og kigge må ikke slippe det (in-game 25/9).
|
||||
local steeredByAi = ADCpBridge.isWorking(vehicle) or isAdActive(vehicle) or assignment.phase == "starting"
|
||||
local speed = vehicle.getLastSpeed ~= nil and vehicle:getLastSpeed() or 0
|
||||
if isControlledByPlayer(vehicle) and not steeredByAi and speed > ADFieldWork.PLAYER_DRIVING_KMH then
|
||||
if assignment.phase ~= "awaitingPlayer" and isControlledByPlayer(vehicle) and not steeredByAi and speed > ADFieldWork.PLAYER_DRIVING_KMH then
|
||||
log(assignment.fieldId, "'%s' frigivet — spilleren kører det", ADRuns.vehicleLabel(vehicle))
|
||||
return ADFieldWork.release(assignment)
|
||||
end
|
||||
@@ -935,6 +997,8 @@ function ADFieldWork.follow(adEnv, assignment)
|
||||
end
|
||||
elseif assignment.phase == "working" then
|
||||
followWorking(adEnv, field, assignment)
|
||||
elseif assignment.phase == "awaitingPlayer" then
|
||||
followManual(adEnv, field, info, assignment)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
-- AD Manual Transport
|
||||
-- Brede høstere (mejetærsker med skærebord) kan ikke køre på vejene: spilleren kører dem til marken, monterer
|
||||
-- skærebordet og stiger ud; moden overtager derfra. Rene regler (testbare uden spillet); spil-delen ligger i
|
||||
-- adFieldWork.
|
||||
|
||||
ADManualTransport = {}
|
||||
|
||||
ADManualTransport.REMIND_MS = 300000 -- påmindelse hvert 5. minut, mens moden venter på spilleren
|
||||
|
||||
-- Må høsteren vælges til marken? Manuel: ja — skærebordet monteres først på marken (tjekkes ved overtagelse).
|
||||
-- Ellers skal det monterede skærebord kunne tage afgrøden.
|
||||
function ADManualTransport.acceptsHarvester(candidate, fruit)
|
||||
if fruit == nil then return false end
|
||||
if candidate.manual then return true end
|
||||
return candidate.fruits ~= nil and candidate.fruits[fruit] == true
|
||||
end
|
||||
|
||||
-- Klar til overtagelse? s = {atField, fruits = {[fruit] = true}, fruit, controlled, forced}.
|
||||
-- Returnerer true, eller false + grund: "notThere" | "noHeader" | "wrongHeader" | "playerInside".
|
||||
-- forced ("Start nu"): sted og spiller springes over, skærebordet tjekkes stadig.
|
||||
function ADManualTransport.readiness(s)
|
||||
if not s.forced and not s.atField then return false, "notThere" end
|
||||
local fruits = s.fruits or {}
|
||||
if next(fruits) == nil then return false, "noHeader" end
|
||||
if s.fruit == nil or not fruits[s.fruit] then return false, "wrongHeader" end
|
||||
if not s.forced and s.controlled then return false, "playerInside" end
|
||||
return true
|
||||
end
|
||||
|
||||
function ADManualTransport.shouldRemind(lastAt, now)
|
||||
return lastAt == nil or now - lastAt >= ADManualTransport.REMIND_MS
|
||||
end
|
||||
@@ -6,7 +6,7 @@
|
||||
-- Funktionerne lægges på SmartPickupFrame (indlæses efter SmartPickupFrame.lua).
|
||||
|
||||
-- Markflådens rækker (vehiclePrefab/ingredientPrefab) routes til onClickFieldRow.
|
||||
SmartPickupFrame.FIELD_ROW_KINDS = {fleetVehicle = true, fleetMode = true}
|
||||
SmartPickupFrame.FIELD_ROW_KINDS = {fleetVehicle = true, fleetMode = true, fleetManual = true}
|
||||
SmartPickupFrame.ROLE_KEYS = {
|
||||
mower = "spu_field_role_mower", tedder = "spu_field_role_tedder", rake = "spu_field_role_rake",
|
||||
collector = "spu_field_role_collector", harvester = "spu_field_role_harvester", unloader = "spu_field_role_unloader",
|
||||
@@ -238,6 +238,12 @@ function SmartPickupFrame:fillFleetRows(adEnv)
|
||||
local title = string.format(" %s — %s, %.1f m%s", tostring(rig.label), self:roleText(rig.role), rig.width or 0, self:rigDetails(rig))
|
||||
row:getDescendantByName("title"):setText(title)
|
||||
row:getDescendantByName("option"):setState(rig.enabled and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false)
|
||||
if rig.role == "harvester" then
|
||||
-- for bred til vejene med skærebord: spilleren kører den til marken, moden overtager derfra
|
||||
local option = self:addToggleRow("fleetManual", string.format(i18n:getText("spu_field_manualTransport"), tostring(rig.label)),
|
||||
{i18n:getText("ui_no"), i18n:getText("ui_yes")}, rig.manual)
|
||||
self.rowData[option] = {kind = "fleetManual", vehicleId = rig.id}
|
||||
end
|
||||
end
|
||||
if #rigs == 0 then
|
||||
self:addTextRow(" " .. i18n:getText("spu_field_noRigs"))
|
||||
@@ -266,7 +272,9 @@ function SmartPickupFrame:refreshFieldRows(adEnv)
|
||||
local selected = self.selectedFieldId ~= nil and ADFieldJobs.getField(self.selectedFieldId) or nil
|
||||
local info = self.selectedFieldId ~= nil and ADFields.getInfo(self.selectedFieldId) or nil
|
||||
local isWorking = selected ~= nil and ADFieldModel.isWorking(selected)
|
||||
self.fieldStartButtonInfo.text = self.i18n:getText(isWorking and "spu_field_button_stop" or "spu_field_button_start")
|
||||
local awaiting = self.selectedFieldId ~= nil and ADFieldJobs.awaitingPlayer(self.selectedFieldId) ~= nil
|
||||
self.fieldStartButtonInfo.text = self.i18n:getText(awaiting and "spu_field_button_takeover"
|
||||
or (isWorking and "spu_field_button_stop" or "spu_field_button_start"))
|
||||
self.fieldStartButtonInfo.disabled = selected == nil or info == nil or (not isWorking and info.markerId == nil)
|
||||
end
|
||||
|
||||
@@ -279,6 +287,8 @@ function SmartPickupFrame:onClickFieldRow(state, data)
|
||||
ADFieldJobs.toggleFleet(data.vehicleId)
|
||||
elseif data.kind == "fleetMode" then
|
||||
ADFieldJobs.setFleetMode(isOn and "all" or "ticked")
|
||||
elseif data.kind == "fleetManual" then
|
||||
if ((ADFieldJobs.state.manual or {})[data.vehicleId] == true) ~= isOn then ADFieldJobs.toggleManual(data.vehicleId) end
|
||||
end
|
||||
frame:requestRebuild()
|
||||
end)
|
||||
@@ -290,7 +300,12 @@ function SmartPickupFrame:onClickFieldStartStop()
|
||||
local id = frame.selectedFieldId
|
||||
if id == nil then return end
|
||||
local field = ADFieldJobs.getField(id)
|
||||
if ADFieldModel.isWorking(field) then
|
||||
if ADFieldJobs.awaitingPlayer(id) ~= nil then
|
||||
-- manuel transport: overtag nu (skærebordet tjekkes stadig)
|
||||
ADFieldJobs.requestTakeover(id)
|
||||
frame.statusOverride = string.format(frame.i18n:getText("spu_field_takeoverRequested"), id)
|
||||
frame.statusOverrideTicks = SmartPickupFrame.STATUS_OVERRIDE_TICKS
|
||||
elseif ADFieldModel.isWorking(field) then
|
||||
ADFieldJobs.stopField(frame.getAdEnv(), id)
|
||||
else
|
||||
local decision = ADFieldJobs.lastDecision[id]
|
||||
|
||||
@@ -59,6 +59,9 @@
|
||||
<text name="spu_month_upcomingOpen" text="F%d: %s — kan også sås næste måned"/>
|
||||
<text name="spu_month_upcomingLast" text="F%d: %s — OBS: sidste såmåned"/>
|
||||
<text name="spu_field_button_start" text="Start nu"/>
|
||||
<text name="spu_field_manualTransport" text=" Manuel transport af %s (for bred til vejene)"/>
|
||||
<text name="spu_field_button_takeover" text="Start nu"/>
|
||||
<text name="spu_field_takeoverRequested" text="F%d: overtager høsteren nu, hvis skærebordet passer"/>
|
||||
<text name="spu_field_button_stop" text="Stop mark"/>
|
||||
<text name="spu_field_noMarker" text="! mangler markør F%d"/>
|
||||
<text name="spu_field_stage_idle" text="Venter"/>
|
||||
|
||||
@@ -59,6 +59,9 @@
|
||||
<text name="spu_month_upcomingOpen" text="F%d: %s — can also be sown next month"/>
|
||||
<text name="spu_month_upcomingLast" text="F%d: %s — NOTE: last sowing month"/>
|
||||
<text name="spu_field_button_start" text="Start now"/>
|
||||
<text name="spu_field_manualTransport" text=" Manual transport of %s (too wide for roads)"/>
|
||||
<text name="spu_field_button_takeover" text="Start now"/>
|
||||
<text name="spu_field_takeoverRequested" text="F%d: taking over the harvester now if the header fits"/>
|
||||
<text name="spu_field_button_stop" text="Stop field"/>
|
||||
<text name="spu_field_noMarker" text="! missing marker F%d"/>
|
||||
<text name="spu_field_stage_idle" text="Waiting"/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<modDesc descVersion="107">
|
||||
<author>masterdraco</author>
|
||||
<version>1.37.2.0</version>
|
||||
<version>1.38.0.0</version>
|
||||
<title>
|
||||
<en>AD Smart Pickup</en>
|
||||
</title>
|
||||
@@ -38,6 +38,7 @@
|
||||
<sourceFile filename="adCpBridge.lua"/>
|
||||
<sourceFile filename="adCourseCache.lua"/>
|
||||
<sourceFile filename="adFieldStorage.lua"/>
|
||||
<sourceFile filename="adManualTransport.lua"/>
|
||||
<sourceFile filename="adFieldWork.lua"/>
|
||||
<sourceFile filename="adFieldSiloPlanner.lua"/>
|
||||
<sourceFile filename="adFieldSiloConfig.lua"/>
|
||||
|
||||
@@ -52,6 +52,9 @@ local withHarvester = {{phase = "working", stage = "harvesting", helper = false}
|
||||
local dHelper = J.decide(harvesting, harvestInfo, withHarvester, 0)
|
||||
check("J8 så aflæsningsvogn", dHelper.role, "unloader"); check("J8 hjælper", dHelper.helper, true)
|
||||
check("J8 vogn på plads -> intet", J.decide(harvesting, harvestInfo, {withHarvester[1], {phase = "working", stage = "harvesting", helper = true}}, 0).action, "none")
|
||||
-- manuel transport: høsteren venter på spilleren -> ingen høster mere og endnu ingen tømmevogn
|
||||
local awaiting = {{phase = "awaitingPlayer", stage = "harvesting", helper = false}}
|
||||
check("J8b venter på spilleren -> intet (ingen ny høster, ingen tømmevogn)", J.decide(harvesting, harvestInfo, awaiting, 0).action, "none")
|
||||
-- høsteren færdig, afgrøden væk (marken er nu bar) -> videre, selv om vognen stadig kører
|
||||
local afterInfo = {id = 50, markerId = 7, kind = "bare", readyRatio = 0}
|
||||
check("J8 høst færdig -> videre", J.decide(harvesting, afterInfo, {{phase = "finished", stage = "harvesting"}, {phase = "working", stage = "harvesting", helper = true}}, 0).action, "advance")
|
||||
|
||||
@@ -92,5 +92,11 @@ ADFieldStorage.save({fields = {}, fleet = {}, fleetMode = "all"})
|
||||
local old = ADFieldStorage.load()
|
||||
check("ST7 gammel fil: tomt register", next(old.courseRecords), nil)
|
||||
|
||||
-- ST8: manuel transport pr. maskine gemmes og indlæses; gammel fil uden -> tom
|
||||
disk = {}
|
||||
ADFieldStorage.save({fields = {}, fleet = {}, fleetMode = "all", manual = {vehicleCombine = true}})
|
||||
check("ST8 manuel transport indlæst", ADFieldStorage.load().manual.vehicleCombine, true)
|
||||
check("ST8 gammel fil: ingen manuelle", next(old.manual or {}), nil)
|
||||
|
||||
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
||||
os.exit(failures == 0 and 0 or 1)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
-- Kør: luajit tests/test_adManualTransport.lua (fra repo-roden)
|
||||
-- Manuel transport af brede høstere (in-game 27/9: mejetærskere med skærebord er for brede til vejene).
|
||||
dofile("FS25_ADSmartPickup/adManualTransport.lua")
|
||||
local M = ADManualTransport
|
||||
local failures = 0
|
||||
local function check(name, actual, expected)
|
||||
if actual == expected then print("OK " .. name) else
|
||||
failures = failures + 1
|
||||
print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual)))
|
||||
end
|
||||
end
|
||||
local WHEAT, BARLEY = 1, 2
|
||||
|
||||
-- valg af høster
|
||||
check("manuel høster uden skærebord kan vælges", M.acceptsHarvester({manual = true, fruits = {}}, WHEAT), true)
|
||||
check("automatisk høster uden skærebord kan ikke", M.acceptsHarvester({fruits = {}}, WHEAT), false)
|
||||
check("automatisk høster med passende skærebord", M.acceptsHarvester({fruits = {[WHEAT] = true}}, WHEAT), true)
|
||||
check("ukendt afgrøde -> ingen", M.acceptsHarvester({manual = true}, nil), false)
|
||||
|
||||
-- klar til overtagelse
|
||||
local header = {[WHEAT] = true}
|
||||
local function ready(s) local ok, why = M.readiness(s); return ok and "ok" or why end
|
||||
check("hjemme -> notThere", ready({atField = false, fruits = {}, fruit = WHEAT}), "notThere")
|
||||
check("på marken uden skærebord -> noHeader", ready({atField = true, fruits = {}, fruit = WHEAT}), "noHeader")
|
||||
check("forkert skærebord -> wrongHeader", ready({atField = true, fruits = {[BARLEY] = true}, fruit = WHEAT}), "wrongHeader")
|
||||
check("spilleren sidder i -> playerInside", ready({atField = true, fruits = header, fruit = WHEAT, controlled = true}), "playerInside")
|
||||
check("klar", ready({atField = true, fruits = header, fruit = WHEAT}), "ok")
|
||||
check("Start nu: spiller i og ikke på marken -> ok", ready({atField = false, fruits = header, fruit = WHEAT, controlled = true, forced = true}), "ok")
|
||||
check("Start nu tjekker stadig skærebordet", ready({atField = false, fruits = {}, fruit = WHEAT, forced = true}), "noHeader")
|
||||
|
||||
-- påmindelser
|
||||
check("første gang -> påmind", M.shouldRemind(nil, 1000), true)
|
||||
check("for tidligt", M.shouldRemind(1000, 1000 + M.REMIND_MS - 1), false)
|
||||
check("efter 5 min", M.shouldRemind(1000, 1000 + M.REMIND_MS), true)
|
||||
|
||||
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
||||
os.exit(failures == 0 and 0 or 1)
|
||||
Reference in New Issue
Block a user