From 9be60d4b1c264353514c2d965c2af8dbd63f7ebf Mon Sep 17 00:00:00 2001 From: masterdraco Date: Fri, 25 Sep 2026 23:51:32 +0200 Subject: [PATCH] =?UTF-8?q?feat(marker):=20markfl=C3=A5de=20=E2=80=94=20al?= =?UTF-8?q?le=20markroller,=20accept-filter=20(afgr=C3=B8de/ukrudt),=20pic?= =?UTF-8?q?kMany=20til=20multi-tool?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FS25_ADSmartPickup/adFieldFleet.lua | 86 ++++++++++++++++++++++------- tests/test_adFieldFleet.lua | 35 +++++++++++- 2 files changed, 99 insertions(+), 22 deletions(-) diff --git a/FS25_ADSmartPickup/adFieldFleet.lua b/FS25_ADSmartPickup/adFieldFleet.lua index deb7e05..6dd9d24 100644 --- a/FS25_ADSmartPickup/adFieldFleet.lua +++ b/FS25_ADSmartPickup/adFieldFleet.lua @@ -1,48 +1,92 @@ -- AD Field Fleet --- Markflåden som ren logik: hvilken rolle et sæt har ud fra redskabernes specs, og hvilket ledigt sæt --- der sendes til en mark. Kun sæt med flueben bruges; et sæt spilleren sidder i, røres aldrig. +-- Markflåden som ren logik: hvilken rolle et sæt har ud fra redskabernes specs, og hvilke ledige sæt der +-- sendes til en mark. Kun sæt med flueben bruges; et sæt spilleren sidder i, røres aldrig. ADFieldFleet = {} --- Rækkefølge = prioritet når et køretøj har flere (læssevogn med egen pickup m.m.) +-- Rækkefølge = prioritet når et køretøj har flere (såmaskine med gødning er såmaskine; traktor med plov er +-- plov, ikke aflæsningsvogn). sprayRole kommer fra sprøjtens/sprederens fyld (gødning/kalk/ukrudt). ADFieldFleet.ROLE_SPECS = { + {role = "harvester", spec = "spec_combine"}, {role = "collector", spec = "spec_forageWagon"}, {role = "rake", spec = "spec_windrower"}, {role = "tedder", spec = "spec_tedder"}, {role = "mower", spec = "spec_mower"}, + {role = "seeder", spec = "spec_sowingMachine"}, + {role = "roller", spec = "spec_roller"}, + {role = "weeder", spec = "spec_weeder"}, + {role = "sprayRole"}, + {role = "mulcher", spec = "spec_mulcher"}, + {role = "cultivator", spec = "spec_cultivator"}, + {role = "plow", spec = "spec_plow"}, + {role = "unloader", spec = "canUnload"}, } function ADFieldFleet.roleFromSpecs(specs) for _, entry in ipairs(ADFieldFleet.ROLE_SPECS) do - if specs[entry.spec] then return entry.role end + if entry.role == "sprayRole" then + if specs.sprayRole ~= nil then return specs.sprayRole end + elseif specs[entry.spec] then + return entry.role + end end return nil end --- Nærmeste brugbare sæt til rollen. Grund når intet passer: noRig (ingen med flueben og rollen), --- allBusy (alle optaget/i brug af spilleren), tooWide/tooNarrow (bredden, med tal fra den første). -function ADFieldFleet.pick(candidates, role, x, z, mowWidth) - local best, bestDistance = nil, math.huge - local sawRole, widthReason, widthArgs = false, nil, nil +-- Kandidater med rollen, sorteret nærmest først, delt i brugbare og afviste (med første grund). +local function sortCandidates(candidates, role, x, z, requiredWidth, accept) + local usable, sawRole, sawAccepted = {}, false, false + local widthReason, widthArgs = nil, nil for _, candidate in ipairs(candidates) do if candidate.role == role and candidate.enabled then sawRole = true - if not candidate.busy and not candidate.controlled then - local ok, reason, args = ADFieldModel.checkWidth(role, candidate.width or 0, mowWidth) - if ok then - local dx, dz = candidate.x - x, candidate.z - z - local distance = dx * dx + dz * dz - if distance < bestDistance then best, bestDistance = candidate, distance end - elseif widthReason == nil then - widthReason, widthArgs = reason, args + if accept == nil or accept(candidate) then + sawAccepted = true + if not candidate.busy and not candidate.controlled then + local ok, reason, args = ADFieldModel.checkWidth(role, candidate.width or 0, requiredWidth) + if ok then + local dx, dz = candidate.x - x, candidate.z - z + table.insert(usable, {candidate = candidate, distance = dx * dx + dz * dz}) + elseif widthReason == nil then + widthReason, widthArgs = reason, args + end end end end end - if best ~= nil then return best end - if not sawRole then return nil, "noRig" end - if widthReason ~= nil then return nil, widthReason, widthArgs end - return nil, "allBusy" + table.sort(usable, function(a, b) return a.distance < b.distance end) + local reason, args = nil, nil + if not sawRole then + reason = "noRig" + elseif not sawAccepted then + reason = "cannotDo" + elseif widthReason ~= nil then + reason, args = widthReason, widthArgs + else + reason = "allBusy" + end + return usable, reason, args +end + +-- Nærmeste brugbare sæt. Grund når intet passer: noRig (ingen med flueben og rollen), cannotDo (ingen kan +-- tage opgaven, fx afgrøden), allBusy, tooWide/tooNarrow (bredden, med tal fra den første). +-- accept (valgfri): candidate -> bool, fx "høsteren kan tage hvede". +function ADFieldFleet.pick(candidates, role, x, z, requiredWidth, accept) + local usable, reason, args = sortCandidates(candidates, role, x, z, requiredWidth, accept) + if usable[1] ~= nil then return usable[1].candidate end + return nil, reason, args +end + +-- n sæt på én gang (multi-tool kræver alle baner): de n nærmeste, ellers ingen. notEnough har {ledige, n}. +function ADFieldFleet.pickMany(candidates, role, n, x, z, requiredWidth, accept) + local usable, reason, args = sortCandidates(candidates, role, x, z, requiredWidth, accept) + if #usable >= n then + local picked = {} + for index = 1, n do picked[index] = usable[index].candidate end + return picked + end + if #usable > 0 then return nil, "notEnough", {#usable, n} end + return nil, reason, args end function ADFieldFleet.toggle(enabledIds, id) diff --git a/tests/test_adFieldFleet.lua b/tests/test_adFieldFleet.lua index 4c9093b..2fc8f94 100644 --- a/tests/test_adFieldFleet.lua +++ b/tests/test_adFieldFleet.lua @@ -15,7 +15,7 @@ check("FL1 slåmaskine", F.roleFromSpecs({spec_mower = true}), "mower") check("FL1 vender", F.roleFromSpecs({spec_tedder = true}), "tedder") check("FL1 river", F.roleFromSpecs({spec_windrower = true}), "rake") check("FL1 læssevogn", F.roleFromSpecs({spec_forageWagon = true, spec_mower = true}), "collector") -check("FL1 intet", F.roleFromSpecs({spec_plow = true}), nil) +check("FL1 intet", F.roleFromSpecs({spec_trailer = true}), nil) local function rig(id, role, x, extra) local r = {id = id, role = role, enabled = true, busy = false, controlled = false, width = 40.8, x = x, z = 0, label = id} @@ -49,5 +49,38 @@ local toggled = F.toggle(enabled, "b") check("FL7 b til", toggled.b, true); check("FL7 original urørt", enabled.b, nil) check("FL7 a fra", F.toggle(toggled, "a").a, nil) +-- FL8: nye roller i prioriteret rækkefølge +check("FL8 høster", F.roleFromSpecs({spec_combine = true, spec_cutter = true}), "harvester") +check("FL8 såmaskine før kultivator (combi)", F.roleFromSpecs({spec_sowingMachine = true, spec_cultivator = true, sprayRole = "fertilizer"}), "seeder") +check("FL8 kultivator", F.roleFromSpecs({spec_cultivator = true}), "cultivator") +check("FL8 plov", F.roleFromSpecs({spec_plow = true}), "plow") +check("FL8 tromle", F.roleFromSpecs({spec_roller = true}), "roller") +check("FL8 strigle", F.roleFromSpecs({spec_weeder = true}), "weeder") +check("FL8 stubhakker", F.roleFromSpecs({spec_mulcher = true}), "mulcher") +check("FL8 gødningssprøjte", F.roleFromSpecs({sprayRole = "fertilizer"}), "fertilizer") +check("FL8 kalkspreder", F.roleFromSpecs({sprayRole = "limer"}), "limer") +check("FL8 ukrudtssprøjte", F.roleFromSpecs({sprayRole = "weeder"}), "weeder") +check("FL8 aflæsningsvogn", F.roleFromSpecs({canUnload = true}), "unloader") +check("FL8 traktor med plov er ikke aflæsningsvogn", F.roleFromSpecs({spec_plow = true, canUnload = true}), "plow") + +-- FL9: accept-filter (fx høster der kan tage afgrøden, strigle kun til småt ukrudt) +local combineWheat = rig("jd", "harvester", 10, {fruits = {[3] = true}}) +local combineBeet = rig("ropa", "harvester", 1, {fruits = {[9] = true}}) +local forWheat = F.pick({combineBeet, combineWheat}, "harvester", 0, 0, nil, function(c) return c.fruits[3] == true end) +check("FL9 høster der kan tage hvede", forWheat.id, "jd") +local _, whyFruit = F.pick({combineBeet}, "harvester", 0, 0, nil, function(c) return c.fruits[3] == true end) +check("FL9 ingen kan tage afgrøden", whyFruit, "cannotDo") + +-- FL10: flere sæt til multi-tool — alle eller ingen, nærmeste først +local m1, m2, m3 = rig("m1", "mower", 30), rig("m2", "mower", 10), rig("m3", "mower", 20, {busy = true}) +local two = F.pickMany({m1, m2, m3}, "mower", 2, 0, 0) +check("FL10 to valgt", #two, 2) +check("FL10 nærmeste først", two[1].id, "m2") +local none, whyMany, args = F.pickMany({m1, m2, m3}, "mower", 3, 0, 0) +check("FL10 for få -> ingen", none, nil) +check("FL10 grund", whyMany, "notEnough") +check("FL10 ledige/krævet", args[1] .. "/" .. args[2], "2/3") +check("FL10 ingen med rollen", select(2, F.pickMany({m1}, "tedder", 1, 0, 0)), "noRig") + print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)