feat(gui): fanen Måned — marker i gang med fremdrift, næste trin og om høst-/såvinduet lukker ved månedsskiftet; dom 'du kan sove' + marker der kan sås nu (v1.35.0.0)
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
-- AD Month View
|
||||
-- Rene regler for fanen Måned: hvilke marker har arbejde i gang, hvor langt er de, hvad kommer bagefter,
|
||||
-- og lukker vinduet (høst/såning) ved månedsskiftet? GUI'et oversætter nøglerne; spillet kaldes kun via
|
||||
-- ctx-funktionerne (testbart uden FS).
|
||||
|
||||
ADMonthView = {}
|
||||
|
||||
ADMonthView.PERIODS = 12
|
||||
-- Trin der kræver afgrøden eller såvinduet: sorteres før pleje/klargøring
|
||||
ADMonthView.MAIN_STAGES = {harvesting = true, mowing = true, sowing = true}
|
||||
-- Trin før såningen i klargøringskæden (lukker såvinduet, bliver marken ikke sået)
|
||||
ADMonthView.BEFORE_SOWING = {mowing = true, tedding = true, raking = true, collecting = true, harvesting = true,
|
||||
strawCollecting = true, baling = true, baleCollecting = true, mulching = true,
|
||||
liming = true, plowing = true, cultivating = true, sowing = true}
|
||||
|
||||
function ADMonthView.nextPeriod(period)
|
||||
if type(period) ~= "number" or period < 1 then return 1 end
|
||||
return period % ADMonthView.PERIODS + 1
|
||||
end
|
||||
|
||||
-- Visse FS25-sæsondata gemmer overgangene som lister af {startState, endState}; strukturen er ikke
|
||||
-- dokumenteret, så alle tabeller i periodens data gennemsøges. Ukendt = nej (ingen falsk alarm).
|
||||
local function findsTransition(data, fromState, toState, depth)
|
||||
if type(data) ~= "table" or depth > 3 then return false end
|
||||
if data.startState == fromState and data.endState == toState then return true end
|
||||
for _, value in pairs(data) do
|
||||
if type(value) == "table" and findsTransition(value, fromState, toState, depth + 1) then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Visner afgrøden (stadie state) ved overgangen til periode nextPeriod?
|
||||
function ADMonthView.withersIn(fruit, nextPeriod, state)
|
||||
if fruit == nil or type(state) ~= "number" or state <= 0 then return false end
|
||||
local withered = fruit.witheredState
|
||||
if type(withered) ~= "number" or withered <= 0 then return false end
|
||||
local periods = fruit.growthDataSeasonal ~= nil and fruit.growthDataSeasonal.periods or nil
|
||||
local data = periods ~= nil and periods[nextPeriod] or nil
|
||||
return findsTransition(data, state, withered, 0)
|
||||
end
|
||||
|
||||
-- Såvindue for en afgrøde: "closes" (kan sås nu, ikke næste måned), "open" (også næste måned), "closed".
|
||||
function ADMonthView.sowWindow(plantableIn, fruitName, period)
|
||||
if fruitName == nil or fruitName == "" or plantableIn == nil then return "open" end
|
||||
local now = plantableIn(fruitName, period)
|
||||
if not now then return "closed" end
|
||||
return plantableIn(fruitName, ADMonthView.nextPeriod(period)) and "open" or "closes"
|
||||
end
|
||||
|
||||
-- Er marken i gang (vises i tabellen)? Venter/færdig og passiv "klar" (manuel uden Start nu) er ikke.
|
||||
function ADMonthView.isActive(field)
|
||||
if field == nil then return false end
|
||||
if field.stage == "idle" or field.stage == "done" then return false end
|
||||
if field.stage == "ready" then return ADFieldModel.shouldStart(field) end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Næste trin i kæden (uden at ændre marken). nil = intet (pleje går tilbage til venter, sidste trin -> færdig).
|
||||
function ADMonthView.nextStage(field, info)
|
||||
if field.stage == "ready" then
|
||||
local stages = ADFieldModel.workStages(ADFieldModel.applyTarget(field.plan, info.kind, info.fruitName), info.kind, info.needs)
|
||||
return stages[1]
|
||||
end
|
||||
if ADFieldModel.isCare(field.stage) then return nil end
|
||||
local nextField = ADFieldModel.advance(field, info.needs)
|
||||
if nextField.stage == "done" or nextField.stage == "idle" then return nil end
|
||||
return nextField.stage
|
||||
end
|
||||
|
||||
-- Vindue for en aktiv mark: "harvestCloses" | "lastSowMonth" | "ok".
|
||||
-- ctx = {period, plantableIn(name, period), withers(info, nextPeriod)}
|
||||
function ADMonthView.window(field, info, ctx)
|
||||
local stage = field.stage
|
||||
local cropStage = stage == "harvesting" or stage == "mowing" or stage == "ready"
|
||||
if cropStage and info.kind ~= "bare" and ctx.withers ~= nil and ctx.withers(info, ADMonthView.nextPeriod(ctx.period)) then
|
||||
return "harvestCloses"
|
||||
end
|
||||
local sowsLater = field.plan ~= nil and field.plan.sow and ADMonthView.BEFORE_SOWING[stage]
|
||||
if sowsLater and ADMonthView.sowWindow(ctx.plantableIn, ADFieldModel.sowFruit(field), ctx.period) == "closes" then
|
||||
return "lastSowMonth"
|
||||
end
|
||||
return "ok"
|
||||
end
|
||||
|
||||
-- Fremdrift 0-1: Courseplays egen (gennemsnit af sættene) vinder; høst/slåning ellers ud fra hvor meget
|
||||
-- af afgrøden der er væk; ellers ukendt (nil).
|
||||
function ADMonthView.progress(field, info, cpProgress)
|
||||
if type(cpProgress) == "number" then return math.max(0, math.min(1, cpProgress)) end
|
||||
if (field.stage == "harvesting" or field.stage == "mowing") and type(info.readyRatio) == "number" then
|
||||
return math.max(0, math.min(1, 1 - info.readyRatio))
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function priority(row)
|
||||
if row.window ~= "ok" then return 0 end
|
||||
if ADMonthView.MAIN_STAGES[row.stage] then return 1 end
|
||||
return 2
|
||||
end
|
||||
|
||||
-- Hele fanen: {rows = {...}, critical = n, upcoming = {...}}.
|
||||
-- infos = ADFields.list(), fieldsById = ADFieldJobs.state.fields,
|
||||
-- ctx = {period, plantableIn(name, period), withers(info, nextPeriod), cpProgress(fieldId) -> 0-1|nil}
|
||||
function ADMonthView.build(infos, fieldsById, ctx)
|
||||
local rows, upcoming, critical = {}, {}, 0
|
||||
for _, info in ipairs(infos or {}) do
|
||||
local field = fieldsById[info.id]
|
||||
if field ~= nil and ADMonthView.isActive(field) then
|
||||
local row = {id = info.id, stage = field.stage, crop = info.cropTitle, sowFruit = nil,
|
||||
progress = ADMonthView.progress(field, info, ctx.cpProgress ~= nil and ctx.cpProgress(info.id) or nil),
|
||||
nextStage = ADMonthView.nextStage(field, info), window = ADMonthView.window(field, info, ctx)}
|
||||
if field.stage == "sowing" or (info.kind == "bare" and ADMonthView.BEFORE_SOWING[field.stage]) then
|
||||
row.sowFruit = field.plan.sow and ADFieldModel.sowFruit(field) or nil
|
||||
end
|
||||
if row.window ~= "ok" then critical = critical + 1 end
|
||||
table.insert(rows, row)
|
||||
elseif field ~= nil and info.kind == "bare" and (field.stage == "idle" or field.stage == "done") then
|
||||
local target = field.plan.targetFruit
|
||||
if target ~= nil and target ~= "" and ctx.plantableIn ~= nil and ctx.plantableIn(target, ctx.period) then
|
||||
table.insert(upcoming, {id = info.id, fruit = target,
|
||||
closes = ADMonthView.sowWindow(ctx.plantableIn, target, ctx.period) == "closes"})
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(rows, function(a, b)
|
||||
local pa, pb = priority(a), priority(b)
|
||||
if pa ~= pb then return pa < pb end
|
||||
return a.id < b.id
|
||||
end)
|
||||
table.sort(upcoming, function(a, b)
|
||||
if a.closes ~= b.closes then return a.closes end
|
||||
return a.id < b.id
|
||||
end)
|
||||
return {rows = rows, critical = critical, upcoming = upcoming}
|
||||
end
|
||||
|
||||
-- "50 %" / "–"
|
||||
function ADMonthView.percentText(progress)
|
||||
if progress == nil then return "–" end
|
||||
return string.format("%d %%", math.floor(progress * 100 + 0.5))
|
||||
end
|
||||
Reference in New Issue
Block a user