# Room tables

> Pages for the whole room, open as a tab for everyone: an NPC list, initiative, a calendar. How to declare them, who writes and an annotated example.

Page: https://docs.rpgdesk.com/en/fichas/tabelas/

A **room table** is a page like a sheet, with two differences:

- there is **one per room**, and it stays open for everyone, as a tab;
- **only game masters write**; players see it.

They are for what belongs to the room, not to a character: the campaign’s NPCs, a calendar, the travel map, the factions’ reputation.

## Declare it

In the manifest, replace `"sheets"` with `"tables"`:

```json
"tables": [{ "type": "com.yourname.system.npcs", "title": "NPCs" }]
```

A single `.html` file brings one sheet **or** one table. A [package](https://docs.rpgdesk.com/en/fichas/pacotes/) can bring both, with one HTML for each.

## In the room

1. Install the plugin in **Room settings → Plugins**.
2. In the **… tables** group, below the plugin, click **Enable**.
3. The table opens for everyone as a tab that doesn’t come into view, next to the other tables (or in the last space used): click its name to see it.

Disabling it closes the table for everyone and keeps the data: enabling it again brings everything back as it was.

## In code

- `kind` is `'table'`, and `item.ownerId` is `null` (the table has no owner).
- The manifest’s `private` does not apply to tables: hide parts with `doc.restrict` (see [Private parts](https://docs.rpgdesk.com/en/fichas/partes-privadas/#on-tables-docrestrict)).
- The game master’s controls get `data-role="gm"`: for players, the fields already lock by themselves, but the create and delete buttons disappear.
- To change a player’s card, say whose: `RPGDesk.card.setBar(0, 7, 12, { userId })`, with the `id` from `await RPGDesk.room.players()`.

## Example: the NPC list

*Working example:* [Room NPCs](https://docs.rpgdesk.com/exemplos/en/npc-table.html)

The example is a list where each NPC has a name, HP, initiative, notes and a button to roll initiative, all with HTML attributes. The JavaScript does two things:

```js
RPGDesk.connect().then(({ doc, me }) => {
  const npcs = doc.list('npcs');
  const empty = document.getElementById('empty');
  // "No NPCs yet." disappears when the list has items
  npcs.watch((items) => { empty.hidden = items.length > 0; }, { now: true });
  if (me.role !== 'gm') return;
  // "Hidden from players": the NPC becomes game master only
  const seen = new Map();
  npcs.watch((items, { local }) => {
    for (const npc of items) {
      const hidden = npc.hidden === true;
      if (seen.get(npc.id) === hidden) continue;
      seen.set(npc.id, hidden);
      if (local) doc.restrict(['npcs', '#' + npc.id], hidden ? 'gm' : null);
    }
  }, { now: true });
});
```

- Only the browser of the game master who checked the box (`local`) calls `restrict`: the other game masters with the table open do not repeat the request.
- The hidden NPC really disappears for players: the server does not even send it.

> **Tip**
>
> In test mode, the table opens with the **GM** role. Switch to **Another player** on the band to see how players see it.
