fix(markarbejde): afgrøde og vækststadie fra field.fieldState (spillets marktilstand); getFieldDataAtWorldPosition's 2. værdi er jord-bits, ikke vækst — F108 majs = bar jord; høstet mark vises som 'stub (afgrøde)' (v1.28.7.0)

This commit is contained in:
2026-09-26 12:38:25 +02:00
parent 36f381fc36
commit abbe40da01
9 changed files with 73 additions and 14 deletions
+9
View File
@@ -37,3 +37,12 @@ function ADFieldView.soilParts(info)
if (info.weed or 0) >= ADFieldModel.WEED_MIN then table.insert(parts, "weed") end if (info.weed or 0) >= ADFieldModel.WEED_MIN then table.insert(parts, "weed") end
return parts return parts
end end
-- Afgrødekolonnen: høstet mark = "stub (Hvede)", bar mark = "bar jord", ellers afgrøden.
function ADFieldView.cropText(info, getText)
if info.kind == "bare" and info.stubbleOf ~= nil then
return string.format(getText("spu_field_stubble"), tostring(info.stubbleOf))
end
if info.kind == "bare" then return getText("spu_field_bare") end
return tostring(info.cropTitle or "-")
end
+21 -8
View File
@@ -153,6 +153,15 @@ end
ADFields.SAMPLE_DIRECTIONS = 16 ADFields.SAMPLE_DIRECTIONS = 16
-- field.fieldState (FS25, bekræftet i AI-Bonde via aifProbe): {fruitIndex|nil, growthState} eller nil
-- når spillet ikke har tilstanden (så bruges punkt-afstemningen).
function ADFields.readFieldState(field)
local state = field ~= nil and field.fieldState or nil
if type(state) ~= "table" or type(state.fruitTypeIndex) ~= "number" then return nil end
local fruitIndex = state.fruitTypeIndex > 0 and state.fruitTypeIndex or nil
return {fruitIndex = fruitIndex, growthState = fruitIndex ~= nil and state.growthState or nil}
end
-- Markens omrids som {{x, z}, ...}. FS25: field:getDensityMapPolygon():getVerticesList() (flad liste -- Markens omrids som {{x, z}, ...}. FS25: field:getDensityMapPolygon():getVerticesList() (flad liste
-- x1, z1, x2, z2 ... — som Courseplay); ældre: polygonPoints (noder). Tom liste hvis intet findes. -- x1, z1, x2, z2 ... — som Courseplay); ældre: polygonPoints (noder). Tom liste hvis intet findes.
function ADFields.readPolygon(field) function ADFields.readPolygon(field)
@@ -243,7 +252,7 @@ end
local function newScan(adEnv, entry) local function newScan(adEnv, entry)
local field, fieldId = entry.field, entry.id local field, fieldId = entry.field, entry.id
local scan = {id = fieldId, queue = {}, head = 1, tail = 0, points = {}, fruitVotes = {}, growthVotes = {}, local scan = {id = fieldId, queue = {}, head = 1, tail = 0, points = {}, fruitVotes = {},
anySum = 0, readySum = 0, soil = {fert = {0, 0}, lime = {0, 0}, plowLevel = {0, 0}, weed = {0, 0}}} anySum = 0, readySum = 0, soil = {fert = {0, 0}, lime = {0, 0}, plowLevel = {0, 0}, weed = {0, 0}}}
local info = {id = fieldId, field = field, points = scan.points, verts = {}, mowable = false, readyRatio = 0} local info = {id = fieldId, field = field, points = scan.points, verts = {}, mowable = false, readyRatio = 0}
scan.info = info scan.info = info
@@ -289,7 +298,15 @@ end
-- Når afgrøden kendes: klar-andel, vækststadie og jord pr. punkt (én opgave pr. punkt). -- Når afgrøden kendes: klar-andel, vækststadie og jord pr. punkt (én opgave pr. punkt).
function ADFields.scanPerFruit(scan) function ADFields.scanPerFruit(scan)
local info = scan.info local info = scan.info
info.fruitIndex = winner(scan.fruitVotes) -- spillets egen marktilstand (som fields.xml) er sandheden; punkt-afstemningen er reserve.
-- (getFieldDataAtWorldPosition giver isOnField, densityBits, groundType — IKKE vækststadie)
local state = ADFields.readFieldState(info.field)
info.growthState = state ~= nil and state.growthState or nil
if state ~= nil then
info.fruitIndex = state.fruitIndex
else
info.fruitIndex = winner(scan.fruitVotes)
end
local fruitIndex = info.fruitIndex local fruitIndex = info.fruitIndex
local settings = missionSettings() local settings = missionSettings()
scan.settings = settings scan.settings = settings
@@ -301,11 +318,6 @@ function ADFields.scanPerFruit(scan)
if okA and anyPixels then scan.anySum = scan.anySum + anyPixels end if okA and anyPixels then scan.anySum = scan.anySum + anyPixels end
local okR, _, readyPixels = pcall(FSDensityMapUtil.getFruitArea, fruitIndex, x - 2, z - 2, x + 2, z, x, z + 2, false, false) local okR, _, readyPixels = pcall(FSDensityMapUtil.getFruitArea, fruitIndex, x - 2, z - 2, x + 2, z, x, z + 2, false, false)
if okR and readyPixels then scan.readySum = scan.readySum + readyPixels end if okR and readyPixels then scan.readySum = scan.readySum + readyPixels end
local okG, _, state = pcall(function()
local y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 1, z)
return FSDensityMapUtil.getFieldDataAtWorldPosition(x, y, z)
end)
if okG and type(state) == "number" then vote(scan.growthVotes, state) end
end end
end) end)
push(scan, function() push(scan, function()
@@ -345,11 +357,12 @@ local function finishScan(adEnv, scan)
info.cropTitle = fillType ~= nil and fillType.title or (fruit ~= nil and fruit.name) or "?" info.cropTitle = fillType ~= nil and fillType.title or (fruit ~= nil and fruit.name) or "?"
end end
info.kind = ADFields.classify(info.fruitIndex, info.mowable, info.readyRatio) info.kind = ADFields.classify(info.fruitIndex, info.mowable, info.readyRatio)
info.phase = fruit ~= nil and ADFields.growthPhase(winner(scan.growthVotes), fruit) or nil info.phase = fruit ~= nil and ADFields.growthPhase(info.growthState, fruit) or nil
info.growing = info.fruitIndex ~= nil and ADFields.isGrowing(info.phase, info.anyPixels, info.readyRatio) info.growing = info.fruitIndex ~= nil and ADFields.isGrowing(info.phase, info.anyPixels, info.readyRatio)
-- høstet stub eller visnet afgrøde er i praksis en bar mark (klargøring/såning) -- høstet stub eller visnet afgrøde er i praksis en bar mark (klargøring/såning)
if info.kind == "harvest" and (info.phase == "cut" or info.phase == "withered" or info.phase == "none") then if info.kind == "harvest" and (info.phase == "cut" or info.phase == "withered" or info.phase == "none") then
info.kind = "bare" info.kind = "bare"
info.stubbleOf = info.phase == "cut" and info.cropTitle or nil
end end
local settings = scan.settings or missionSettings() local settings = scan.settings or missionSettings()
for key, sum in pairs(scan.soil) do for key, sum in pairs(scan.soil) do
@@ -109,7 +109,7 @@ function SmartPickupFrame:fieldCells(row)
table.insert(names, ADRuns.vehicleLabel(assignment.vehicle)) table.insert(names, ADRuns.vehicleLabel(assignment.vehicle))
end end
local name = string.format("%s%sF%d", info.id == self.selectedFieldId and "> " or "", info.markerId == nil and "! " or "", info.id) local name = string.format("%s%sF%d", info.id == self.selectedFieldId and "> " or "", info.markerId == nil and "! " or "", info.id)
local crop = info.kind == "bare" and self.i18n:getText("spu_field_bare") or (info.cropTitle or "-") local crop = ADFieldView.cropText(info, function(key) return self.i18n:getText(key) end)
return { return {
name, crop, self:fieldPlanText(field, info), name, crop, self:fieldPlanText(field, info),
self.i18n:getText(field.plan.mode == "auto" and "spu_field_mode_auto" or "spu_field_mode_manual"), self.i18n:getText(field.plan.mode == "auto" and "spu_field_mode_auto" or "spu_field_mode_manual"),
@@ -190,7 +190,7 @@ function SmartPickupFrame:fillFieldDetail(adEnv, info)
local field = ADFieldJobs.getField(info.id) local field = ADFieldJobs.getField(info.id)
local plan = field.plan local plan = field.plan
local yesNo = {i18n:getText("ui_no"), i18n:getText("ui_yes")} local yesNo = {i18n:getText("ui_no"), i18n:getText("ui_yes")}
local crop = info.kind == "bare" and i18n:getText("spu_field_bare") or tostring(info.cropTitle or "-") local crop = ADFieldView.cropText(info, function(key) return i18n:getText(key) end)
self:addTextRow(string.format(i18n:getText("spu_field_section"), info.id, crop)) self:addTextRow(string.format(i18n:getText("spu_field_section"), info.id, crop))
local targetTexts, targetValues = self:sowFruitChoices("spu_field_target_none") local targetTexts, targetValues = self:sowFruitChoices("spu_field_target_none")
self:addChoiceRow("fieldTarget", i18n:getText("spu_field_target"), targetTexts, targetValues, plan.targetFruit or "") self:addChoiceRow("fieldTarget", i18n:getText("spu_field_target"), targetTexts, targetValues, plan.targetFruit or "")
+1
View File
@@ -94,6 +94,7 @@
<text name="spu_field_fermenter" text="gæringssilo"/> <text name="spu_field_fermenter" text="gæringssilo"/>
<text name="spu_field_storeAutoSilage" text="intet valgt: nærmeste gæringssilo med plads, ellers nærmeste silo"/> <text name="spu_field_storeAutoSilage" text="intet valgt: nærmeste gæringssilo med plads, ellers nærmeste silo"/>
<text name="spu_field_bare" text="bar jord"/> <text name="spu_field_bare" text="bar jord"/>
<text name="spu_field_stubble" text="stub (%s)"/>
<text name="spu_field_careNeed" text="trænger til %s"/> <text name="spu_field_careNeed" text="trænger til %s"/>
<text name="spu_field_fertilize" text="Gød op til max (når afgrøden vokser)"/> <text name="spu_field_fertilize" text="Gød op til max (når afgrøden vokser)"/>
<text name="spu_field_lime" text="Kalk når jorden trænger"/> <text name="spu_field_lime" text="Kalk når jorden trænger"/>
+1
View File
@@ -94,6 +94,7 @@
<text name="spu_field_fermenter" text="fermenting silo"/> <text name="spu_field_fermenter" text="fermenting silo"/>
<text name="spu_field_storeAutoSilage" text="none chosen: nearest fermenting silo with room, else nearest silo"/> <text name="spu_field_storeAutoSilage" text="none chosen: nearest fermenting silo with room, else nearest silo"/>
<text name="spu_field_bare" text="bare soil"/> <text name="spu_field_bare" text="bare soil"/>
<text name="spu_field_stubble" text="stubble (%s)"/>
<text name="spu_field_careNeed" text="needs %s"/> <text name="spu_field_careNeed" text="needs %s"/>
<text name="spu_field_fertilize" text="Fertilize to max (while the crop grows)"/> <text name="spu_field_fertilize" text="Fertilize to max (while the crop grows)"/>
<text name="spu_field_lime" text="Lime when the soil needs it"/> <text name="spu_field_lime" text="Lime when the soil needs it"/>
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?> <?xml version="1.0" encoding="utf-8" standalone="no"?>
<modDesc descVersion="107"> <modDesc descVersion="107">
<author>masterdraco</author> <author>masterdraco</author>
<version>1.28.6.0</version> <version>1.28.7.0</version>
<title> <title>
<en>AD Smart Pickup</en> <en>AD Smart Pickup</en>
</title> </title>
+7
View File
@@ -38,5 +38,12 @@ local parts = V.soilParts({lime = 0.2, fert = 1, fertMax = 2, weed = 3, needs =
check("V5 dele", table.concat(parts, ","), "lime,plow,fert,weed") check("V5 dele", table.concat(parts, ","), "lime,plow,fert,weed")
check("V5 intet", #V.soilParts({lime = 2, fert = 2, fertMax = 2, weed = 0, needs = {}}), 0) check("V5 intet", #V.soilParts({lime = 2, fert = 2, fertMax = 2, weed = 0, needs = {}}), 0)
-- cropText: høstet = stub (afgrøde), bar = bar jord, ellers afgrøden
local texts = {spu_field_stubble = "stub (%s)", spu_field_bare = "bar jord"}
local function T(key) return texts[key] end
check("FVcrop stub", ADFieldView.cropText({kind = "bare", stubbleOf = "Hvede", cropTitle = "Hvede"}, T), "stub (Hvede)")
check("FVcrop bar", ADFieldView.cropText({kind = "bare"}, T), "bar jord")
check("FVcrop afgrøde", ADFieldView.cropText({kind = "harvest", cropTitle = "Majs"}, T), "Majs")
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
os.exit(failures == 0 and 0 or 1) os.exit(failures == 0 and 0 or 1)
+27 -1
View File
@@ -15,7 +15,7 @@ local MAIZE = 10
function getWorldTranslation() return 0, 0, 0 end function getWorldTranslation() return 0, 0, 0 end
function getTerrainHeightAtWorldPos() return 0 end function getTerrainHeightAtWorldPos() return 0 end
-- markskiltet står i marken, men ved en stribe uden afgrøde (x > 1400) -- markskiltet står i marken, men ved en stribe uden afgrøde (x > 1400)
local function cropAt(x) return x <= 1400 and MAIZE or nil end function cropAt(x) return x <= 1400 and MAIZE or nil end
FSDensityMapUtil = { FSDensityMapUtil = {
getFieldDataAtWorldPosition = function() return 1, 2 end, getFieldDataAtWorldPosition = function() return 1, 2 end,
getFruitTypeIndexAtWorldPos = function(x, z) return cropAt(x) end, getFruitTypeIndexAtWorldPos = function(x, z) return cropAt(x) end,
@@ -56,5 +56,31 @@ check("PG3 majsen findes trods skiltet på bar stribe", info and info.fruitName,
check("PG4 ikke bar mark", info and info.kind, "harvest") check("PG4 ikke bar mark", info and info.kind, "harvest")
check("PG5 markør ved kanten findes (afstand til omridset)", info and info.markerId, 900) check("PG5 markør ved kanten findes (afstand til omridset)", info and info.markerId, 900)
check("PG6 afstand = til kanten", info and info.markerDistance, 5) check("PG6 afstand = til kanten", info and info.markerDistance, 5)
-- PG7-9: field.fieldState er spillets sandhed (fields.xml: MAIZE growthState 2); jord-bits fra
-- getFieldDataAtWorldPosition (2. værdi) er IKKE vækststadie. Høstet mark (cutState) = bar, vist som stub.
field.fieldState = {fruitTypeIndex = MAIZE, growthState = 2}
FSDensityMapUtil.getFieldDataAtWorldPosition = function() return true, 0 end -- jord-bits 0 lignede "ingen vækst"
local function rescan()
ADFields.cache, ADFields.listCache, ADFields.activeScan, ADFields.lastScanStart = {}, nil, nil, nil
for _ = 1, 2000 do
if ADFields.getInfo(108) ~= nil then break end
ADFields.scanStep(adEnv, 1, now)
now = now + 16
end
return ADFields.getInfo(108)
end
info = rescan()
check("PG7 majs i vækst (fieldState), ikke bar", info and info.kind, "harvest")
check("PG7 fase vokser", info and info.phase, "growing")
field.fieldState = {fruitTypeIndex = MAIZE, growthState = 9}
info = rescan()
check("PG8 høstet (cutState) = bar", info and info.kind, "bare")
check("PG8 stub af majs", info and info.stubbleOf, "Majs")
field.fieldState = {fruitTypeIndex = 0, growthState = 0}
cropAt = function() return nil end
info = rescan()
check("PG9 ingen afgrøde = bar uden stub", info and info.kind .. "/" .. tostring(info.stubbleOf), "bare/nil")
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
os.exit(failures == 0 and 0 or 1) os.exit(failures == 0 and 0 or 1)
+4 -2
View File
@@ -17,7 +17,7 @@ function getWorldTranslation(node) local p = nodes[node]; return p[1], 0, p[2] e
function getTerrainHeightAtWorldPos() return 0 end function getTerrainHeightAtWorldPos() return 0 end
local calls = 0 local calls = 0
FSDensityMapUtil = { FSDensityMapUtil = {
getFieldDataAtWorldPosition = function() calls = calls + 1; return 1, 6 end, -- på mark, vækststadie 6 getFieldDataAtWorldPosition = function() calls = calls + 1; return true, 170 end, -- på mark (2. værdi = jord-bits, IKKE vækst)
getFruitTypeIndexAtWorldPos = function() calls = calls + 1; return WHEAT end, getFruitTypeIndexAtWorldPos = function() calls = calls + 1; return WHEAT end,
getFruitArea = function(_, _, _, _, _, _, _, any) calls = calls + 1; return 0, any and 10 or 10 end, getFruitArea = function(_, _, _, _, _, _, _, any) calls = calls + 1; return 0, any and 10 or 10 end,
} }
@@ -30,7 +30,9 @@ g_fruitTypeManager = {
} }
g_fillTypeManager = {getConverterDataByName = function() return {} end, getFillTypeNameByIndex = function() return "X" end} g_fillTypeManager = {getConverterDataByName = function() return {} end, getFillTypeNameByIndex = function() return "X" end}
g_farmlandManager = {getFarmlandOwner = function() return 1 end} g_farmlandManager = {getFarmlandOwner = function() return 1 end}
local field = {getId = function() return 7 end, farmland = {id = 70}, polygonPoints = {101, 102, 103, 104}, posX = 50, posZ = 50} -- spillets egen marktilstand (field.fieldState, som fields.xml): afgrøde + vækststadie
local field = {getId = function() return 7 end, farmland = {id = 70}, polygonPoints = {101, 102, 103, 104}, posX = 50, posZ = 50,
fieldState = {fruitTypeIndex = WHEAT, growthState = 6}}
g_fieldManager = {getFields = function() return {field} end} g_fieldManager = {getFields = function() return {field} end}
g_currentMission = {terrainRootNode = 1, missionInfo = {}, fieldGroundSystem = { g_currentMission = {terrainRootNode = 1, missionInfo = {}, fieldGroundSystem = {
getDensityMapData = function() return 1, 0, 2 end, getMaxValue = function() return 2 end}} getDensityMapData = function() return 1, 0, 2 end, getMaxValue = function() return 2 end}}