-- 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. Høst/slåning: andelen af HELE arealet der stadig står (standingRatio) — readyRatio duer ikke, -- for slået/høstet afgrøde forsvinder også fra dens nævner (in-game 27/9 F90: halvt slået = 0 %), og -- Courseplays tal er kun positionen i kurset (starter forfra på 0 ved genstart). Øvrige trin: Courseplay. function ADMonthView.progress(field, info, cpProgress) local cropStage = field.stage == "harvesting" or field.stage == "mowing" if cropStage and type(info.standingRatio) == "number" then return math.max(0, math.min(1, 1 - info.standingRatio)) end if type(cpProgress) == "number" then return math.max(0, math.min(1, cpProgress)) end if cropStage 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