# Tilemap and Metatile Runtime Binary Layout Agenda

## Status

Open

## Purpose

Convergir a discussão sobre o contrato de mapa para handheld em duas camadas:

- formato de autoria/edição (JSON legível),
- formato de execução (layout binário compacto em runtime).

O objetivo é fechar qual estrutura será adotada para `tilemap bank`, `tileset bank` e dados de colisão/flags, com foco em previsibilidade de memória e streaming de mapas.

## Domain Owner

`docs/packer`

Este tema é owner de packer porque envolve transformação de artefatos (`JSON -> BIN`), contrato de build e layout de saída para consumo do runtime.

Domínios impactados (cross-domain):

- `docs/vm-arch` (contrato de leitura em runtime e limites de memória),
- `docs/studio` (edição/preview de mapas no workspace),
- `docs/compiler/pbs` (integração futura com referências de assets em código, quando aplicável).

## Problem

Hoje existe alinhamento conceitual de que:

1. visual e colisão devem ser separados por responsabilidade;
2. o mapa não deve repetir metadados extensos por célula;
3. apenas uma janela ativa de até `9` mapas deve ficar residente em memória.

Mas ainda não existe decisão formal sobre:

- layout binário final por célula em runtime,
- orçamento por mapa e por janela ativa,
- responsabilidade exata entre `map bank` e `tileset bank`.

Sem esse contrato, o packer não fecha especificação de saída e o runtime/studio ficam sem baseline único.

## Context

Premissas atuais da discussão:

- `tileset bank` pode ter tamanho diferente dos demais banks;
- `map bank` não precisa seguir o mesmo tamanho de `tileset bank`;
- mapa deve referenciar IDs compactos (visual, colisão e flags), em vez de duplicar estrutura completa por célula;
- paletas por bank continuam sendo opção válida para preservar decisões artísticas locais;
- orçamento alvo foi discutido no contexto de `64 KiB` por map bank e janela ativa de `3x3` mapas (`9` residentes).

## Options

### Option A — Célula `u8` (mapa ultra-compacto)

- Cada célula armazena apenas `metatileId` (`0..255`).
- Colisão/flags vêm de tabela auxiliar por `metatileId`.

### Option B — Célula `u16` bit-packed (recomendação inicial)

- Cada célula usa `16 bits` com divisão sugerida:
  - `visualId`: `10 bits` (`0..1023`),
  - `collisionId`: `5 bits` (`0..31`),
  - `flags`: `1 bit` (`0..1`) ou reservado para evolução.
- Permite desacoplamento visual/lógico sem custo de `u32`.

### Option C — Célula `u32` (maior flexibilidade)

- Exemplo de divisão:
  - `visualId`: `12 bits`,
  - `collisionId`: `8 bits`,
  - `flags/event`: `12 bits`.
- Ganho de expressividade para triggers/eventos inline; custo de memória dobra vs `u16`.

## Tradeoffs

- Option A minimiza RAM e I/O, mas limita variedade visual e desloca muita semântica para tabelas externas.
- Option B oferece bom equilíbrio para handheld: compacta, previsível e com espaço suficiente para muitos casos de mapa.
- Option C simplifica evolução funcional (eventos por célula), mas pressiona memória e banda de streaming sem necessidade comprovada agora.

## Runtime Binary Structure (focus)

Estrutura sugerida para runtime (baseada em Option B):

1. **Map Header (fixo)**
   - `magic` (`4 bytes`)
   - `version` (`u16`)
   - `width` (`u16`)
   - `height` (`u16`)
   - `cellEncoding` (`u8`) — ex.: `1 = U16_PACKED_V1`
   - `visualBankId` (`u16`)
   - `collisionBankId` (`u16`)
   - `reserved` / `checksum` (conforme decisão posterior)

2. **Cell Stream**
   - vetor contínuo com `width * height` células `u16`, little-endian;
   - leitura linear favorece cache e descompressão simples.

3. **Optional Chunks (future-proof)**
   - bloco opcional de `eventTriggers`;
   - bloco opcional de `spawnPoints`;
   - bloco opcional de `navHints`.

Packing de célula (`U16_PACKED_V1`):

- `bits 0..9`   => `visualId`
- `bits 10..14` => `collisionId`
- `bit 15`      => `flag0`

Decodificação de referência:

- `visualId -> metatile visual table -> 4 subtiles (8x8) + palette/flip/priority`
- `collisionId -> collision/material table -> walk/solid/swim/damage/etc.`

## Memory Notes for Active Window (`9` maps)

Assumindo `64 KiB` por map bank:

- `1` mapa residente: `64 KiB`
- `9` mapas residentes: `576 KiB`

Capacidade por encoding dentro de `64 KiB`:

- `u8`: `65,536` células (`256x256` máximo quadrado)
- `u16`: `32,768` células (`~181x181` máximo quadrado, ou retângulos equivalentes)
- `u32`: `16,384` células (`128x128` máximo quadrado)

## Recommendation

Adotar `Option B` como baseline para decisão:

1. autoria em JSON orientada a IDs (`visualId`, `collisionId`, `flags`);
2. empacotamento determinístico para `U16_PACKED_V1` no build;
3. janela ativa de runtime limitada a `9` mapas com budget explícito;
4. extensão para `u32` somente via nova versão de encoding e evidência de necessidade.

## Open Questions

1. `collisionId` com `5 bits` (`32` classes) é suficiente para os biomas/projetos previstos?
2. `flag0` deve ser reservado para trigger rápido ou para variação visual contextual?
3. Quais chunks opcionais entram já em `V1` e quais ficam para `V2`?
4. O `map bank` seguirá estritamente `64 KiB` ou terá tamanho variável com metadado de capacidade?
5. Qual política de compressão do stream (`none`, `LZ4`, etc.) será padrão no packer?

## Expected Follow-up

1. Abrir `decision` em `docs/packer/decisions` fechando o encoding `U16_PACKED_V1`.
2. Propagar contrato de leitura para `docs/vm-arch`.
3. Definir no `docs/studio/specs` o schema de edição JSON correspondente.
4. Planejar PR de implementação (`packer` + `runtime`) com testes de roundtrip (`JSON -> BIN -> decode`).
