# ADR 0011: BNCentral GridVania chunking

**Date:** 2026-05-20
**Phase:** 07 (Workflow Smoke + Convention Locks, Plan 07-03)

[doc->REQ-MAP-02]

## Status

**Accepted** — locks the BNCentral GridVania chunk grid as **3×3 (9 chunks)** with
chunk dimensions `cols 2668/2664/2668 px, rows 2132/2136/2132 px`.

Re-evaluation gate: Phase 7 smoke-test (Plan 07-07) operator UAT reports
unacceptable lag on the 3×3 synthetic world. Absent that, locked through v1.2
BNCentral concrete conversion (out of v1.1 scope per ROADMAP). Concrete
BNCentral conversion is v1.2 work; this ADR locks the chunk-grid convention
only so v1.2 inherits a stable target.

## Context

BNCentral is the largest room in the legacy BNO client — **8000 × 6400 px** per
`extracted/client-5-8/rooms/0058-BNCentral/meta.json` (see CLAUDE.md
"Extracted Constants" table, "BNCentral room dimensions" row).

LDtk authoring is reported to lag significantly when a single Level's pixel
dimensions exceed roughly 5000 px on either axis. Upstream-reported instances:

- [deepnight/ldtk#1029](https://github.com/deepnight/ldtk/issues/1029)
- [deepnight/ldtk#1073](https://github.com/deepnight/ldtk/issues/1073)
- [deepnight/ldtk#985](https://github.com/deepnight/ldtk/issues/985)
- [deepnight/ldtk#1006](https://github.com/deepnight/ldtk/issues/1006)

Authoring BNCentral as a single 8000×6400 Level is therefore non-viable: the
operator workflow (open level, edit IntGrid + entities, save) would lag past
the point of usability.

LDtk's native escape hatch is **GridVania** world layout — Levels are tiled in
a fixed grid forming a single contiguous playable world. Chunking BNCentral as
GridVania keeps each authored Level small while letting the runtime stream
adjacent chunks for seamless traversal.

Operator preference (recorded in
`.planning/phases/07-workflow-smoke-convention-locks/07-CONTEXT.md` D-09): use
an **odd-dim** grid so the central hub area (BNCentral's namesake — the part
players spend the most time in) lives inside a single uninterrupted chunk
instead of straddling a 2×2 seam. The highest-value area becomes the easiest
to author, not the hardest. The smoke-test plan (07-07) also benefits: the
center chunk is the obvious "this is where players spawn and walk" focal
point of the synthetic world.

Also relevant: ADR 0009 (sibling Plan 07-01) locks the LDtk `defaultGridSize`
convention at **4 px**. Any chunking scheme must keep every chunk dimension
divisible by 4 so the gridSize=4 cell lattice tiles cleanly across chunk
boundaries.

## Decision

1. **Chunk grid = 3×3 GridVania** (9 chunks total) per D-09. The center chunk
   owns the BNCentral hub area uninterrupted — no central seam to author
   across or test cross-chunk transitions through. Authoring effort
   concentrates on edge / corner chunks, which carry less gameplay weight.

2. **Chunk dimensions per D-10:**
   - Column widths (left → right): `2668 / 2664 / 2668` px
   - Row heights (top → bottom): `2132 / 2136 / 2132` px

   Each chunk dimension is a multiple of 4 (honors gridSize=4 from ADR 0009).
   Every chunk sits well under the LDtk ~5000 px editor-lag threshold —
   maximum single-axis chunk dimension is 2668 px (about 53% of the lag
   threshold).

   **Arithmetic check (verbatim form per PATTERNS.md PITFALL 5):**

   ```
   cols: 2668 + 2664 + 2668 = 8000 ✓ (matches BNCentral pxWid)
   rows: 2132 + 2136 + 2132 = 6400 ✓ (matches BNCentral pxHei)
   all dims % 4 == 0 ✓ (honors gridSize=4 from ADR 0009)
   ```

   Per-chunk corner coordinates (world-space origin top-left, smoke-test
   recipe for Plan 07-07):

   | Position | World origin (worldX, worldY) | Chunk size (W × H) |
   |---|---|---|
   | NW corner | (0, 0)         | 2668 × 2132 |
   | N edge    | (2668, 0)      | 2664 × 2132 |
   | NE corner | (5332, 0)      | 2668 × 2132 |
   | W edge    | (0, 2132)      | 2668 × 2136 |
   | CENTER    | (2668, 2132)   | 2664 × 2136 |
   | E edge    | (5332, 2132)   | 2668 × 2136 |
   | SW corner | (0, 4268)      | 2668 × 2132 |
   | S edge    | (2668, 4268)   | 2664 × 2132 |
   | SE corner | (5332, 4268)   | 2668 × 2132 |

3. **ADR scope per D-11:** this ADR locks the chunk-grid CONVENTION ONLY.
   Concrete BNCentral conversion — i.e., authoring the 9 Level files with
   actual extracted tile and entity data, plus the runtime chunk-stream
   loader — is **out of v1.1 scope** (per ROADMAP "Carried out of v1.1
   scope"). It is v1.2 work. The Phase 7 smoke test (Plan 07-07) authors a
   **synthetic** 8000×6400 GridVania world using these exact chunk
   dimensions, proving the convention is editor-viable before v1.2 commits
   to converting the real room.

## Consequences

### Positive

- Central hub area sits in one uninterrupted chunk — highest-traffic gameplay
  surface is the easiest to author and the cheapest to test (no cross-chunk
  transitions inside the hub).
- All 9 chunks sit at ~2664/2668 px on the long axis — comfortably under the
  ~5000 px LDtk editor-lag threshold (about 53% of it). Editor stays
  responsive on commodity dev hardware.
- gridSize=4 alignment preserved: every chunk dimension is divisible by 4, so
  the IntGrid lattice tiles seamlessly across chunk boundaries with no
  half-cell artifacts at seams.
- Deferred concrete conversion gives the smoke-test (Plan 07-07) freedom to
  fail without blocking v1.1 close. The smoke proves the chunk-grid
  convention; v1.2 inherits a proven, stable target.

### Negative

- 9 chunks means 9 Level files (not one). Repo gets 9 `.ldtkl` external Level
  files plus the parent `.ldtk` world index instead of a single Level.
- Cross-chunk transitions at the 4 internal seams (between adjacent chunks)
  must be authored explicitly — collision, NPC pathing, and parallax must
  not break across seams. Deferred to v1.2 implementation; smoke test only
  proves the grid is *authorable*, not that gameplay flows seamlessly.

### Neutral

- 3×3 is odd, so any future 2×N or N×2 layout for a *different* zone is a
  separate decision. This ADR scopes only BNCentral; other zones (Bahoo,
  Schweisstar, smaller rooms) are not bound by 3×3 and will be evaluated
  case-by-case in v1.2+.

## Alternatives considered

- **Single 8000×6400 Level (no chunking).** Rejected: known LDtk editor lag
  above ~5000 px on either axis (deepnight/ldtk#1029, #1073). Operator
  workflow becomes non-viable before any real authoring can happen.
- **2×2 GridVania (4 chunks of 4000×3200).** Rejected: still near the
  ~5000 px editor-lag threshold (4000 px long-axis chunks). Worse, the
  central hub straddles all 4 chunks — the highest-value area becomes the
  hardest to author and test (4-way transition at the center seam). Inverts
  the "easiest where it matters most" principle behind D-09.
- **4×4 GridVania (16 chunks of 2000×1600).** Rejected: too many chunks for
  the gain. Central hub still straddles the 2×2 inner block. Transition
  authoring overhead scales with 16 Levels instead of 9. No editor-lag
  benefit beyond 3×3 — chunks are already well clear of the threshold at
  3×3.
- **3×4 or 4×3 mixed-asymmetric.** Rejected: asymmetric grid forces the
  operator (and future devs) to remember which axis has the extra row /
  column — easy source of off-by-one errors during authoring and during
  Plan 07-07 smoke-test recipe generation. Symmetric 3×3 is mentally
  cheaper.

## References

- `extracted/client-5-8/rooms/0058-BNCentral/meta.json` — 8000×6400
  source-of-truth (`pxWid` / `pxHei`).
- `CLAUDE.md` §"Extracted Constants" — BNCentral room dimensions row.
- `docs/adr/0009-ldtk-gridsize-convention.md` — gridSize=4 invariant that
  every chunk dimension here honors.
- `.planning/phases/07-workflow-smoke-convention-locks/07-CONTEXT.md` —
  D-09 (3×3 odd-dim hub-centric rationale), D-10 (chunk dimensions),
  D-11 (v1.2 deferral of concrete conversion).
- `.planning/research/v1.1/PITFALLS.md` §Pitfall #4 — BNCentral LDtk
  editor-lag rationale.
- deepnight/ldtk upstream issues: [#1029](https://github.com/deepnight/ldtk/issues/1029),
  [#1073](https://github.com/deepnight/ldtk/issues/1073),
  [#985](https://github.com/deepnight/ldtk/issues/985),
  [#1006](https://github.com/deepnight/ldtk/issues/1006).
