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:
2026-09-27 09:12:49 +02:00
parent 5274925f2a
commit 5df4fdc7c3
8 changed files with 394 additions and 4 deletions
+25 -3
View File
@@ -11,10 +11,12 @@ SmartPickupFrame.STATUS_OVERRIDE_TICKS = 6
SmartPickupFrame.MAX_STATUS_REASONS = 3
SmartPickupFrame.LOG_PREFIX = "ADSmartPickup: "
SmartPickupFrame.STATE_OFF, SmartPickupFrame.STATE_ON = 1, 2
SmartPickupFrame.TAB_OVERVIEW, SmartPickupFrame.TAB_FLOWS, SmartPickupFrame.TAB_FIELDS = 1, 2, 3
SmartPickupFrame.TAB_OVERVIEW, SmartPickupFrame.TAB_FLOWS, SmartPickupFrame.TAB_FIELDS, SmartPickupFrame.TAB_MONTH = 1, 2, 3, 4
-- Måned-fanen genopbygges hvert N. sekund (ingen klikbare rækker, så fokus går ikke tabt)
SmartPickupFrame.MONTH_REBUILD_TICKS = 5
-- Lagerfaner: én pr. bygningstype (indeks -> kind i ADSources.getFarmInventory)
SmartPickupFrame.TAB_NAMES = {"spu_tab_overview", "spu_tab_flows", "spu_tab_fields", "spu_tab_factories", "spu_tab_silos", "spu_tab_barns", "spu_tab_sheds"}
SmartPickupFrame.TAB_KINDS = {[4] = "factory", [5] = "silo", [6] = "husbandry", [7] = "objectStorage"}
SmartPickupFrame.TAB_NAMES = {"spu_tab_overview", "spu_tab_flows", "spu_tab_fields", "spu_tab_month", "spu_tab_factories", "spu_tab_silos", "spu_tab_barns", "spu_tab_sheds"}
SmartPickupFrame.TAB_KINDS = {[5] = "factory", [6] = "silo", [7] = "husbandry", [8] = "objectStorage"}
function SmartPickupFrame.getTabKind(tab)
return SmartPickupFrame.TAB_KINDS[tab]
@@ -49,6 +51,7 @@ function SmartPickupFrame.new(i18n, getAdEnv)
-- Oversigt: Start/Stop alle i stedet for knapperne der virker på ét valgt flow.
self.startAllButtonInfo = {inputAction = InputAction.MENU_ACTIVATE, text = i18n:getText("spu_button_startAll"), callback = function() self:onClickStartAll() end}
self.stopAllButtonInfo = {inputAction = InputAction.MENU_EXTRA_2, text = i18n:getText("spu_button_stopAll"), callback = function() self:onClickStopAll() end}
self.monthButtons = {self.backButtonInfo}
self.flowButtons = {self.backButtonInfo, self.startButtonInfo, self.newButtonInfo, self.renameButtonInfo, self.deleteButtonInfo}
self.overviewButtons = {self.backButtonInfo, self.startAllButtonInfo, self.stopAllButtonInfo, self.newButtonInfo}
-- Marker: Start nu / Stop for den valgte mark
@@ -409,6 +412,17 @@ function SmartPickupFrame:rebuild()
self:restoreFocusIfLost()
return
end
if self.currentTab == SmartPickupFrame.TAB_MONTH then
if adEnv ~= nil then
self:fillMonthRows(adEnv)
end
self.monthRebuildTicks = SmartPickupFrame.MONTH_REBUILD_TICKS
self:colorRows()
self.boxLayout:invalidateLayout()
self:refreshButtons()
self:restoreFocusIfLost()
return
end
if self.currentTab == SmartPickupFrame.TAB_OVERVIEW then
if adEnv ~= nil then
self:fillOverviewRows(adEnv)
@@ -540,6 +554,8 @@ function SmartPickupFrame:refreshButtons()
wanted = self.overviewButtons
elseif self.currentTab == SmartPickupFrame.TAB_FIELDS then
wanted = self:getFieldButtons()
elseif self.currentTab == SmartPickupFrame.TAB_MONTH then
wanted = self.monthButtons
end
if self.menuButtonInfo ~= wanted then
self.menuButtonInfo = wanted
@@ -560,6 +576,12 @@ function SmartPickupFrame:refreshLive()
self:setStatusText(self.i18n:getText("spu_status_noAd"))
return
end
if self.currentTab == SmartPickupFrame.TAB_MONTH then
self.monthRebuildTicks = (self.monthRebuildTicks or 0) - 1
if self.monthRebuildTicks <= 0 then self:requestRebuild() end
self:refreshButtons()
return
end
local isOverview = self.currentTab == SmartPickupFrame.TAB_OVERVIEW
if isOverview then
self:refreshOverview(adEnv)
@@ -0,0 +1,107 @@
-- Smart Pickup — fanen Måned
-- Hvad skal nås før månedsskiftet? Høst og såning har korte vinduer; fanen viser markerne med arbejde i
-- gang, fremdrift, næste trin og om vinduet lukker. Kun visning — reglerne ligger i ADMonthView.
-- Periodens navn: spillets egen tekst (fx "Sen sommer") hvis den findes, ellers "Periode 6".
-- Periode 1 = tidligt forår = marts (nordlig halvkugle) -> "(aug)".
function SmartPickupFrame:monthPeriodText(period)
local month = (period + 1) % 12 + 1
local names = {}
for name in string.gmatch(self.i18n:getText("spu_month_months"), "[^,]+") do table.insert(names, name) end
local suffix = names[month] ~= nil and (" (" .. names[month] .. ")") or ""
local ok, text = pcall(function() return g_i18n:formatPeriod(period, false) end)
if ok and type(text) == "string" and text ~= "" then return text .. suffix end
return string.format(self.i18n:getText("spu_month_period"), period) .. suffix
end
-- {period, day, days, hour, minute}; nil hvis miljøet ikke kan læses.
function SmartPickupFrame.monthClock()
local ok, clock = pcall(function()
local environment = g_currentMission.environment
local dayTime = environment.dayTime or 0
return {period = environment.currentPeriod, day = environment.currentDayInPeriod, days = environment.daysPerPeriod,
hour = math.floor(dayTime / 3600000), minute = math.floor(dayTime / 60000) % 60}
end)
if ok and type(clock) == "table" and type(clock.period) == "number" then return clock end
return nil
end
-- Courseplays fremdrift for markens sæt (gennemsnit), nil hvis ingen melder noget.
function SmartPickupFrame.monthCpProgress(fieldId)
local sum, count = 0, 0
for _, assignment in ipairs(ADFieldJobs.assignmentsFor(fieldId)) do
local vehicle = assignment.vehicle
if not assignment.helper and vehicle ~= nil and vehicle.getCpFieldWorkProgress ~= nil then
local ok, progress = pcall(vehicle.getCpFieldWorkProgress, vehicle)
if ok and type(progress) == "number" then
sum, count = sum + progress, count + 1
end
end
end
return count > 0 and sum / count or nil
end
function SmartPickupFrame.monthContext(period)
return {
period = period,
plantableIn = function(name, forPeriod)
local fruit = g_fruitTypeManager ~= nil and g_fruitTypeManager:getFruitTypeByName(name) or nil
return fruit ~= nil and ADCpBridge.isPlantableIn(fruit, forPeriod)
end,
withers = function(info, nextPeriod)
local fruit = info.fruitIndex ~= nil and g_fruitTypeManager:getFruitTypeByIndex(info.fruitIndex) or nil
local ok, result = pcall(ADMonthView.withersIn, fruit, nextPeriod, info.growthState)
return ok and result == true
end,
cpProgress = SmartPickupFrame.monthCpProgress,
}
end
function SmartPickupFrame:monthStageText(stage)
if stage == nil then return "–" end
return self.i18n:getText("spu_field_stage_" .. tostring(stage))
end
function SmartPickupFrame:monthCells(row)
local crop = row.sowFruit ~= nil and self:fruitTitle(row.sowFruit) or (row.crop or "")
return {"F" .. tostring(row.id), self:monthStageText(row.stage), crop, ADMonthView.percentText(row.progress),
self:monthStageText(row.nextStage), self.i18n:getText("spu_month_window_" .. row.window)}
end
function SmartPickupFrame:fillMonthRows(adEnv)
local i18n = self.i18n
local clock = SmartPickupFrame.monthClock()
if clock == nil then
self:addTextRow(i18n:getText("spu_month_noClock"))
return
end
self:addTextRow(string.format(i18n:getText("spu_month_header"), self:monthPeriodText(clock.period),
clock.day or 1, clock.days or 1, clock.hour, clock.minute))
local infos = ADFields.list()
if #infos == 0 then
self:addTextRow(i18n:getText("spu_field_scanning"))
return
end
local view = ADMonthView.build(infos, ADFieldJobs.state.fields, SmartPickupFrame.monthContext(clock.period))
if view.critical > 0 then
self:addTextRow(string.format(i18n:getText("spu_month_verdictUrgent"), view.critical))
else
self:addTextRow(i18n:getText("spu_month_verdictCalm"))
end
if #view.rows == 0 then
self:addTextRow(i18n:getText("spu_month_noWork"))
else
self:addCellRow(self.fieldPrefab, {i18n:getText("spu_col_field"), i18n:getText("spu_month_col_work"), i18n:getText("spu_col_crop"),
i18n:getText("spu_month_col_progress"), i18n:getText("spu_month_col_next"), i18n:getText("spu_month_col_window")})
for _, row in ipairs(view.rows) do
self:addCellRow(self.fieldPrefab, self:monthCells(row))
end
end
if #view.upcoming > 0 then
self:addTextRow(i18n:getText("spu_month_upcoming"))
for _, entry in ipairs(view.upcoming) do
local key = entry.closes and "spu_month_upcomingLast" or "spu_month_upcomingOpen"
self:addTextRow(string.format(i18n:getText(key), entry.id, self:fruitTitle(entry.fruit)))
end
end
end