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
return parts
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
-- 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
-- x1, z1, x2, z2 ... — som Courseplay); ældre: polygonPoints (noder). Tom liste hvis intet findes.
function ADFields.readPolygon(field)
@@ -243,7 +252,7 @@ end
local function newScan(adEnv, entry)
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}}}
local info = {id = fieldId, field = field, points = scan.points, verts = {}, mowable = false, readyRatio = 0}
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).
function ADFields.scanPerFruit(scan)
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 settings = missionSettings()
scan.settings = settings
@@ -301,11 +318,6 @@ function ADFields.scanPerFruit(scan)
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)
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)
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 "?"
end
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)
-- 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
info.kind = "bare"
info.stubbleOf = info.phase == "cut" and info.cropTitle or nil
end
local settings = scan.settings or missionSettings()
for key, sum in pairs(scan.soil) do
@@ -109,7 +109,7 @@ function SmartPickupFrame:fieldCells(row)
table.insert(names, ADRuns.vehicleLabel(assignment.vehicle))
end
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 {
name, crop, self:fieldPlanText(field, info),
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 plan = field.plan
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))
local targetTexts, targetValues = self:sowFruitChoices("spu_field_target_none")
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_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_stubble" text="stub (%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_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_storeAutoSilage" text="none chosen: nearest fermenting silo with room, else nearest silo"/>
<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_fertilize" text="Fertilize to max (while the crop grows)"/>
<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"?>
<modDesc descVersion="107">
<author>masterdraco</author>
<version>1.28.6.0</version>
<version>1.28.7.0</version>
<title>
<en>AD Smart Pickup</en>
</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 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"))
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 getTerrainHeightAtWorldPos() return 0 end
-- 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 = {
getFieldDataAtWorldPosition = function() return 1, 2 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("PG5 markør ved kanten findes (afstand til omridset)", info and info.markerId, 900)
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"))
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
local calls = 0
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,
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_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_currentMission = {terrainRootNode = 1, missionInfo = {}, fieldGroundSystem = {
getDensityMapData = function() return 1, 0, 2 end, getMaxValue = function() return 2 end}}