> For the complete documentation index, see [llms.txt](https://faction-os.gitbook.io/faction-os-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://faction-os.gitbook.io/faction-os-docs/docs/battlefield.md).

# Battlefield Visual System

## Status

The shipped battlefield is an asset-driven 2D React surface. The previous Three.js implementation was removed from current runtime paths during the 2026-05-28 battlefield migration. Historical details for that removed version live in `docs/battlefield-3d.md`.

## Runtime Stack

| Layer                   | Implementation                                                  |
| ----------------------- | --------------------------------------------------------------- |
| Full app component      | `apps/web/src/components/battlefield/Battlefield.tsx`           |
| Coordinate helper       | `apps/web/src/lib/battlefield2d.ts`                             |
| Celebration helper      | `apps/web/src/components/battlefield/achievementCelebration.ts` |
| App assets              | `apps/web/public/battlefield/`                                  |
| Public demo battlefield | `public-demo/index.html`                                        |
| Public demo assets      | `public-demo/assets/battlefield/`                               |
| Asset promotion record  | `docs/media/media-assets.md`                                    |

The full app uses React, DOM buttons, CSS effects, and optimized WebP art. There is no current Three.js, WebGL, canvas, raycaster, GLTF loader, or 3D runtime dependency in the battlefield path.

## Runtime Assets

| Asset                  | Full app path                                     | Public demo path                                     |
| ---------------------- | ------------------------------------------------- | ---------------------------------------------------- |
| Battlefield background | `apps/web/public/battlefield/background.webp`     | `public-demo/assets/battlefield/background.webp`     |
| Orc standee            | `apps/web/public/battlefield/heroes/orc.webp`     | `public-demo/assets/battlefield/heroes/orc.webp`     |
| Human standee          | `apps/web/public/battlefield/heroes/human.webp`   | `public-demo/assets/battlefield/heroes/human.webp`   |
| Elf standee            | `apps/web/public/battlefield/heroes/elf.webp`     | `public-demo/assets/battlefield/heroes/elf.webp`     |
| Undead standee         | `apps/web/public/battlefield/heroes/undead.webp`  | `public-demo/assets/battlefield/heroes/undead.webp`  |
| Lobster standee        | `apps/web/public/battlefield/heroes/lobster.webp` | `public-demo/assets/battlefield/heroes/lobster.webp` |

Approved references live under `assets/battlefield-2d/reference/`. The stable asset record in `docs/media/media-assets.md` records prompts, source paths, optimized paths, dimensions, byte sizes, decisions, caveats, chroma-key choices, and regeneration rules.

## Visual Asset Contract

The current 2026-05-28 generated asset set is approved for both the full app and public demo:

* one universal 16:9 tactical board background
* five emblem-backed bust standees for orc, human, elf, undead, and lobster
* no baked UI text, labels, health bars, logos, watermarks, buttons, cursors, or click affordances
* darker, lower-noise center play area so DOM labels, rings, and buttons remain readable
* faction identity conveyed through perimeter zones, silhouettes, accents, and CSS state treatment rather than separate board variants
* hero silhouettes readable at 44-64 px interaction scale

The app and public demo each load a 668,358 byte runtime set: a 195,330 byte background plus 473,028 bytes of standees. This is well under the 3 MB initial budget. Keep the current generated background unless browser critique shows scale or compression issues; it is 1672x941 rather than the ideal future 2048x1152 target, but remains a clean 16:9 asset.

Session 08 verified app/demo byte-for-byte parity on 2026-05-29 with:

```bash
npm run battlefield:check
```

The check validates the explicit six-asset manifest, app/demo SHA-256 parity, runtime byte budgets, public demo service-worker precache coverage, same-origin shell asset existence, public demo workspace import exclusions, and stable docs path references. The public demo service-worker cache version for this verified set is `factionos-demo-v9`.

When regenerating battlefield art, use the media-asset record as the source of truth. Generate one asset per prompt, place raw drafts in ignored `tmp/`, review references under `assets/battlefield-2d/reference/`, and promote only optimized runtime copies into app-owned paths.

## Interaction Contract

The full app battlefield preserves the product selection contract:

* reads heroes, missions, achievements, and selected hero state from `useGameStore`
* writes selection through `setSelectedHeroId`
* renders each hero as a native button with an accessible name
* toggles selection when the selected hero is clicked again
* clears selection through a native empty-terrain button layered under hero tokens
* keeps roster and hero detail drawer selection synchronized
* exposes `aria-pressed` for selected hero buttons
* revalidates selected hero state when the selected hero leaves the local store
* includes active mission context in hero token accessible names when the mission is present
* keeps failed background or hero image loads visually recoverable through explicit loading, loaded, and fallback DOM states

The public demo mirrors the same visible interaction locally without importing workspace packages.

## Positioning Model

Hero `position.x` and `position.z` remain the spatial inputs. The helper maps them to a clamped 2D board plane:

```
x: -5..5 -> left: 12%..88%
z: -5..5 -> top: 22%..78%
missing or non-finite values -> center
```

`Battlefield.tsx` applies those percentages to absolutely positioned DOM hero buttons. The button transform anchors the token base to the board while the standee art rises above it.

`battlefield2d.ts` also owns label placement for battlefield tokens. Labels stay below top and center tokens and move above bottom-edge tokens so selected or hover labels remain inside the 16:9 board. Degenerate custom limits fall back to bounded board coordinates instead of emitting non-finite positions.

## Visual States

The battlefield uses CSS instead of scene-graph mutation:

* all hero tokens render a static state badge
* active heroes get a stable label, ring, and glow
* awaiting input uses a dashed badge/ring plus visible ping treatment
* verifying uses a distinct ring treatment and static badge
* spawning uses a static badge plus short entrance treatment
* idle uses a neutral badge/ring treatment
* error state uses a stronger warning ring and badge weight
* selected state uses `aria-pressed`, a larger halo, and a persistent selected label
* dismissed heroes stay visible with dimmed, dotted, and struck-through treatment
* achievement unlocks create short DOM/CSS radial bursts
* reduced motion disables looping and burst animations while keeping static color, rings, and labels visible

The board itself has a generated tactical background, a CSS fallback gradient, a subtle grid overlay, and a center readability vignette. The board exposes `data-background-state`, `data-connection-state`, and `data-hero-count` so tests and future audits can distinguish loading, loaded, fallback, local snapshot, and empty states without relying on image internals.

Achievement bursts are capped at `CELEBRATION_MAX_CONCURRENT`. Timers are cleared when bursts expire, when capped bursts are removed, and when the Battlefield component unmounts. Fresh unlocks sort deterministically by unlock time and id before rendering.

## Scanner Camps And Scanner State

The shipped app renders scanner findings as honest Null Legion camps on the 2D battlefield. Current Quest Board codebase issues from the canonical `suggestion_update` snapshot reconcile through the browser-local `GameProjection` and `legionCamps` helpers before the battlefield reads them. The camp layer renders ranked camp, fort, and citadel buttons with safe sector labels, HP, issue counts, and bounded Champion Mode visibility for small rosters.

Every rendered camp maps to current issue ids. Activating a camp by pointer or keyboard opens the existing Quest Board surface filtered to those issue cards. The Banish control is dry: it calls the existing issue-dismiss path for current cards and does not create rewards, XP, loot, achievements, camp kills, camp damage, Dawn Report behavior, protocol events, server routes, or new storage keys.

The scanner state layer distinguishes the main board states at a glance: Dispatch Scouts for running manual scans, Golden Age for completed clean canonical snapshots, scan-not-run before a canonical scanner snapshot exists, scan-failed while preserving existing camps, zero-hero neutrality, and mock drill labeling. Reduced-motion mode removes camp and scout animation while keeping static labels, tier, sector, HP, issue-count chips, marker opacity, and scanner-state copy visible.

## Live Tier And Combat Playback

Phase 24 implementation evidence is recorded for the live tier and combat playback layer. The battlefield now reads projection-owned pending links and camp links to render linked strikes, counterattacks, combo stages, display-only HP drain, joint-assault trims, and verified burn-downs. It also renders live skirmishers from test failures, ram/gate state from build events, gremlin pressure, capped wraith stacks, siege pressure, scrying, and a single projection-owned alert focus.

The live tier preserves the existing battlefield contract:

* all Phase 24 state flows through `gameProjection.ts` selectors and presentation helpers, not a parallel game state source
* camps and live markers inspect through to real Quest Board issues, test paths, heroes, missions, tool events, or verification events
* `CombatPlaybackLayer`, `LiveLegionLayer`, and `ScryingAndAlertFocusLayer` use DOM/CSS labels, rings, data attributes, and screen-reader summaries rather than generated source-image paths
* reduced motion keeps static badges, labels, HP chips, alert state, and typed fallback posture visible while suppressing nonessential motion classes
* replay, reconnect, and mock traffic cannot persist live-tier kills, links, counters, or storage writes
* verified burn-down is display-only evidence from `task_verification`; Phase 24 adds no rewards, XP, loot, banners, protocol events, server routes, hosted dependencies, public-demo synthetic front, or trusted-erasure claim

Generated Phase 3 sheets under `assets/generated/game-design/phase03/` are typed implementation references only. They are pinned in `apps/web/src/lib/generatedPhase3Assets.ts` and cataloged in `apps/web/src/data/mediaCatalog.ts`, but runtime battlefield code must not load those source paths directly.

## Public Demo Boundary

`public-demo/` is still a no-build static artifact. It carries copied optimized assets under `public-demo/assets/battlefield/` and keeps demo-local positioning and state inline in `index.html`. It must not import `apps/web`, `@factionos/protocol`, or other workspace packages.

When battlefield assets change, update both app and demo runtime copies, run `npm run battlefield:check`, update `public-demo/sw.js`, bump the service worker cache version when the precache list changes or forced shell-cache content refresh is needed, and verify offline reload after one online visit.

Session 04 changes are app-side state, interaction, fallback, and reduced-motion contract updates only. Session 05 owns public-demo behavior parity, service worker cache updates, and any app/demo asset-copy validation.

## Scope Decisions

The 2D battlefield migration deliberately avoids direct promotion of legacy `EXAMPLES/` media, FBX/TGA/GLTF loader work, Phaser, PixiJS, canvas physics, and new gameplay-loop infrastructure. CSS/DOM owns labels, state effects, hit targets, reduced-motion variants, and achievement bursts. Future faction-aware board variants or keep/token images should be added only after browser visual QA proves the universal board is insufficient.

## Validation

Required checks for battlefield changes:

```bash
npm run battlefield:check
npm run test -- apps/web/tests/Battlefield.test.tsx apps/web/tests/BattlefieldInteraction.test.tsx apps/web/tests/battlefield2d.test.ts apps/web/tests/achievementCelebration.test.ts
npm --workspace apps/web run typecheck
npm run lint
npm run format:check
```

Browser validation should include full app and public demo desktop/mobile screenshots, failed-request checks, console/page-error checks, visible fallback states, nonzero battlefield dimensions, and offline public demo reload when service-worker assets or shell content change. Phase 02 Session 07 records this evidence for the current app and public demo surfaces, including forced app fallback art, public demo offline reload after one online visit, and the mobile public-demo header overlap fix.

Focused coverage currently lives in:

* `apps/web/tests/battlefield2d.test.ts`
* `apps/web/tests/Battlefield.test.tsx`
* `apps/web/tests/BattlefieldInteraction.test.tsx`
* `apps/web/tests/achievementCelebration.test.ts`
* `tests/e2e/app.e2e.ts`
* `tests/e2e/public-demo.e2e.ts`


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://faction-os.gitbook.io/faction-os-docs/docs/battlefield.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
