Skip to content

Paint every backdrop scenery - #30

Merged
Attacktive merged 6 commits into
mainfrom
feature/painted-sceneries
Aug 15, 2026
Merged

Paint every backdrop scenery#30
Attacktive merged 6 commits into
mainfrom
feature/painted-sceneries

Conversation

@Attacktive

Copy link
Copy Markdown
Owner

All four backdrop sceneries now draw through a shared painted-layer engine instead of flat two-plane silhouettes.

The engine

A SceneryLayer carries an outline, a SceneryMaterial, and a SceneryPlane; sceneryLayerColor() blends a material's intrinsic daylight color toward the sky-derived plane tone by how much atmosphere sits in front of it. Clear day keeps colors honest, overcast and fog gray them out, dawn and dusk backlight them, and night collapses to within a hair of the old silhouette look. SILHOUETTE stays in the enum as the atmospheric anchor — it takes the plane tone exactly, reproducing the pre-paint rendering.

SceneryGlyph gained a plane field so a painted shape can sit on either depth plane. That is what lets the farmhouse and the crane be real buildings instead of shapes cut out of the hill behind them, which could only ever be hill-colored.

The scenes

  • BeachWATER and SAND, with the old freighter-plus-triangle pair replaced by a single sloop: dark hull and mast under a mainsail with a concave leech and a jib off the forestay.
  • MountainsROCK ridges behind a FOREST near range over a MEADOW floor, with snowcaps whose top edge is the ridge outline itself, so snow hugs rock exactly. Only genuinely high summits earn a cap.
  • CountrysidePASTURE, MEADOW, and a ripe WHEAT strip, carrying a red BARN under a dark roof with a chimney rising from behind the slope, plus a post-and-rail fence planted on the wheat contour.
  • Metropolis — hazy STEEL towers behind warm MASONRY blocks, fronted by a park band with tree crowns. The near plane trades its continuous roofline for detached blocks with real street gaps. One block is a construction site: a bare slab with the lift core poking above it, never lit, under a tower crane whose apex joins the beacon list and blinks red for free.

Two bug classes fixed along the way

Aspect-ratio distortion. Several glyphs mixed x-based widths with fixed y-offsets, so they stretched vertically on wide screens. Every glyph now derives its verticals from its own on-screen width (rise = width * aspectRatio), single-basis. The shark fin, whale, sloop, barn, park trees, and crane all went through this; the barn keeps its proportions at every aspect pins it as a regression test.

Landscape overcrowding. scaled() grows feature counts linearly with aspect ratio, which holds density constant per unit of screen height — rotating to landscape multiplied counts ~4.3× for ~2.2× more width, cramming roughly twice the buildings per inch. The new scaledSparse() grows with the square root of the width gain, making spacing rotation-invariant. Applied to the metropolis skyline and park trees; the portrait multiplier is exactly 1, so portrait renders are untouched.

Also

A rare helicopter crosses above the skyline, slot-scheduled like the meteors and bird flocks. Unlike the birds it flies at night, carrying a blinking anti-collision light, but storms, fog, and a heavy deck still ground it.

The orphaned rooflineRun and silhouetteLayers helpers are gone now that no scene wants a continuous wall or an unpainted plane.

Testing

44 unit tests pass. New coverage pins each scene's layer materials, the crane's parts and plane, the beacon count including the crane apex, barn proportions across three aspect ratios, and — in ScenePaletteTest — that water reads cool, sand and wheat warm, pasture green, steel cool, masonry warm, that a thunderstorm collapses every material to the plane tone, and that the far plane sits deeper in haze than the near one.

Each scene was verified visually on an emulator during development via a throwaway capture harness; the harness is not part of this branch.

🤖 Generated with Claude Code

Attacktive and others added 5 commits August 16, 2026 00:17
The backdrop scenes were monochrome by construction: both silhouette
planes were just the sky's bottom color darkened. This replaces the
two-plane contract with a list of painted layers, each carrying a
material (WATER, SAND, HULL, SAIL, PARASOL), and colors every layer per
frame as a blend between the material's intrinsic daylight color and
the old sky-derived tone.

The blend amount comes from the day phase (day 0.35, dawn/dusk 0.75,
night 0.95) and is pushed to 1 by the same overcast/fog/precipitation/
thunder signals that gray the sky, so weather legibility is untouched:
a storm still collapses everything toward the silhouette look, and
night is nearly indistinguishable from before.

Beach only for now — metropolis, mountains, and countryside emit
SILHOUETTE layers that take the plane tone exactly, pixel-identical to
the previous look, each awaiting its own repaint.

Also in the beach pass:
- The freighter is gone (it read as a submarine); a single sloop
  remains, rebuilt with a hull, mast, concave-leech mainsail, and jib,
  in HULL/SAIL livery.
- Shapes are aspect-proof: ships derive heights from their own
  on-screen length, fins and the whale from one width basis, so
  portrait no longer stretches them tall.
- Shark fins point straight up and curve like actual dorsal fins.
- Parasols split into a dark pole and a red canopy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Second scene through the painted-layer engine. The far ridge is hazy
ROCK, the near range alpine FOREST green, and a new sunlit MEADOW
valley floor rolls along the bottom as a third color band.

Snow hugs the tallest far summits: each cap's top edge is the ridge
outline itself, its underside a snowline with a central dip, and the
bottom corners slide down the real slopes. The tallest summit always
earns snow regardless of how the seeded ridge came out; the fixed
threshold only adds caps on genuinely high peaks.

Snowcaps ride on the beach sloop's machinery, generalized for it:
SceneryShip/ships became SceneryGlyph/glyphs — closed painted shapes
drawn over the far plane — with no renderer changes beyond the rename.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ed barn

Third scene through the painted-layer engine. Hazy PASTURE hills sit
behind the MEADOW rise carrying the windmill, and a golden WHEAT strip
rolls along the bottom as the warm third band. The windmill gets white
canvas sails and the fence posts weathered-wood brown.

The farmhouse is no longer a cut in the hill outline — welded in, it
could only ever be hill-colored. It's now painted glyphs: a falu-red
BARN body under a dark roof, the chimney rising from behind the slope.
Glyphs learned a depth plane for this, so the house draws over the near
fill while the sloop and snowcaps stay on the far plane untouched.

Every barn height derives from its own on-screen width (span × aspect),
so the proportions hold at every screen aspect — pinned by a test that
compares the on-screen height/width ratio across three aspects. The
bare fence-post ticks read as floating pillars; they're now a proper
post-and-rail fence planted on the wheat boundary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…construction site

Fourth and final scene through the painted-layer engine: hazy STEEL
towers behind warm MASONRY blocks, fronted by a park band (MEADOW) with
FOREST tree crowns. The near plane trades the continuous roofline for
detached blocks with real street gaps, and the skyline thins out —
6 far + 5 near in portrait. Landscape counts now grow with the square
root of the width gain (scaledSparse) instead of linearly: the old rule
kept density constant per screen height, cramming ~2x the buildings per
inch on a rotated device. The sqrt curve makes tower spacing
rotation-invariant. The orphaned rooflineRun and silhouetteLayers
helpers are gone; SILHOUETTE stays as the palette's atmospheric anchor.

One near block is a construction site: a bare slab with the lift core
poking above it, never lit, with a tower crane over it — mast planted
exactly on the seeded slab line, jib, counter-jib, apex, and hook line,
all single-basis sized so the crane survives every aspect. The crane
apex joins the beacons list, so it blinks red for free.

A rare helicopter crosses above the skyline, slot-scheduled like the
meteors and bird flocks. Unlike the birds it flies at night — carrying
a blinking anti-collision light — but storms, fog, and a heavy deck
still ground it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sweep after re-reviewing the branch. SceneryLayer and SceneryGlyph
collapse to one-line data classes; multiline expressions get their
grouping blank line (mountain ranges, crane parts, sloop hull, barn
body); the usedSetback condition unwraps to a single soft-wrapped line;
the windmill sail color gets a named val instead of riding inline as a
long call argument. The SceneryOutlines doc stops claiming accents are
stroked in the near tone — they've been weathered wood since the fence
got painted. Tests drop the fully-qualified kotlin.math.abs for an
import, and the beach test finally admits its ships became one sloop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Attacktive Attacktive self-assigned this Aug 15, 2026
@Attacktive Attacktive added the enhancement New feature or request label Aug 15, 2026
@codacy-production

codacy-production Bot commented Aug 15, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 91 complexity · 0 duplication

Metric Results
Complexity 91
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Codacy flagged drawHelicopter at 51 lines of code against its limit of
50 — stricter than the detekt.yml LongMethod threshold of 60, so the
local build had nothing to say about it.

Splitting it along the seam it already had: helicopterPass answers
whether a helicopter is crossing right now and where, drawHelicopter
just draws the one it is handed. The random draws happen in the same
order as before, so the schedule and every position it produces are
unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Attacktive
Attacktive merged commit 504c07b into main Aug 15, 2026
6 checks passed
@Attacktive
Attacktive deleted the feature/painted-sceneries branch August 15, 2026 15:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant