Skip to content

96 weather and crop calendar - #110

Draft
Grisu118 wants to merge 6 commits into
mainfrom
96-weather-and-crop-calendar
Draft

96 weather and crop calendar#110
Grisu118 wants to merge 6 commits into
mainfrom
96-weather-and-crop-calendar

Conversation

@Grisu118

Copy link
Copy Markdown
Member

Resolves #96

Grisu118 and others added 6 commits August 16, 2026 20:50
…hannels

The game's Anbaukalender screen has two halves, and they move at very different
speeds: the crop table is fixed the moment a map loads, while the forecast turns
over on the hour. So they ship as two channels rather than one.

cropCalendar.json carries, per crop the map shows, which of the twelve periods it
may be sown in and which it may be harvested in — read through the same three
calls the game's own frame uses. Event-driven on DAY_CHANGED and
PERIOD_LENGTH_CHANGED, since the only part that moves is the today marker.

Growth mode is the exception, and it has no message to subscribe to:
GrowthSystem:setGrowthMode writes missionInfo, fires its multiplayer event and
logs, and publishes nothing (MessageType.SETTING_CHANGED covers the client's
GameSettings, not the savegame's). The channel polls it every 2 s instead,
comparing against the mode the last collect() used so a skipped write retries. It
has to: outside SEASONAL the game answers "plantable" for all twelve periods, so a
stale file shows an entirely wrong calendar rather than a slightly old one.

weather.json carries the forecast — now, twelve two-hourly steps, six days —
subscribed to HOUR_CHANGED and DAY_CHANGED, the same two the in-game screen
reloads on. Three things it does not normalise, each for a reason in the header:
period labels come from g_i18n because the month shifts by hemisphere; Beaufort is
taken from the raw m/s because the game ceils before converting; and the wind
angle is left as the engine's own, since the current reading and the forecast
entries come from different sources and one compass convention would make one of
them wrong.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
environment.weather.temperature ran its three values through g_i18n:getTemperature,
which converts to Fahrenheit for anyone who picked it, and then labelled them
"°C" from a hardcoded string. A Fahrenheit player got Fahrenheit numbers under a
Celsius label.

The unit now comes from g_i18n:getTemperatureUnit, the same place the value's
conversion does. Found while writing the weather channel, which has to get this
pairing right and would have sat next to the wrong one.

No shape change, so VDTelemetry.VERSION stands.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CropCalendarData and WeatherForecastData, their parsers, their ServerMessage
variants and the two watcher registrations — the same path every other channel
takes, including the rule that an absent file broadcasts null so the app clears
rather than freezing. On a different map the crop list is a different set of crops
entirely, and a stale forecast is read to decide whether to cut hay.

The forecast type is named WeatherForecastData because model.Weather is already
taken by the telemetry channel's live min/max/current block. WeatherKind is parsed
by hand rather than serialized as an enum, so a weather type a future game version
adds costs one icon instead of the whole channel.

Two derivations live in shared, where the panel and the widget both reach them and
a test can call them off the composition: periodRuns merges a crop's periods into
contiguous bars (grass sows March–October *and* February, which has to draw as
two), and todayFraction places the marker, half-day offset included, the way the
game's own updateTodayBar does.

The four captures are Grisu's, taken 2026-08-16. They passed every assertion
first time, and they pinned down three things that had only been reasoned about:
the hourly strip really wraps past midnight, formatDayInPeriod drops the day
number entirely at daysPerPeriod = 1, and only the current wind angle is snapped
to 45° while the forecast angles are raw. noSeasons.json is vanilla.json's
savegame flipped to GrowthMode.DAILY — the case where every crop reports all
twelve periods and the calendar stops meaning anything.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both were private copies about to gain a third: InvoicesSection's FilterChip and
MapPanel's field/POI search box, which the calendar needs as-is. Moved into
components/ and both call sites repointed — no visual change, the definitions came
across verbatim.

WidgetDashboard's own Chip is deliberately left alone. It looks different on
purpose (bordered, on white, no ripple) for the page editor, and folding it in
would have restyled that screen as a side effect of unrelated work.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… widget

The game's Anbaukalender, plus the two questions it makes you scan the whole grid
to answer — what can I sow now, what can I harvest now — as a search box and two
filter chips carrying their own counts. The chips are not exclusive: both on means
both, which is how the question actually gets asked.

Layout is four scroll containers over two shared ScrollStates rather than one per
row: the header and the bars move sideways together so a column stays under its
label, the names and the bars move vertically together so a row stays beside its
name. Periods fill the width when there is room and scroll when there is not.

Sowing and harvest are told apart four ways over, because the game's own legend
does it by hue alone and that is the one thing this dashboard may not do: fixed
lane order (sow above, harvest below), brightness, square versus capsule bar ends,
and S/H letters in the name column. Rows alternate a guidance shade and highlight
when pressed, held by crop id so re-filtering cannot move the highlight; the
selected row also carries a solid leading edge bar and darker ink, since DarkGray
drops to 3.9:1 on the selection wash. The grid rules are drawn as an alpha so they
survive all three row backgrounds.

WeatherIcons draws its eight glyphs rather than borrowing them. Weather type is
carried by shape alone — rain slants, snow is angular, hail is round — and a set
assembled half from Material would not read as one strip. The arrow is turned to
windDirection + 180 like the game's, then negated: the engine measures
counter-clockwise and Compose's rotate() turns clockwise, which had the vane
mirrored about the vertical axis.

Outside seasonal growth the game answers "plantable" for all twelve periods, so
every bar fills and the filters mean nothing. The panel says so rather than
letting the grid look broken.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Four deferrals, each with the reason rather than a plan. The one worth knowing
before anyone tries it: crop icons are unreachable — the game's row icon is
fillType.hudOverlayFilename, which points inside dataS2/, and AssetResolver opens
gameDir-relative paths and mod zips, neither of which reaches into a packed
archive. The ground-layer legend is the only cheap substitute and only covers
crops already growing on the map, which is precisely the set you do not need to
look up.

Three in-game checks left, all needing a session this branch could not reach: a
multiplayer client (are forecastItems replicated at all?), a southern-hemisphere
map, and a season length changed mid-session. The growth-mode watch and the
non-seasonal calendar are already answered — noSeasons.json came out of flipping
the setting live in a running savegame, so both the poll and the data are proven.

The captures-wanted entry is retired for these two channels and replaced with what
is still missing from them: HAIL, THUNDER and TWISTER have never appeared on the
wire, so three of the eight drawn glyphs are unexercised by any fixture, as is a
day caption from a map with more than one day per period.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added area:lua This issue is related to the lua part of this repository (FS25 Mod) area:kotlin This issue is related to the kotlin part (app) labels Aug 16, 2026
@coderabbitai

coderabbitai Bot commented Aug 16, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 905725e7-feaf-43b0-a5db-d7c466fdb4dd

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:kotlin This issue is related to the kotlin part (app) area:lua This issue is related to the lua part of this repository (FS25 Mod)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Weather and Crop Calendar

1 participant