Structure level numbers: classic bitmap font by default + graphics toggle (#4264)

## What

Structure **level numbers** now render in the **`round_6x6_modified`**
bitmap font by default (matching the old PIXI-based `StructureLayer` /
`v31`), with a graphics setting to switch back to the smooth
`overpass-bold` MSDF font.

Two commits:

1. **Default to the classic bitmap font** — `StructureLevelPass` drew
level digits from the `overpass-bold` MSDF atlas (the one `NamePass`
uses for player names); switch the default to the `round_6x6_modified`
pixel font (white digits with a baked-in dark outline).
2. **Add a runtime toggle** — load both fonts and switch between them
live via a new `Classic level numbers` graphics setting.

## How

- `StructureLevelPass` loads both atlases up front and selects one per
frame from `settings.structureLevel.classicFont`, re-laying-out the
digits when the toggle flips (digit advances differ between the fonts).
The fragment shader is a single program with a `uClassic` branch: direct
bitmap sample (white fill + baked outline) vs. MSDF median + synthesized
outline.
- New override `structure.classicNumbers` in `GraphicsOverrides`
(default `true` = classic), applied onto
`settings.structureLevel.classicFont` in `applyGraphicsOverrides` — so
it switches live, like the existing colorblind/classic-icons toggles.
- `GraphicsSettingsModal` gets a `Classic level numbers` toggle next to
`Classic icons` (with `en.json` strings).

## Testing

- `tsc --noEmit`, ESLint, Prettier, and `npm run build-prod` all pass.
- Ran the game headless, built/upgraded cities to level 2–3, and
confirmed: the classic toggle renders the pixel font, flipping it
renders the smooth MSDF font, and flipping back restores the pixel font
— switching live with no shader errors.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Evan
2026-06-13 19:07:17 -07:00
committed by GitHub
co-authored by Claude Opus 4.8
parent f4db4a33c8
commit f76f133589
9 changed files with 245 additions and 69 deletions
@@ -328,6 +328,16 @@ export class GraphicsSettingsModal extends LitElement implements Controller {
this.patchStructure({ classicIcons: !this.currentClassicIcons() });
}
private currentClassicNumbers(): boolean {
return (
this.userSettings.graphicsOverrides().structure?.classicNumbers ?? true
);
}
private onToggleClassicNumbers() {
this.patchStructure({ classicNumbers: !this.currentClassicNumbers() });
}
private patchPassEnabled(patch: Partial<GraphicsOverrides["passEnabled"]>) {
const current = this.userSettings.graphicsOverrides();
this.userSettings.setGraphicsOverrides({
@@ -423,6 +433,7 @@ export class GraphicsSettingsModal extends LitElement implements Controller {
const hoverGlowAlpha = this.currentHoverGlowAlpha();
const namesColored = !this.currentDarkNames();
const classicIcons = this.currentClassicIcons();
const classicNumbers = this.currentClassicNumbers();
const highlightFill = this.currentHighlightFill();
const highlightBrighten = this.currentHighlightBrighten();
const highlightThicken = this.currentHighlightThicken();
@@ -637,6 +648,25 @@ export class GraphicsSettingsModal extends LitElement implements Controller {
</div>
</button>
<button
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
@click=${this.onToggleClassicNumbers}
>
<div class="flex-1">
<div class="font-medium">
${translateText("graphics_setting.classic_numbers_label")}
</div>
<div class="text-sm text-slate-400">
${translateText("graphics_setting.classic_numbers_desc")}
</div>
</div>
<div class="text-sm text-slate-400">
${classicNumbers
? translateText("user_setting.on")
: translateText("user_setting.off")}
</div>
</button>
<div
class="px-3 py-1 text-xs font-semibold text-slate-400 uppercase tracking-wider mt-2"
>