feat(marker): marker — art (slå/høst/bar), vækstfase, jordniveauer (gødning/kalk/pløj/ukrudt) og spillets indstillinger
This commit is contained in:
@@ -33,6 +33,42 @@ function ADFields.buildMowable(mowerConverter, tedderConverter, fillTypeName)
|
||||
return result
|
||||
end
|
||||
|
||||
-- Markens art: slåafgrøde, høstafgrøde eller bar (ingen afgrøde). Vokser = afgrøde der ikke er klar endnu.
|
||||
function ADFields.classify(fruitIndex, isMowable, readyRatio)
|
||||
if fruitIndex == nil then return "bare", false end
|
||||
local kind = isMowable and "mowable" or "harvest"
|
||||
return kind, (readyRatio or 0) < 0.9
|
||||
end
|
||||
|
||||
-- Vækststadie -> fase ud fra afgrødens grænser (FS25-felter hedder minHarvestingGrowthState eller
|
||||
-- minHarvestGrowthState afhængigt af version).
|
||||
function ADFields.growthPhase(state, fruit)
|
||||
if state == nil or fruit == nil then return nil end
|
||||
if state == 0 then return "none" end
|
||||
if fruit.cutState ~= nil and fruit.cutState ~= 0 and state == fruit.cutState then return "cut" end
|
||||
if fruit.witheredState ~= nil and state == fruit.witheredState then return "withered" end
|
||||
local minReady = fruit.minHarvestingGrowthState or fruit.minHarvestGrowthState
|
||||
local maxReady = fruit.maxHarvestingGrowthState or fruit.maxHarvestGrowthState or minReady
|
||||
if minReady ~= nil and state >= minReady and state <= (maxReady or minReady) then return "ready" end
|
||||
if minReady ~= nil and state < minReady then return "growing" end
|
||||
return "cut"
|
||||
end
|
||||
|
||||
-- Vokser marken? Fasen vinder; ukendt fase -> der er voksende afgrøde (getFruitArea) og den er ikke klar.
|
||||
function ADFields.isGrowing(phase, anyPixels, readyRatio)
|
||||
if phase ~= nil then return phase == "growing" end
|
||||
return (anyPixels or 0) > 0 and (readyRatio or 0) < 0.9
|
||||
end
|
||||
|
||||
-- Jordens behov (kalk/pløjning) ud fra gennemsnitsniveauer og spillets indstillinger (kalk/pløjning kan
|
||||
-- være slået fra i savegamet). Ukendt niveau = intet behov.
|
||||
function ADFields.soilNeeds(levels, settings)
|
||||
return {
|
||||
lime = settings.limeRequired and levels.lime ~= nil and levels.lime < 1 or false,
|
||||
plow = settings.plowingRequired and levels.plowLevel ~= nil and levels.plowLevel < 1 or false,
|
||||
}
|
||||
end
|
||||
|
||||
function ADFields.getMowable()
|
||||
if ADFields.mowable ~= nil then return ADFields.mowable end
|
||||
local ok, result = pcall(function()
|
||||
@@ -47,6 +83,80 @@ function ADFields.getMowable()
|
||||
return ADFields.mowable
|
||||
end
|
||||
|
||||
-- ---- density-maps (samme læsning som AI-Bonden/Arbejdsseddel, bevist i spillet) ----
|
||||
ADFields.densityCache = {}
|
||||
local function readDensity(channel, x, z)
|
||||
if channel == nil or g_currentMission == nil or g_currentMission.fieldGroundSystem == nil then return nil end
|
||||
local cached = ADFields.densityCache[channel]
|
||||
if cached == nil then
|
||||
local ok, mapId, first, num = pcall(function() return g_currentMission.fieldGroundSystem:getDensityMapData(channel) end)
|
||||
if not ok or mapId == nil then ADFields.densityCache[channel] = false; return nil end
|
||||
cached = {modifier = DensityMapModifier.new(mapId, first, num, g_currentMission.terrainRootNode)}
|
||||
ADFields.densityCache[channel] = cached
|
||||
elseif cached == false then
|
||||
return nil
|
||||
end
|
||||
local value
|
||||
pcall(function()
|
||||
cached.modifier:setParallelogramWorldCoords(x - 0.5, z - 0.5, 1, 0, 0, 1, DensityCoordType.POINT_VECTOR_VECTOR)
|
||||
local sum, area = cached.modifier:executeGet()
|
||||
if area ~= nil and area > 0 then value = sum / area end
|
||||
end)
|
||||
return value
|
||||
end
|
||||
|
||||
local function readWeed(x, z)
|
||||
local weedSystem = g_currentMission ~= nil and g_currentMission.weedSystem or nil
|
||||
if weedSystem == nil or weedSystem.densityMap == nil then return nil end
|
||||
if ADFields.weedModifier == nil then
|
||||
local ok, modifier = pcall(DensityMapModifier.new, weedSystem.densityMap, weedSystem.firstChannel or 0, weedSystem.numChannels or 4, g_currentMission.terrainRootNode)
|
||||
ADFields.weedModifier = ok and modifier or false
|
||||
end
|
||||
if ADFields.weedModifier == false then return nil end
|
||||
local value
|
||||
pcall(function()
|
||||
ADFields.weedModifier:setParallelogramWorldCoords(x - 0.5, z - 0.5, 1, 0, 0, 1, DensityCoordType.POINT_VECTOR_VECTOR)
|
||||
local sum, area = ADFields.weedModifier:executeGet()
|
||||
if area ~= nil and area > 0 then value = sum / area end
|
||||
end)
|
||||
return value
|
||||
end
|
||||
|
||||
local function average(points, reader, channel)
|
||||
local sum, count = 0, 0
|
||||
for _, p in ipairs(points) do
|
||||
local value = reader(channel, p.x, p.z)
|
||||
if value ~= nil then sum, count = sum + value, count + 1 end
|
||||
end
|
||||
return count > 0 and sum / count or nil
|
||||
end
|
||||
|
||||
local function missionSettings()
|
||||
local info = g_currentMission ~= nil and g_currentMission.missionInfo or {}
|
||||
local function flag(value) return value == nil or value == true end
|
||||
return {
|
||||
limeRequired = flag(info.limeRequired),
|
||||
plowingRequired = flag(info.plowingRequiredEnabled),
|
||||
weedsEnabled = flag(info.weedsEnabled),
|
||||
}
|
||||
end
|
||||
|
||||
-- Jord og pleje: gødning (og max), kalk, pløjning, ukrudt — gennemsnit over prøvepunkterne.
|
||||
local function readSoil(info)
|
||||
if type(FieldDensityMap) ~= "table" or #info.points == 0 then return end
|
||||
local settings = missionSettings()
|
||||
info.fert = average(info.points, readDensity, FieldDensityMap.SPRAY_LEVEL)
|
||||
local okMax, maxValue = pcall(function() return g_currentMission.fieldGroundSystem:getMaxValue(FieldDensityMap.SPRAY_LEVEL) end)
|
||||
info.fertMax = okMax and type(maxValue) == "number" and maxValue or 2
|
||||
info.lime = average(info.points, readDensity, FieldDensityMap.LIME_LEVEL)
|
||||
info.plowLevel = average(info.points, readDensity, FieldDensityMap.PLOW_LEVEL)
|
||||
if settings.weedsEnabled then
|
||||
info.weed = average(info.points, function(_, x, z) return readWeed(x, z) end, nil)
|
||||
end
|
||||
info.needs = ADFields.soilNeeds(info, settings)
|
||||
if not settings.limeRequired then info.lime = nil end
|
||||
end
|
||||
|
||||
local function fieldOwner(field)
|
||||
local farmland = field.farmland or (field.getFarmland ~= nil and field:getFarmland()) or nil
|
||||
if farmland == nil then return nil end
|
||||
@@ -111,7 +221,23 @@ local function readyRatio(fruitIndex, points)
|
||||
local okR, _, readyPixels = pcall(FSDensityMapUtil.getFruitArea, fruitIndex, p.x - 2, p.z - 2, p.x + 2, p.z, p.x, p.z + 2, false, false)
|
||||
if okR and readyPixels then readySum = readySum + readyPixels end
|
||||
end
|
||||
return anySum > 0 and readySum / anySum or 0
|
||||
return anySum > 0 and readySum / anySum or 0, anySum
|
||||
end
|
||||
|
||||
-- Vækststadiet på prøvepunkterne (flertal): getFieldDataAtWorldPosition's anden returværdi.
|
||||
local function growthStateAt(points)
|
||||
local votes, best, bestVotes = {}, nil, 0
|
||||
for _, p in ipairs(points) do
|
||||
local ok, _, state = pcall(function()
|
||||
local y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, p.x, 1, p.z)
|
||||
return FSDensityMapUtil.getFieldDataAtWorldPosition(p.x, y, p.z)
|
||||
end)
|
||||
if ok and type(state) == "number" then
|
||||
votes[state] = (votes[state] or 0) + 1
|
||||
if votes[state] > bestVotes then best, bestVotes = state, votes[state] end
|
||||
end
|
||||
end
|
||||
return best
|
||||
end
|
||||
|
||||
local function findMarker(adEnv, fieldId, x, z)
|
||||
@@ -145,8 +271,22 @@ local function scanField(adEnv, field, fieldId)
|
||||
end
|
||||
if mowable ~= nil then
|
||||
info.mowable, info.windrow, info.hay = true, mowable.windrow, mowable.hay
|
||||
info.readyRatio = readyRatio(info.fruitIndex, points)
|
||||
end
|
||||
local fruit = nil
|
||||
if info.fruitIndex ~= nil then
|
||||
info.readyRatio, info.anyPixels = readyRatio(info.fruitIndex, points)
|
||||
fruit = g_fruitTypeManager:getFruitTypeByIndex(info.fruitIndex)
|
||||
info.fruitName = fruit ~= nil and fruit.name or nil
|
||||
info.harvestFillType = fruit ~= nil and fruit.fillType ~= nil and fruit.fillType.index or nil
|
||||
end
|
||||
info.kind = ADFields.classify(info.fruitIndex, info.mowable, info.readyRatio)
|
||||
info.phase = fruit ~= nil and ADFields.growthPhase(growthStateAt(points), 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"
|
||||
end
|
||||
readSoil(info)
|
||||
if info.x ~= nil then
|
||||
local marker, distance = findMarker(adEnv, fieldId, info.x, info.z)
|
||||
info.markerId = marker ~= nil and marker.id or nil
|
||||
|
||||
Reference in New Issue
Block a user