# JavaScript na ficha

> O rpgdesk.js pelo código: conectar, ler e gravar o documento, acompanhar as mudanças, campos calculados e listas.

Página: https://docs.rpgdesk.com/fichas/javascript/

Quando os atributos no HTML não bastam (uma conta, um botão especial, uma regra do seu sistema), o JavaScript entra. Tudo passa pelo objeto `RPGDesk`, que o rpgdesk.js cria. A lista completa das funções está em [A API do rpgdesk.js](https://docs.rpgdesk.com/referencia/api/).

## Conectar

```js
RPGDesk.connect().then(({ doc, me, item, room, kind, language, test }) => {
  // tudo o que usa o documento começa aqui
});
```

`RPGDesk.connect()` espera a conexão com o RPG Desk (ou com o modo de teste) e devolve o **contexto**:

| Campo      | O que é                                                                                      |
| ---------- | -------------------------------------------------------------------------------------------- |
| `doc`      | o documento da ficha (veja abaixo)                                                           |
| `me`       | quem está vendo: `{ id, nick, role, isOwner }`. `role` é `'gm'`, `'player'` ou `'spectator'` |
| `item`     | a ficha: `{ id, name, type, ownerId }`                                                       |
| `room`     | a mesa: `{ id, name, system }`                                                               |
| `kind`     | `'sheet'` (ficha) ou `'table'` (tabela da sala)                                              |
| `language` | `'pt'` ou `'en'`, a língua do RPG Desk de quem está vendo                                    |
| `test`     | `true` no modo de teste, fora do RPG Desk                                                    |
| `plugin`   | `{ id, version }` do seu plugin                                                              |

Pode chamar `RPGDesk.connect()` quantas vezes quiser: é sempre a mesma conexão. Depois de conectado, `RPGDesk.context` e `RPGDesk.doc` também dão o contexto e o documento.

## Ler e gravar

```js
doc.get('nome');                 // 'Aria'
doc.get('pv');                   // { atual: 18, max: 22 } (um grupo vira objeto)
doc.get('nao_existe');           // undefined

await doc.set('nivel', 4);                       // grava um campo
await doc.set('pv', { atual: 10, max: 12 });     // grava campo a campo (o que não veio fica)
await doc.set('anotacoes', null);                // apaga
await doc.update({ 'pv.atual': 3, 'pv.max': 10 }); // vários de uma vez
```

- Os valores são textos, números, verdadeiro/falso ou objetos com eles. Listas se gravam pela API de listas (veja abaixo).
- `set` e `update` devolvem uma promessa: a gravação vai ao RPG Desk, que confere a permissão e devolve a mudança em milissegundos.
- `doc.batch(() => { … })` junta várias gravações num lote só, aplicado inteiro ou recusado inteiro.
- `doc.canWrite('pv.atual')` diz se a pessoa pode escrever ali.

Uma gravação sem permissão (um jogador escrevendo na ficha de outro) é recusada. Para saber:

```js
doc.on('rejected', (mensagem) => RPGDesk.ui.toast(mensagem, 'error'));
```

## Acompanhar as mudanças

```js
const parar = doc.watch('pv', (pv, { local }) => {
  barra.style.width = `${(pv.atual / pv.max) * 100}%`;
}, { now: true });
```

- O `watch` avisa a cada mudança no caminho, ou em qualquer campo dentro dele, venha de quem vier.
- `{ now: true }` chama a função já, com o valor de agora.
- `local` diz se a mudança saiu deste navegador.
- O `watch` devolve a função que para de avisar.

## Campos calculados

Um valor que sai de outros (o modificador de um atributo, a carga total) se calcula com `doc.compute`:

```js
const mod = (valor) => Math.floor(((Number(valor) || 10) - 10) / 2);
doc.compute('mod_forca', ['forca'], mod);
doc.compute('defesa', ['destreza', 'armadura'], (des, arm) => 10 + mod(des) + (Number(arm) || 0));
```

`doc.compute(alvo, [fontes], função)` grava o alvo quando uma fonte muda. Mostre o resultado com `data-text` ou use nas rolagens (`data-roll="1d20 + {mod_forca}"`).

> **Não grave dentro de um watch**
>
> Todo mundo com a ficha aberta roda o mesmo código. Um `watch` que grava faria cada pessoa gravar de novo. O `compute` grava só no navegador de onde a mudança saiu, e só se a pessoa pode escrever no alvo.

## Listas

```js
const inv = doc.list('inventario');

inv.items();            // [{ id: 'i1…', nome: 'Corda', qtd: 1 }, …]
inv.length;             // 1
const id = inv.add({ nome: 'Tocha', qtd: 3 });   // no fim; inv.add(valor, 0) põe no começo
await inv.item(id).set('qtd', 2);
await inv.move(id, 0);  // muda a posição
await inv.remove(id);
inv.watch((itens) => totalEl.textContent = itens.length, { now: true });
```

- Cada item tem um `id` próprio, que o `add` devolve na hora.
- `inv.item(id)` tem `get`, `set`, `watch` e `remove`, com os caminhos relativos ao item.
- Os caminhos também podem ser listas: `['inventario', '#' + id, 'qtd']` é o campo `qtd` do item `id`.

## Eventos da mesa

```js
RPGDesk.on('players', (jogadores) => { /* entrou ou saiu alguém */ });
RPGDesk.on('me', (me) => { /* o meu papel mudou */ });
RPGDesk.on('language', (lingua) => { /* 'pt' ou 'en' */ });
RPGDesk.on('theme', (cores) => { /* as cores do tema mudaram */ });
RPGDesk.on('library', (itens) => { /* a biblioteca mudou */ });
```

`RPGDesk.on` devolve a função que para de ouvir.

## Uma ficha em duas línguas

Use `language` para trocar os textos:

```js
const TEXTOS = { pt: { rolar: 'Rolar' }, en: { rolar: 'Roll' } };
RPGDesk.connect().then(({ language }) => {
  for (const b of document.querySelectorAll('[data-roll]')) b.textContent = TEXTOS[language].rolar;
});
```

O nome do plugin e o título da ficha, no manifesto, também aceitam as duas línguas: `"title": { "pt": "Personagem", "en": "Character" }`. Veja [O manifesto](https://docs.rpgdesk.com/referencia/manifesto/).

## Quando dá erro

Um erro no seu código não derruba a mesa: a ficha mostra um aviso com o número de **Erros da página**, e o painel tem **Copiar erros**. No modo de teste, os erros aparecem no **Registro**. Veja [Testar e desenvolver](https://docs.rpgdesk.com/fichas/desenvolvimento/#os-erros-da-p%C3%A1gina).
