Docs Extensões de jogo

Interface e entrada

Botões e cartões de escolha do canvas ficam clicáveis, com destaque automático ao passar o mouse; registre teclas de atalho, clique no chão para escolher uma posição, leia para onde o mouse aponta e saiba quem o jogador local selecionou. Cliques, teclas de atalho, feitiços lançados, o texto completo do chat e jogadores que saem entram no fluxo de eventos.

O que o canvas desenha agora pode ser clicado. O runtime assume a entrada da janela do jogo, e um programa externo pode:

RecursoEm uma fraseO jogo recebe?
Itens clicáveis do canvasBotões, cartões de escolha, painéis: o clique gera ui.click, e o destaque ao passar o mouse é automáticoO clique sobre o botão não chega ao jogo
Teclas de atalhoRegistre combinações como F5 ou ctrl+shift+Q; ao pressionar, gera hotkeyPode ser engolida (junto com o caractere que ela produziria)
Clique no chãoUm clique no mundo gera mouse.world, com as coordenadas no chãoPode ser engolido (“clique numa posição para pôr a torre”)
Posição do mouseAtualizada a cada frame: pixels da tela, ponto do chão sob o cursor, item do canvas sob o cursor—
SeleçãoQuem o jogador local selecionou; a cada mudança, gera selection.changed—

Tudo é entrada local + desenho local: nada entra no fluxo de comandos do jogo, então é seguro também em partidas multijogador. Mas, se o callback alterar o mundo (criar unidades, mudar atributos), aí só funciona em partida solo.

Python: g.ui

ui = g.ui                                                   # no primeiro uso, o runtime assume a entrada da janela
ui.button("shop", "Comprar uma poção (50 de ouro)", screen=(40, 300), on_click=lambda g, ev: buy(g))
c = ui.choice("Subiu de nível! Escolha uma recompensa", [("Força +5", "Aguenta mais"), ("Vel. de ataque +20%", "Bate mais"), ("Invocar lobo", "Mais um ajudante")],
              pause=True, on_pick=lambda g, i: give(g, i))  # uma fileira de cartões no meio da tela; pause=True pausa o jogo durante a escolha
i = c.wait(timeout=30)                                      # também dá para esperar bloqueando (os eventos continuam sendo processados, nada se perde)
ui.hotkey("F5", lambda g, ev: g.say(hero, "Entendido!"))    # engolida por padrão
ui.hotkey("ctrl+shift+Q", on_press=..., swallow=False)
ui.mouse(on_click, capture=True, buttons=("left", "right"))  # captura cliques no chão: informa botão esquerdo e direito, e os engole
xy = ui.pick_point("Clique no chão: onde fica a torre?")    # versão bloqueante: próximo clique esquerdo no chão -> (x, y); Esc ou timeout -> None
ui.cursor()                                                 # {'screen': (x, y), 'world': (x, y, z) ou None, 'hover': 'shop'}
ui.toast("A onda 3 chegou!", seconds=3)
ui.close()                                                  # recolhe os seus controles e teclas de atalho; só devolve a entrada da janela se nenhum outro programa estiver usando a entrada
g.close()                                                   # ou desconecta de vez (também dá para escrever with Game(...) as g:)

Os callbacks recebem (g, ev) e disparam quando você chama g.events() — o executor de Bots e o de mods de jogabilidade chamam isso a cada tick. Cliques sem callback vão para ui.clicks. Uma exceção lançada dentro de um callback só vai para o log, sem afetar os outros callbacks nem os eventos.

Também dá para usar o canvas diretamente: g.canvas.text(..., clickable=True, hover=cor); os cliques chegam pelo fluxo de eventos, e ev.key é a key dada ao desenhar. Desenhar um elemento clicável liga a entrada automaticamente, sem precisar mexer em g.ui antes.

Como escrever teclas de atalho: F1 ~ F24, A ~ Z, 0 ~ 9, numpad0 ~ numpad9, space enter esc tab backspace insert delete home end pageup pagedown left up right down, com os prefixos opcionais ctrl+, shift+, alt+.

Letras e números sem tecla modificadora entram em conflito com a digitação no chat e com os atalhos do jogo. Prefira teclas que o jogo não usa, como F5 ~ F8, ou combinações.

Novos eventos

g.events() passa a trazer estes (campos completos em Protocolo W3P):

kindQuandoCampos de conveniência
ui.clickUm item interativo do canvas foi clicado.key key do canvas, .button ('left' / 'right'), .mods teclas modificadoras
ui.hoverO mouse entra / sai de um item do canvas.key (None ao sair)
hotkeyUma tecla de atalho registrada foi pressionada.key a tecla como foi registrada, .mods
mouse.worldCom os cliques no chão ativados, um clique no mundo.x .y coordenadas no chão, .button, .value (1 = engolido)
selection.changedA seleção do jogador local mudouUse g.selection() para obter as unidades
spell.castUma unidade lançou uma habilidade (que entrou em recarga).spell código de quatro caracteres, .b nível, .value recarga em segundos, .x .y ponto de lançamento
messageApareceu uma linha numa caixa de mensagens da tela.text texto completo, .frame qual caixa, .chat (quando é chat)
player.leftUm jogador saiu ou foi removido por derrota.player
game.endedSaída da partida—

Chat e mensagens na tela

O que o jogador digita no chat é lido direto de .chat no evento message:

for ev in g.events():
    if ev.kind == "message" and ev.chat and ev.chat["text"] == "-follow":
        ...                                    # ev.chat = {'channel': 'Todos', 'sender': 'nome do jogador', 'text': '-follow'}

g.messages() tem um cursor próprio e independente, e nele também aparecem as dicas do jogo (“Você precisa de mais fazendas”, “Não é possível construir aí”). Ao escrever um Bot, use-o para saber por que um comando não foi executado.

Usando a partir de outras linguagens

  • Gateway: os métodos ui.button, ui.choice, ui.hotkey, ui.mouse, ui.cursor estão disponíveis com o mesmo nome no gateway. Um cliente remoto não pode passar funções de callback: cliques e teclas de atalho chegam pelos eventos enviados (o evento ui.click traz a key).
  • Escrevendo direto na memória compartilhada: envie primeiro o comando semântico input_enable (opcode W3P 74), e o runtime passa a assumir a entrada; no bloco de entrada Local\War3Input_<pid>, você escreve a tabela de teclas de atalho e as chaves do mouse, e ele devolve a posição do mouse, o ponto do chão sob o cursor e o item sob o cursor. O bit 0x40 nas flags de um item do canvas significa “interativo”. O layout está em Protocolo W3P.

Vários programas ao mesmo tempo

Mods, Farsight, MCP e cada sessão do gateway podem pôr botões e registrar teclas de atalho na mesma partida ao mesmo tempo, sem um atrapalhar o outro:

  • Cada programa registra as próprias teclas de atalho e a própria chave de cliques no chão, e o SDK junta tudo numa tabela só para o runtime. Cada tecla aparece uma vez só, os eventos vão para todos, e cada um reconhece as próprias teclas de atalho pela tecla;
  • ui.close() só retira o que é seu, e a entrada da janela só é devolvida quando o último programa sai;
  • Se um programa for encerrado à força, sem tempo de arrumar a casa: o runtime confere a cada 2 segundos e, quando todos os programas registrados já saíram, limpa as teclas de atalho e a interceptação de cliques no chão que eles deixaram, e os botões que desenharam deixam de interceptar cliques.

Medições

2026-09-25, verificação em partidas reais numa instância de teste, 16/16:

  • Clicar no botão → ui.click + callback, e o contador de interceptações do runtime sobe 1 (o jogo não recebeu esse clique); clicar fora do botão não dispara nada;
  • F6 → hotkey; clique no chão → mouse.world (engolido);
  • Criar um paladino e selecioná-lo → selection.changed, e g.selection() confere; lançar Escudo Divino → spell.cast('AHds', 1, 35.0);
  • Texto do mapa → message; chat → message, com .chat separando quem falou e o conteúdo;
  • Derrotar o computador → player.left; encerrar a partida → game.ended.

Cliques de uma pessoa real nos botões e o destaque ao passar o mouse também foram conferidos um a um.

Limites e cuidados

  • A posição vem do mouse real: o próprio jogo lê a posição pelo cursor do sistema, então o destaque e cursor() refletem o mouse real. A interceptação só cuida dos cliques e das teclas.
  • Desenhado abaixo do cursor do mouse: o Warcraft desenha o cursor como parte da imagem, a cada frame. O canvas e os balões de fala são desenhados antes do passo em que o jogo desenha o cursor: cobrem a interface do jogo e ficam cobertos pelo cursor. Só quando o cursor não é desenhado num frame (escondido ou em cinemática) o desenho volta a ser feito no último passo.
  • Escala do sistema: se você escrever seus próprios testes e enviar cliques por mensagens de janela, as coordenadas enviadas por um processo sem reconhecimento de DPI são ampliadas pelo sistema (medido: ×1.5 com escala de 150%). Declare o programa de teste como DPI-aware antes. Cliques de uma pessoa real não são afetados.
  • Na primeira vez, as fontes precisam ser pré-aquecidas, o que leva cerca de 1 segundo. Nesse intervalo, os botões ainda não foram desenhados e não podem ser clicados.
  • Fora de uma partida, cliques no chão não são informados: no menu principal e na tela de resultados, mouse.world não é enviado nem engolido.
  • Se um clique foi engolido ao pressionar e, antes de soltar, você muda para outro programa ou arrasta o mouse para fora da janela, o estado também é reiniciado: a próxima soltura não é engolida junto.
  • A 1.27 não tem funções para criar novos frames de interface do jogo (elas só chegaram na 1.31): os botões e cartões daqui são desenhados pelo runtime, com estilo livre, mas não aparecem na hierarquia de menus do próprio jogo.