Skip to content

Art pass slices 5-6: minimap, Inter, hit feedback, art checks, screenshots and streaming guide (close) - #54

Merged
tonianev merged 12 commits into
mainfrom
claude/confident-hopper-ho6dz1
Oct 28, 2026
Merged

tonianev merged 12 commits into
mainfrom
claude/confident-hopper-ho6dz1

Conversation

@tonianev

Copy link
Copy Markdown
Owner

What and why

The last two slices of the pre-stream art pass (ROADMAP "Art pass (pre-stream)"). After this the game is ready for the owner's Mac sign-off and the first gameplay stream.

Slice 5

  • Minimap (owner decision 7):
    • bottom-left, 192 px, north up, no fog, no pings;
    • shows terrain, territory and borders, Town outlines with an amber outline on a Town being annexed, every unit, the selection and the view trapezoid;
    • left click or drag moves the camera; right click orders through the new orders::ground_order, the same path as a world right click; attack-move works from it too.
    • --minimap-check plus a real-input xdotool drive.
    • Paint cost paint_us p95 is 290 to 400 µs on lavapipe.
  • Inter for every HUD, overlay and menu text, with tabular figures so the readouts don't jitter. A missing or unparsable font file warns and keeps Fira Mono. The HUD fit tests now measure real Inter advances through skrifa, a dev-dependency that adds no package to Cargo.lock.
  • Hit feedback (combat_fx.rs): arrows on a low arc, Mangonel stones and melee lunges. It is presentation only and runs on the fixed clock.
  • Owner decisions 15 and 16:
    • The Mangonel banner is now scale 450: 123‰ against its own 120‰ floor, and its reach dropped from 1.32 m to 0.60 m.
    • The Town and Ore Pit banners are unchanged.
    • The Seat-flag placement rule is recorded as accepted.

Slice 6

  • scripts/art_checks.sh (just art-checks) and scripts/art_drive.sh (just art-drive): click_check: ok 16/16 and minimap_check: ok with real X input.
  • CI game-linux renders art_bench and scripts/check_draws.sh --fight gates it below 500 draws. The gate also needs a measured fight window.
  • Five 1280x720 screenshots in docs/screenshots/art-*.png, checked row by row against the ART_STYLE checklist.
  • PLAYTEST "Owner's Mac checklist (art pass)", plus BUILD_TIMES rows, ART_STYLE, ui.md, ARCHITECTURE and HANDOFF updates.
  • Three good-first-issue texts in the ROADMAP. No GitHub issues were created.
  • docs/STREAMING.md: a runbook for the owner's stream, with every flag, path and number checked against the code. It covers:
    • the two formats, you vs the Standard bot and bot vs bot via --spectate;
    • 1280x720 logical size;
    • OBS settings on Apple Silicon, including the occluded-window freeze and the unfocused-window frame cap;
    • a pre-flight run and verifying the replay afterwards.

Review: four lenses (game, visual, tests and CI, docs and rules) raised 18 findings: 15 confirmed and fixed in four commits, 3 refuted.

Determinism: no fixture or .hash moved. rules_version stays 25 and SIM_VERSION 17. git diff --stat 15f1616..HEAD -- crates/sim/tests/fixtures is empty.

ROADMAP ticks come in one follow-up commit on this PR, naming it:

  • To be ticked: AP1, AP2, AP3, AP7, AP9, AP10 and M7's check_assets and data-check lines (owner decision 8), each with its pasted evidence.
  • Held for the owner's Mac session:
    • AP4: its "re-read on the Mac with its mode=" clause;
    • AP6: "The owner checks it by hand on the Mac";
    • AP5: fps;
    • AP8: screenshot sign-off.

Tier

Code PR

  • just ci: "ci: all steps passed", 849 passed, 21 ignored. machete and deny are not installed locally and nextest fell back to cargo test; CI runs all three.
  • Tests cover the change, with failing runs before the fixes and mutation checks recorded:
    • minimap: 15 tests, including the plugin-schedule border test and the real HUD layout;
    • fonts: 5;
    • fit tests: real Inter advances;
    • check_draws fails closed on fight_max=0;
    • the process checks match only this run's processes.
  • No f32/f64/HashMap/HashSet/Instant in sim, rules or ai, and no engine dependency outside crates/game.

Docs PR

  • typos and scripts/check_trademark.sh pass; links are relative.

Data PR

  • data-check: OK rules_version=25 rules_hash=0xfb0d0b8986fc8865 ... models=36.
  • cargo test -p sim: 313 passed, warm 8.7 to 9.1 s.

Licensing and disclosure

  • Contribution licence agreement: for the owner to tick.
  • Original assets: none (the screenshots are captures of the game).
  • AI assistance: written by Claude Code agents with adversarial review, per AI_CONTRIBUTIONS.md. Owner to confirm the review.

How to verify

just ci
just art-checks out          # every step PASS; draws max 109, fight_max 109 < 500 on art_bench
just art-drive out/drive     # click_check: ok 16/16; minimap_check: ok
scripts/draw_trace_xcheck.sh out/xcheck   # PASS in both modes (needs about 4 GB for a second build)
just verify                  # 11/11 OK

Then on the Mac: PLAYTEST "Owner's Mac checklist (art pass)", which covers the AP5 fps run, the AP8 sign-off, the AP4 mode= re-read and the AP6 minimap by hand. After that, docs/STREAMING.md.

Known variance: one just art-checks run failed the minimap paint gate with p95 575 µs, and the next passed at 304 µs with no code change. This is VM timing noise, recorded in PLAYTEST, and the Mac reading decides.


Generated by Claude Code

claude added 12 commits October 28, 2026 13:00
Owner decision 15 (2026-10-28): the Mangonel alone flies a smaller
banner, scale 450 (1.25 m, 123 permille), centred on the engine, and
inspect_gltf check lowers only the Mangonel's floor to 120 permille;
every other unit keeps 200. check now prints a unit banner's reach from
the unit's centre and notes one past the unit's radius (0.60 m against
0.5 m here, 1.32 m before). Owner decision 16 accepts the Seat flag's
placement rule in place of the 1.4x height ratio. ART_STYLE, the
visuals.ron comment and the ROADMAP review note record both answers.
fonts.rs installs assets/fonts/InterVariable.ttf over Bevy's built-in
Fira Mono at AssetId::default() in Startup, read with std::fs before the
first PostUpdate registers fonts; a missing file is a warning and Fira
Mono stays. The HUD fit tests drop the 0.6 em monospace helpers and
measure every line with Inter's real advance widths through skrifa
0.42.1, a game dev-dependency on the copy parley already locks (no new
package in Cargo.lock; one dependency edge on game).
…l-input check

Owner decision 7: a 192 px minimap bottom-left over the bottom bar, north
up, one texel per tile: terrain, territory and borders (rebuilt in the
FixedUpdate step that changed the field's border sources), buildings,
every unit (no fog yet), the selection, Town outlines (amber while
annexed) and the camera's view trapezoid. It repaints only when the
tick, camera, window, selection or territory changed and prints
frame_stats: minimap repaints/paint_us on exit. A press or drag moves the
camera; a right click issues orders::ground_order, extracted from the
right click's ground branch (Move, or SetRally for an own building); a
click with attack-move armed issues AttackMove. --minimap-check prints
four steps for a real-input driver and judges them. Unit dots are
brightened 45% rather than the plan's 15%, which left them unreadable on
their own territory.
combat_fx.rs reads SimEvent::Hit after the step and never writes to
the sim. The attacker's data picks the look: a building or a unit kind
with a range over one tile looses an arrow, the Siege class lobs a
stone on a 2 m arc, anything else lunges 0.2 m toward its target and
back. Arrows and stones are two shared meshes with two unlit materials
from a pool of 256 entities, flown in 0.25 s on the fixed clock the
units are interpolated with. Arrows take a low 0.8 m arc: flown straight
at chest height they stayed inside the figures between archer and target
and did not show in the fight shots. Docs and the ROADMAP slice 5 note
record what landed; no acceptance line is ticked.
scripts/art_checks.sh (just art-checks) runs the art pass's local
proxies: data-check, the Yeoman swap walkthrough on a scratch copy of
data/ (AP2), inspect_gltf check (AP3), check_assets and its self-test
(AP1), and five background-mode runs at 1280x720 under lavapipe that
save the four sign-off screenshots and the dev panel's, each run's draw
calls gated under 500 and its replay verified. scripts/art_drive.sh (just
art-drive) takes over the slice 4 and 5 scratch drivers: xdotool clicks
every unit of --click-check and performs --minimap-check's four steps.
scripts/check_draws.sh is the shared gate on a `frame_stats: draws` line.
CI's game-linux job renders art_bench too (tick 200, a 120 s deadline:
tick 300 came 76 to 87 s in on the VM) and gates every render smoke's
draws under 500.
docs/STREAMING.md is the stream set-up for the dev Mac: the two commands
(a match against the Standard bot with --skip-menu, and --spectate --seed
1, the m5_first_match setup decided at tick 26012), why 1280x720, why
--draw-stats, the occlusion and focus settings that change what is
presented, OBS on Apple Silicon, a five-minute pre-flight that reads the
frame_stats exit lines and verifies the replay, and what to keep after
the stream. Every flag, path and number was checked against the code:
the camera's 35 m default zoom, --fast-forward-to's refusal and
--spectate requirement, the replay directory and file name,
WinitSettings::game() in Bevy 0.19.1, and the exit lines' names.
…hecklist

The five 1280x720 screenshots come from just art-checks under lavapipe,
re-encoded losslessly: art-units-40m, art-town-40m, art-battle-60m and
art-match-minimap for the owner's sign-off (AP8), and art-dev-panel for
the counter (AP7). PLAYTEST gains the art pass's proxies with their
observed output, the owner's Mac checklist (AP5's fps and headroom runs,
the dev panel, the four shots on the P3 display and as sRGB, the minimap
by hand, M3b and M4b line 2 with the models) and the screenshots table.
BUILD_TIMES records the slice's measurements, ART_STYLE the measured
checklist row and the art pass's milestone row, ui.md the draw-call gate,
ARCHITECTURE the scripts and the art pass, and HANDOFF that the pass is
ready for the owner's Mac sign-off and the first stream. ROADMAP gains
the slice 6 note, the art pass's three good first issues with how to
verify them, notes on M3b and M4b line 2 and on M7's minimap, fps and
screenshot lines, M7's pulled deliverables and its estimate of 2-3
sessions. No acceptance line is ticked: each tick names the PR that
observes it.
install_ui_font now registers the file's bytes in a throwaway FontCx, the
way load_font_assets_into_font_collection will, before it replaces Fira
Mono at the default font id. Font::from_bytes never parses, so an empty,
truncated or garbage InterVariable.ttf used to replace Fira Mono with a
font Bevy then registered no family for, and every HUD, menu and overlay
text drew nothing with no warning. Such a file is now a warn! and Fira
Mono stays (an_unparsable_font_file_keeps_fira_mono).

Inter's default figures are proportional, so with FiraMono gone every
stock, rate, pop count and countdown that changed by a digit moved the
readouts beside it. fonts::hud_font gives every HUD, overlay and menu
text Inter's tabular figures (OpenType tnum). every_hud_text_uses_tabular_figures
checks the spawned texts' TextFont, and
readouts_hold_their_place_when_a_number_changes lays the top bar out
with Bevy's own text measure and UI layout (the new test-only ui_test
module) and checks that no readout moves when the stocks go from 111 to
444. The fit tests measure figures in their tabular form, read from
Inter's GSUB tnum lookups.
…the plugin's schedule

minimap_overlaps_no_hud_text rebuilt the message line, the annexation
readout and the minimap frame from constants and assumed centred text,
so a change to any of those nodes (FlexStart on the message line, say)
went unseen. It now spawns the real HUD, message line, readout and
minimap at 1280x800 and lays them out with Bevy's text measure and UI
layout (ui_test), writing every text each line can show at its longest
into the real text node and comparing the laid-out rects.

a_new_border_source_repaints_territory_in_the_same_update ran its own
Update chain, not MinimapPlugin's. The new
the_plugin_shows_a_new_border_in_the_update_of_its_tick runs SimPlugin and
MinimapPlugin on their own schedule with a Watchtower placed by the
scenario stream and checks, in the very update whose tick gives the tower
its land, that the territory layer was rebuilt once and the image shows
the won tiles; ordering sync_territory_layer before SimSystems::Step now
fails it.

The module doc and the right-click test claimed the minimap moves the
camera after the outcome. The outcome overlay covers the minimap and
takes its presses, so the doc now says so and the test only claims that
the handler does not gate camera moves on MatchOver.
…n's processes

check_draws.sh --fight accepted fight_max=0, which the game prints when
no counted frame fell inside the fight window, and fight_max is never
above max, so the flag added no check: a run whose armies never fought
passed AP4's fight clause in CI and art_checks. With --fight the field
must now be non-zero and the run's `frame_stats: fight` line must show
fight frames and its ticks (not `none`).

art_checks.sh step 10 matched `pgrep -x eonmark`, which never sees the
step 9 dev binary (eonmark-dev), and both scripts matched any Xvfb or
eonmark on the machine, so another session's display failed them.
art_checks now finds its own games by their `--replay-dir <out>`
argument and its own Xvfb by the auth file it gives xvfb-run; art_drive
keeps the id and name of each game and Xvfb it starts and checks those.
…abular figures

PLAYTEST's caption for art-battle-60m.png named both Towns' buildings,
but the shot holds P0's Seat, its eleven buildings and three sites; P1's
mirrored Town is off-screen, on the minimap only. The match shot's
minimap outlines four Towns, two a player. The sign-off note pointed the
owner at the blue buildings' terracotta wood only; the blue Mangonel and
Supply Wain are mostly the same wood, and on art-units-40m.png their
saturated pixels count more red than blue (343 against 121, 227 against
54), so the note now names them with the same lever. Owner decision 15
also reads "no crowd clipping": scale 450 still reaches 0.10 m past the
Mangonel's radius and a banner inside it would miss the 120 permille
floor, so ROADMAP, ART_STYLE, inspect_gltf's comment and the sign-off
note record that and ask the owner to confirm. ART_STYLE called the
scale-450 pole half as thick; it is 450/700, about two-thirds.

fog.md now cites the plugin-driven border test for AP6's "within one
frame", ROADMAP gains the review's note, and PLAYTEST's proxy rows the
re-run evidence, including one art-checks run whose minimap paint p95 read
575 us and art_drive's ungated short-run p95. The five screenshots are
retaken by just art-checks at b5f7154 (lossless re-encode), so the HUD
shows Inter's tabular figures.
AP1, AP2, AP3, AP7, AP9 and AP10, and M7's check_assets and data-check
lines (owner decision 8), each with the evidence observed at the review
fix round's head. AP4 and AP6 wait for their Mac clauses, and AP5 and
AP8 are the owner's; a Notes line says so.
@tonianev
tonianev merged commit 237644e into main Oct 28, 2026
5 checks passed
@tonianev
tonianev deleted the claude/confident-hopper-ho6dz1 branch October 28, 2026 19:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants