# Canvas

> Desenhe caixas de texto, painéis, barras de progresso, imagens, círculos no chão e rotas com seta sobre a tela do jogo. O runtime desenha tudo a cada frame, sem alterar o estado do jogo, então é seguro em partidas multijogador; funciona por Python, HTTP ou escrevendo direto na memória compartilhada.

Fonte: https://war3ai.com/pt/docs/canvas/

Programas externos podem desenhar **caixas de texto, painéis, barras de progresso, imagens, círculos no chão e rotas no chão (com seta)** sobre a tela do jogo, e o runtime desenha tudo sozinho a cada frame. Serve para o seu próprio HUD, linhas de apoio, avisos, anotações didáticas e painéis de informação para lives.

## Canvas ou funções visuais do JASS: qual usar

| | Canvas (esta página) | [Funções visuais do JASS](https://war3ai.com/pt/docs/jass/) |
|---|---|---|
| Quem desenha | O próprio runtime | O próprio jogo (texto flutuante, efeitos, painéis, diálogo com retrato…) |
| Partidas multijogador | **Seguro**: só desenha na tela desta máquina, sem criar objetos nem alterar o estado do jogo | Só partidas solo |
| Estilo | Livre: fontes com suporte a chinês, cantos arredondados, semitransparência, bordas, qualquer cor, imagens locais | Estilo nativo do jogo |
| Acompanhar coisas | Segue unidades, coordenadas do mundo, posições da tela; círculos no chão acompanham o relevo | Depende da função |
| Custo | Medido: 0.2 ~ 0.35 ms por frame (9 elementos) | Cerca de 13 ms por chamada |

Dá para usar os dois juntos: efeitos com cara nativa pelo JASS; painéis, linhas de apoio e avisos personalizados pelo canvas.

## Python

```python
c = g.canvas                               # no primeiro uso, o runtime instala o hook de desenho (cerca de 0.1 s)
c.text("title", "Olá, este é o canvas", screen=(40, 110), color=(255, 220, 80),
       bg=(0, 0, 0, 170), border="#C49C40", size=22, bold=True)
c.panel("status", "Companheiro · Luz", ["Humor: feliz", "Abates: 12"], screen=(16, 330))
c.bar("hp", 0.62, unit=hero, lift=260, width=100, height=12, color=(80, 220, 80), text="62%")   # segue a unidade
c.text("tag", "O chefe vai soltar o golpe especial!", unit=boss, lift=320, color=(255, 80, 80), size=22, bold=True)
c.circle("danger", (x, y), 300, color=(255, 60, 60), fill=(255, 60, 60, 60), width=3)          # área de perigo no chão
c.circle("aura", hero, 450, color=(80, 200, 255, 220))                                         # círculo que segue a unidade
c.path("route", [(x1, y1), (x2, y2), (x3, y3)], color=(255, 220, 0), width=5, arrow=True)
c.image("icon", "icon.png", screen=(40, 170), width=64, height=64)
c.remove("danger"); c.hide("tag"); c.clear()   # clear só limpa o que você desenhou
c.expire("tag", 5)                         # some sozinho depois de 5 s
with c.batch(): ...                        # muda vários itens de uma vez, escrevendo na memória compartilhada uma vez só
c.stats()                                  # drawnFrames subindo = está desenhando de verdade
```

Cada elemento é identificado por uma `key`: desenhar de novo com a mesma key atualiza o elemento.

**Clicável**: acrescente `clickable=True` a caixas de texto e painéis (a cor ao passar o mouse é definida por `hover=`). Quando o jogador clica, chega um `ui.click` no fluxo de eventos, com `ev.key` igual a essa key, e o jogo não recebe esse clique. Botões, cartões de escolha, atalhos de teclado e cliques no chão prontos estão em [Interface e entrada](https://war3ai.com/pt/docs/ui-input/).

**Posição** (uma por elemento):

- `screen=(x, y)`: pixels da tela; números negativos contam a partir da direita / de baixo; `center=True` alinha pelo centro;
- `frac=(0.5, 0.1)`: fração da tela;
- `world=(x, y)`: coordenadas do mundo;
- `unit=unidade`: segue a unidade. Textos e barras no mundo e em unidades ficam com o ponto médio da borda inferior sobre aquele ponto; `lift` os levanta.

Por padrão, elementos no mundo e em unidades evitam o painel de comando embaixo e o relógio de dia e noite no topo (`over_ui=True` desenha por cima deles). **Cores** podem ser `(r, g, b)`, `(r, g, b, a)`, `"#RRGGBB"` ou `"#RRGGBBAA"`.

| Método | O que desenha | Parâmetros comuns |
|---|---|---|
| `text(key, texto, ...)` | Caixa de texto; várias linhas com `\n` | `color`, `bg` fundo (sem ele, transparente), `border`, `size`, `bold`, `shadow`, `width` (quebra a linha nessa largura), `radius` cantos arredondados |
| `panel(key, título, [linhas...], ...)` | Painel (fundo escuro semitransparente, borda dourada) | Os mesmos de `text` |
| `bar(key, 0..1, ...)` | Barra de progresso: vida, recarga, conjuração | `width`, `height`, `color`, `bg`, `border`, `text` |
| `image(key, caminho, ...)` | Imagem local (png / jpg / bmp / gif) | `width`, `height` (sem eles, tamanho original) |
| `circle(key, unidade ou ponto, raio, ...)` | Círculo no chão, acompanhando o terreno | `color` cor da linha, `fill` preenchimento (com transparência), `width` espessura da linha |
| `path(key, [pontos...], ...)` | Linha poligonal no chão | `color`, `width`, `arrow` seta no fim; os pontos podem ser coordenadas ou unidades |

## HTTP (qualquer linguagem)

Backend do Farsight (só escuta na máquina local):

```http
POST /api/instances/20/canvas
{"set": [
   {"key": "banner", "kind": "text", "text": "Canvas via HTTP", "frac": [0.5, 0.12], "center": true,
    "color": "#FFDC50", "bg": [0, 0, 0, 180]},
   {"key": "hp", "kind": "bar", "value": 0.8, "unit": 596125988, "lift": 260, "text": "80%"},
   {"key": "zone", "kind": "circle", "center": 596125988, "radius": 600, "color": [255, 200, 0], "width": 4},
   {"key": "route", "kind": "path", "points": [[-4587, -9092], [-5387, -8792]], "color": "#50C8FF"}
 ],
 "remove": ["old"], "clear": false}

GET  /api/instances/20/canvas        elementos desenhados agora + quantos frames já foram desenhados
```

`kind` é o nome do método em Python, e os nomes dos parâmetros também são os mesmos; para unidades, use o endereço `addr` do snapshot.

## Escrevendo direto na memória compartilhada

Também dá para dispensar o Python e o Farsight: envie uma vez o comando semântico `canvas_enable` (opcode W3P 73), e o runtime cria o bloco de memória compartilhada `Local\War3Canvas_<pid>`: cabeçalho de 64 bytes + 256 entradas × 112 bytes + pool de 64 KB para textos / pontos. A escrita segue o seqlock (o número de sequência fica ímpar → as entradas e o pool são escritos → o número fica par); o runtime lê uma vez por frame, reaproveita o frame anterior se pegar uma escrita pela metade e devolve o número de frames desenhados, o número de elementos e o contador de falhas. A implementação de referência em Python é `sdk/python/w3canvas.py`, e as estruturas estão definidas no header do protocolo; veja [Protocolo W3P](https://war3ai.com/pt/docs/protocol/).

## Vários programas desenhando ao mesmo tempo

Mods, Farsight, MCP e gateway podem desenhar na mesma partida ao mesmo tempo, mas só existe um canvas. A regra é: **cada programa só mexe nos próprios elementos**.

- Antes de escrever, pega-se um lock nomeado, leem-se os elementos existentes, mantêm-se os dos outros, trocam-se os próprios e grava-se tudo de volta;
- Cada elemento registra quem o desenhou (PID + número sequencial dentro do processo); quando o programa que o desenhou termina, ele é limpo na próxima vez que alguém escrever, e os botões dele deixam de interceptar cliques;
- Os números dos elementos vêm de um contador compartilhado, então não colidem.

O SDK em Python já faz assim, e `clear()` também só limpa o que é seu. Se você escrever direto na memória compartilhada, siga essas regras; do contrário, vai apagar o que os outros desenharam. Os detalhes do layout estão em [Protocolo W3P](https://war3ai.com/pt/docs/protocol/).

## Medições e cuidados

- Medido em 2026-09-25 (1920×1080, velocidade 2×): 9 elementos, 0.27 ~ 0.34 ms por frame, cerca de 63 frames / s, 0 falhas; escrever 9 itens leva 6 ms; com o herói andando, os círculos, textos e barras de vida que seguem unidades acompanham sem atraso. A textura só é redesenhada quando o conteúdo muda; mudar só a posição não redesenha.
- O canvas é desenhado depois da interface do jogo e antes do cursor do mouse: fica por cima das barras de vida, das unidades e da interface do próprio jogo, e o cursor fica por cima dele. Ele evita o painel de comando embaixo e o relógio de dia e noite no topo, mas **não evita os painéis do próprio mapa** (placar e contagem regressiva no canto superior direito) — não coloque os seus painéis no canto superior direito.
- Fora de uma partida (menu principal, tela de resultados), elementos posicionados em coordenadas do mundo ou em unidades não são desenhados; os que estão numa posição da tela continuam aparecendo.
- O círculo no chão projeta cada um dos 64 pontos da circunferência no terreno, então, quando o terreno tem altos e baixos, a forma acompanha o relevo — e isso está certo: ele é desenhado sobre o chão de verdade.
- Na primeira ativação, é preciso instalar o hook e pré-aquecer as fontes, o que leva cerca de 1 segundo; nesse intervalo, os elementos de texto ainda não aparecem, mas círculos e linhas sim.
- Se ocorrer uma falha durante o desenho, nada mais é desenhado nesta sessão (a mesma proteção dos balões sobre a cabeça), e `faults` em `stats()` passa a 1.
- Textos, caminhos de imagem e pontos somam no máximo 64 KB, com até 256 elementos; o caminho da imagem precisa ser um caminho local que o processo do jogo consiga ler.

O painel de status do [companheiro de IA](https://war3ai.com/pt/docs/companion/) é desenhado com o canvas: barra de vida, o que ele está fazendo, humor, abates e número de curas.
