[
 {
  "name": "status",
  "category": "meta",
  "status": "verified",
  "mechanism": "Bloco de mundo + via rápida + tabela de reivindicação",
  "latency": "Cálculo local",
  "signature": "status() -> 'dict'",
  "doc": "Estado da conexão: pid, publicação do mundo (período, tempo de coleta), contadores da via rápida."
 },
 {
  "name": "snapshot",
  "category": "observe",
  "status": "verified",
  "mechanism": "Bloco de mundo W3P Local\\War3World_<pid> (o runtime envia a cada 50 ms, seqlock)",
  "latency": "Snapshot enviado",
  "signature": "snapshot(max_age: 'float' = 0.05)",
  "doc": "Estado completo do mapa inteiro (WorldState): .units .players .items .clock .me; chamadas repetidas dentro de max_age segundos retornam a mesma cópia.\n⚠ Trabalhadores dentro de uma mina de ouro não aparecem na tabela; por padrão o mapa inteiro é visível (no modelo lockstep, a máquina local tem tudo); só Game(fair=True) filtra pela visão."
 },
 {
  "name": "last_seen",
  "category": "observe",
  "status": "verified",
  "mechanism": "visibleTo do snapshot enviado (a cada atualização do snapshot, registra as unidades inimigas/creeps visíveis)",
  "latency": "Snapshot enviado",
  "signature": "last_seen(owner: 'str | int' = 'enemy', max_age: 'float | None' = None) -> 'list'",
  "doc": "Unidades inimigas (ou 'creep' para creeps, ou um número de jogador) vistas pela última vez: [(a unidade como estava naquele momento, o relógio de jogo naquele momento, quantos segundos se passaram)], as mais recentes primeiro.\nSe você a vê morrer, ela sai da tabela. Tanto o modo justo quanto o normal registram pelo critério \"o que nós vemos neste momento\" — é o mapa que o jogador tem na cabeça:\no exército que você reconheceu, onde viu o herói inimigo pela última vez, quando o adversário abriu a expansão. max_age limita aos últimos tantos segundos de jogo."
 },
 {
  "name": "map",
  "category": "observe",
  "status": "verified",
  "mechanism": "Bloco de mapa W3P Local\\War3Map_<pid> (o runtime calcula em lotes depois do início da partida; IsTerrainPathable para caminhar/construir)",
  "latency": "Snapshot enviado",
  "signature": "map()",
  "doc": "A tabela de terreno desta partida, MapInfo: .walkable(x,y) .buildable(x,y) .at(x,y) .bounds (área jogável) .starts (pontos de início) .cells (bit0 não caminhável, bit1 não construível).\nLeva alguns segundos para ficar pronta depois do início da partida; antes disso retorna None. As árvores não estão incluídas (use trees())."
 },
 {
  "name": "me",
  "category": "observe",
  "status": "verified",
  "mechanism": "Cabeçalho do bloco de mundo",
  "latency": "Snapshot enviado",
  "signature": "me() -> 'int | None'",
  "doc": "Qual é o meu número de jogador (0~11)."
 },
 {
  "name": "resources",
  "category": "observe",
  "status": "verified",
  "mechanism": "players[16] do bloco de mundo",
  "latency": "Snapshot enviado",
  "signature": "resources(player: 'int | None' = None) -> 'dict | None'",
  "doc": "{'gold','lumber','food_used','food_cap','gold_gathered','lumber_gathered'}; player é o nosso por padrão, e dá para ler de qualquer jogador.\nRetorna None se não der para ler — não trate como 0."
 },
 {
  "name": "players",
  "category": "observe",
  "status": "verified",
  "mechanism": "players[16] do bloco de mundo",
  "latency": "Snapshot enviado",
  "signature": "players() -> 'list'",
  "doc": "Todos os 16 slots de jogador: Player(id, gold, lumber, food_used, food_cap, gold_gathered, lumber_gathered, race, known)."
 },
 {
  "name": "units",
  "category": "observe",
  "status": "verified",
  "mechanism": "units[] do bloco de mundo",
  "latency": "Snapshot enviado",
  "signature": "units(owner: 'str | int' = 'all', types=None, alive: 'bool' = True) -> 'list'",
  "doc": "Filtra unidades por dono/tipo. owner: 'me' / 'enemy' / 'creep' / 'all' / número do jogador. types: conjunto de códigos de quatro caracteres."
 },
 {
  "name": "unit",
  "category": "observe",
  "status": "verified",
  "mechanism": "by_handle do bloco de mundo",
  "latency": "Snapshot enviado",
  "signature": "unit(handle) -> 'object | None'",
  "doc": "Encontra uma unidade pelo par de handles (lo, hi) (alvos de ordem, alvos de tarefa e eventos trazem pares de handles)."
 },
 {
  "name": "is_building",
  "category": "observe",
  "status": "verified",
  "mechanism": "Snapshot + units.json (spd==0 = construção)",
  "latency": "Snapshot enviado",
  "signature": "is_building(u) -> 'bool'",
  "doc": "Se é uma construção (incluindo torres). Decide pela velocidade de movimento 0 na tabela de unidades; a área ocupada da sede dos Mortos-vivos é 0, então não use a área ocupada para decidir."
 },
 {
  "name": "my_workers",
  "category": "observe",
  "status": "verified",
  "mechanism": "Snapshot enviado",
  "latency": "Snapshot enviado",
  "signature": "my_workers() -> 'list'",
  "doc": "Nossos trabalhadores (Camponês/Peão/Acólito/Wisp)."
 },
 {
  "name": "idle_workers",
  "category": "observe",
  "status": "verified",
  "mechanism": "Snapshot enviado (slot de ordem + slot de tarefa)",
  "latency": "Snapshot enviado",
  "signature": "idle_workers() -> 'list'",
  "doc": "Trabalhadores sem nada para fazer: sem ordem e sem tarefa (os que você acabou de designar neste tick não contam).\n⚠ Dar nova ordem de coleta a um trabalhador que tem tarefa interrompe o ciclo de coleta (a renda vai a zero)."
 },
 {
  "name": "my_heroes",
  "category": "observe",
  "status": "verified",
  "mechanism": "Snapshot enviado",
  "latency": "Snapshot enviado",
  "signature": "my_heroes() -> 'list'",
  "doc": "Nossos heróis vivos (os mortos ficam na lista de revivência do altar; veja revive)."
 },
 {
  "name": "my_army",
  "category": "observe",
  "status": "verified",
  "mechanism": "Snapshot enviado + units.json",
  "latency": "Snapshot enviado",
  "signature": "my_army() -> 'list'",
  "doc": "Nossas unidades de combate: nem trabalhadores, nem construções."
 },
 {
  "name": "my_buildings",
  "category": "observe",
  "status": "verified",
  "mechanism": "Snapshot enviado",
  "latency": "Snapshot enviado",
  "signature": "my_buildings(types=None) -> 'list'",
  "doc": "Nossas construções (incluindo torres e fundações em obra); types pode restringir a alguns tipos, como {'hbar'}."
 },
 {
  "name": "is_constructing",
  "category": "observe",
  "status": "verified",
  "mechanism": "Snapshot enviado (ordem = código de quatro caracteres da construção, ou ordem de construir/reparar)",
  "latency": "Snapshot enviado",
  "signature": "is_constructing(worker) -> 'bool'",
  "doc": "Se este trabalhador está construindo (ou indo construir / ajudando a reparar; inclui os designados neste tick). Pule-o ao escolher um construtor, senão a fundação anterior para."
 },
 {
  "name": "under_construction",
  "category": "observe",
  "status": "verified",
  "mechanism": "Snapshot enviado (a vida da fundação sobe de muito baixa até cheia)",
  "latency": "Snapshot enviado",
  "signature": "under_construction(building) -> 'bool'",
  "doc": "Esta construção ainda não ficou pronta (vida não está cheia). ⚠ Construções danificadas também não estão com vida cheia — basta para decisões na abertura, mas depois que a luta começa, combine com o tempo."
 },
 {
  "name": "gold_mines",
  "category": "observe",
  "status": "verified",
  "mechanism": "Snapshot enviado (ngol/egol/ugol)",
  "latency": "Snapshot enviado",
  "signature": "gold_mines() -> 'list'",
  "doc": "As minas de ouro do mapa. ⚠ A Mina de Ouro Enredada dos Elfos Noturnos e a mina neutra têm cada uma sua própria unidade na mesma coordenada; mande a coleta para a sua."
 },
 {
  "name": "enemies",
  "category": "observe",
  "status": "verified",
  "mechanism": "Snapshot enviado",
  "latency": "Snapshot enviado",
  "signature": "enemies(fighters_only: 'bool' = False) -> 'list'",
  "doc": "Unidades dos jogadores inimigos (sem creeps). fighters_only: remove trabalhadores e construções."
 },
 {
  "name": "creeps",
  "category": "observe",
  "status": "verified",
  "mechanism": "Snapshot enviado (owner 12 = neutro hostil)",
  "latency": "Snapshot enviado",
  "signature": "creeps() -> 'list'",
  "doc": "Creeps (neutros hostis). ⚠ À noite a visão diminui; quando um acampamento distante cai na névoa de guerra, comandos de alvo contra ele são rejeitados (código de motivo 1001)."
 },
 {
  "name": "nearest",
  "category": "meta",
  "status": "verified",
  "mechanism": "Cálculo puro",
  "latency": "Cálculo local",
  "signature": "nearest(candidates, to)",
  "doc": "O mais próximo de to (uma unidade ou (x,y)); retorna None se não houver candidatos."
 },
 {
  "name": "life_mana",
  "category": "observe",
  "status": "verified",
  "mechanism": "hp/hpMax/mana/manaMax da unidade no bloco de mundo",
  "latency": "Snapshot enviado",
  "signature": "life_mana(u) -> 'dict | None'",
  "doc": "{'hp','hp_max','mana','mana_max'} (floats, valores brutos do engine). Para u, basta passar a unidade obtida do snapshot (ela é trocada pela cópia mais recente)."
 },
 {
  "name": "hero_info",
  "category": "observe",
  "status": "verified",
  "mechanism": "level/xp/skillPoints da unidade no bloco de mundo",
  "latency": "Snapshot enviado",
  "signature": "hero_info(hero) -> 'dict | None'",
  "doc": "{'level','xp','skill_points'}."
 },
 {
  "name": "abilities",
  "category": "observe",
  "status": "verified",
  "mechanism": "Detalhes do bloco de mundo: habilidades (código/nível/flags/recarga restante)",
  "latency": "Snapshot enviado",
  "signature": "abilities(u) -> 'list'",
  "doc": "[{code, level, cooldown, flags}]; os buffs ficam em buffs(u). Só unidades \"com detalhes\" têm isso (heróis > unidades de jogadores > creeps, até 256)."
 },
 {
  "name": "buffs",
  "category": "observe",
  "status": "verified",
  "mechanism": "Detalhes do bloco de mundo: objetos de habilidade que começam com B",
  "latency": "Snapshot enviado",
  "signature": "buffs(u) -> 'list'",
  "doc": "Códigos de buff na unidade (por exemplo, 'BHds' Escudo Divino, 'Bslo' Lentidão). O efeito de cada código está em data/game/buffs.json."
 },
 {
  "name": "cooldown",
  "category": "observe",
  "status": "verified",
  "mechanism": "Detalhes do bloco de mundo: recarga restante da habilidade (timer da habilidade)",
  "latency": "Snapshot enviado",
  "signature": "cooldown(u, ability: 'str') -> 'float | None'",
  "doc": "Quantos segundos (de jogo) faltam de recarga desta habilidade; 0 = pode lançar; retorna None se não tiver a habilidade (ou se a unidade não tiver detalhes)."
 },
 {
  "name": "inventory",
  "category": "observe",
  "status": "verified",
  "mechanism": "Detalhes do bloco de mundo: 6 espaços de inventário",
  "latency": "Snapshot enviado",
  "signature": "inventory(hero) -> 'list | None'",
  "doc": "Códigos de quatro caracteres dos itens nos 6 espaços (espaço vazio é None); retorna None se não houver inventário."
 },
 {
  "name": "current_order",
  "category": "observe",
  "status": "verified",
  "mechanism": "order / alvo da ordem / ponto-alvo da ordem da unidade no bloco de mundo",
  "latency": "Snapshot enviado",
  "signature": "current_order(u) -> 'dict | None'",
  "doc": "{'order','target','x','y'}: a ordem que a unidade está executando (order é 0x000D00xx ou o código de quatro caracteres de uma construção; 0 = ociosa).\ntarget é um par de handles; use g.unit(target) para obter a unidade."
 },
 {
  "name": "current_target",
  "category": "observe",
  "status": "verified",
  "mechanism": "Alvo da tarefa da unidade no bloco de mundo",
  "latency": "Snapshot enviado",
  "signature": "current_target(u)",
  "doc": "A unidade que ela está **de fato atacando/perseguindo** (None se não houver).\n⚠ Depois de uma ordem de ataque, o slot de ordem esvazia rápido e o ataque fica preso à tarefa — para saber \"quem ela está atacando\", use isto, não current_order."
 },
 {
  "name": "clock",
  "category": "observe",
  "status": "verified",
  "mechanism": "clockMs do cabeçalho do bloco de mundo (relógio de jogo do engine)",
  "latency": "Snapshot enviado",
  "signature": "clock() -> 'float | None'",
  "doc": "Relógio de jogo do engine (segundos de jogo; 0 durante o carregamento). Com a velocidade do jogo aumentada, ele anda mais rápido que o relógio real."
 },
 {
  "name": "production",
  "category": "observe",
  "status": "verified",
  "mechanism": "Tabela de produção do bloco de mundo (objetos de habilidade Aque/ABnP/AUnP + tempo decorrido acompanhado pelo runtime; erro medido < 0.2 segundo de jogo)",
  "latency": "Snapshot enviado",
  "signature": "production(building)",
  "doc": "O que esta construção está fazendo: Production(kind, queue, duration, elapsed, blocked, progress, remaining…); retorna None se não estiver fazendo nada.\n  kind 'queue' (treino/pesquisa/herói; queue tem até 7 espaços, [0] é o que está em andamento) / 'construction' (em obra) / 'upgrade' (melhoria da sede/torre);\n  blocked = há algo na fila mas não começou (quase sempre falta comida — hora de fazer uma Fazenda); progress 0..1.\nTambém funciona para construções do adversário (no modo justo, só para as construções visíveis)."
 },
 {
  "name": "queue",
  "category": "observe",
  "status": "verified",
  "mechanism": "Tabela de produção do bloco de mundo",
  "latency": "Snapshot enviado",
  "signature": "queue(building) -> 'list'",
  "doc": "Códigos de quatro caracteres na fila de treino/pesquisa ([0] em andamento); ociosa ou não é construção de produção = []."
 },
 {
  "name": "all_production",
  "category": "observe",
  "status": "verified",
  "mechanism": "Tabela de produção do bloco de mundo",
  "latency": "Snapshot enviado",
  "signature": "all_production(owner: 'str | int' = 'me') -> 'list'",
  "doc": "Toda a produção em andamento [(construção, Production)]. owner igual a units(): 'me' / 'enemy' / número do jogador / 'all'.\nUso profissional: ver que unidades o adversário está treinando, que tecnologias está pesquisando e quando sobe de tier (quando você reconhece as construções dele)."
 },
 {
  "name": "path_distance",
  "category": "observe",
  "status": "verified",
  "mechanism": "Bloco de mapa (IsTerrainPathable do engine) + bloco de árvores + área ocupada pelas construções, A* no lado do SDK (128 por célula)",
  "latency": "Snapshot enviado + cálculo local",
  "signature": "path_distance(a, b) -> 'float | None'",
  "doc": "Distância percorrida por uma unidade terrestre de a até b (a e b podem ser unidades ou (x,y)); None se não houver caminho. Em mapas com ilhas, use isto para saber \"dá para chegar por terra a este acampamento/expansão\";\né mais confiável que a distância em linha reta (contorna florestas, penhascos e construções). Precisão de uma célula de 128; frestas mais estreitas que uma célula contam como bloqueadas."
 },
 {
  "name": "reachable",
  "category": "observe",
  "status": "verified",
  "mechanism": "Igual ao anterior",
  "latency": "Snapshot enviado + cálculo local",
  "signature": "reachable(a, b) -> 'bool | None'",
  "doc": "Se dá para chegar por terra (bloco de mapa ainda não calculado = None)."
 },
 {
  "name": "walk_path",
  "category": "observe",
  "status": "verified",
  "mechanism": "Igual ao anterior",
  "latency": "Snapshot enviado + cálculo local",
  "signature": "walk_path(a, b) -> 'list | None'",
  "doc": "Pontos de virada do caminho [(x,y)...] (o último ponto é b); combine com path(units, lista de pontos) para a tropa seguir esse caminho (contornar torres, pegar trilhas secundárias)."
 },
 {
  "name": "upkeep",
  "category": "observe",
  "status": "inferred",
  "mechanism": "Regra fixa da 1.27: 0~50 de comida sem cobrança, 51~80 renda ×0.7, 81~100 ×0.4",
  "latency": "Snapshot enviado",
  "signature": "upkeep(player: 'int | None' = None) -> 'dict | None'",
  "doc": "Faixa de manutenção: {'level': 'none'/'low'/'high', 'income': 1.0/0.7/0.4, 'next_at': comida da próxima faixa (sem próxima = None)}.\nConhecimento de profissional: ao subir para o tier 3 ou fazer melhorias de ataque/armadura, pare em 50 de comida; só suba para 80 antes da batalha decisiva."
 },
 {
  "name": "xp_to_next",
  "category": "observe",
  "status": "verified",
  "mechanism": "level/xp do bloco de mundo + fórmula NeedHeroXP de MiscGame",
  "latency": "Snapshot enviado",
  "signature": "xp_to_next(hero) -> 'int | None'",
  "doc": "Quanta experiência falta para o herói subir de nível (nível 10 = 0)."
 },
 {
  "name": "creep_camps",
  "category": "observe",
  "status": "verified",
  "mechanism": "Snapshot enviado (creeps a até 600 uns dos outros formam um grupo) + nível de units.json",
  "latency": "Snapshot enviado",
  "signature": "creep_camps(link: 'float' = 600.0) -> 'list'",
  "doc": "Agrupa os creeps (visíveis) do mapa em acampamentos: [{'x','y','units','level','hp','max_level'}], do mais perto para o mais longe da nossa base principal.\nlevel = nível total do acampamento (a medida usual de dificuldade de creeping), hp = vida total. Combine com time_to_kill / path_distance para escolher o acampamento."
 },
 {
  "name": "buff_info",
  "category": "observe",
  "status": "verified",
  "mechanism": "data/game/buffs.json (BuffID de AbilityData.slk -> habilidade/efeito/duração)",
  "latency": "Dados locais",
  "signature": "buff_info(code: 'str') -> 'dict | None'",
  "doc": "O que é um código de buff: {'ability','effect','dur','hero_dur','targets'} (ex.: 'Bslo' -> Lentidão). Quando um código tem várias linhas, retorna a primeira."
 },
 {
  "name": "stats",
  "category": "observe",
  "status": "verified",
  "mechanism": "Tabelas de dados (UnitBalance/UnitWeapons/UpgradeData/MiscGame) + níveis de tecnologia em tempo real + nível do herói",
  "latency": "Snapshot enviado + via rápida (um lote a cada 5 s)",
  "signature": "stats(u, player: 'int | None' = None)",
  "doc": "Atributos de combate da unidade, combat.UnitStats: vida/mana máximas, armadura (incluindo melhorias de ataque/armadura e agilidade do herói), tipo de armadura, velocidade de movimento, visão de dia/à noite,\narmas (o que pode atacar, alcance, intervalo de ataque, faixa de dano, tipo de ataque, dano em área). u pode ser uma unidade (usa automaticamente a tecnologia do dono e o nível do herói) ou um código de quatro caracteres (player é o nosso por padrão).\nDepois combine com .dps_vs(outro) / .hits_to_kill(outro) / combat.time_to_kill(grupo, outro). ⚠ Não inclui itens, auras nem buffs."
 },
 {
  "name": "time_to_kill",
  "category": "observe",
  "status": "verified",
  "mechanism": "stats() + vida em tempo real",
  "latency": "Snapshot enviado",
  "signature": "time_to_kill(attackers, target) -> 'float | None'",
  "doc": "Quantos segundos de jogo este grupo leva para matar target atacando junto (usa a vida atual de target; considera vantagens de tipo, armadura e melhorias de ataque/armadura; não considera posicionamento, dano em área nem cura).\nUso profissional: ao focar fogo, ataque primeiro quem \"morre mais rápido\" (menor time_to_kill), não o mais próximo. Se não conseguem atingir = None."
 },
 {
  "name": "time_of_day",
  "category": "observe",
  "status": "verified",
  "mechanism": "Área de extensão do bloco de mundo: GetFloatGameState(GAME_STATE_TIME_OF_DAY)",
  "latency": "Snapshot enviado",
  "signature": "time_of_day() -> 'float | None'",
  "doc": "Hora do dia no jogo (horas, 0~24). A partida começa às 8 da manhã; um dia inteiro = 480 segundos de jogo (240 s de dia e 240 s de noite, escalados pela velocidade do ciclo dia/noite).\nRetorna None se não der para ler (runtime antigo / fora de partida)."
 },
 {
  "name": "is_night",
  "category": "observe",
  "status": "verified",
  "mechanism": "Área de extensão do bloco de mundo (dia das 6h às 18h)",
  "latency": "Snapshot enviado",
  "signature": "is_night() -> 'bool | None'",
  "doc": "Se agora é noite (18:00~6:00). Jogada profissional: à noite os creeps dormem (você ataca primeiro sem ser cercado) e a visão de todas as unidades diminui (boa hora para ataques-surpresa);\nas Sentinelas e as unidades dos Elfos Noturnos ficam invisíveis perto das árvores à noite. Retorna None se não der para ler."
 },
 {
  "name": "seconds_until",
  "category": "observe",
  "status": "verified",
  "mechanism": "Área de extensão do bloco de mundo + dia de 480 segundos (medido: 20 segundos de jogo por hora)",
  "latency": "Snapshot enviado",
  "signature": "seconds_until(hour: 'float') -> 'float | None'",
  "doc": "Quantos segundos de jogo faltam até a hora hour do jogo (por exemplo, seconds_until(18) = quanto falta para anoitecer, útil para planejar creeping noturno)."
 },
 {
  "name": "items_on_ground",
  "category": "observe",
  "status": "verified",
  "mechanism": "items[] do bloco de mundo (só os que estão no chão: handle do portador todo FF)",
  "latency": "Snapshot enviado",
  "signature": "items_on_ground() -> 'list'",
  "doc": "Itens no chão [Item(addr, handle_lo, handle_hi, type, x, y, life)]. Pegar/usar um item emite o evento item.removed."
 },
 {
  "name": "trees",
  "category": "observe",
  "status": "verified",
  "mechanism": "Bloco de árvores Local\\War3Trees_<pid> (atualizado a cada 2 segundos)",
  "latency": "Snapshot enviado",
  "signature": "trees(x: 'float | None' = None, y: 'float | None' = None, limit: 'int' = 60) -> 'list'",
  "doc": "Árvores vivas (as de DestructableData cujo targType inclui tree); se você passar (x,y), vêm ordenadas da mais perto para a mais longe, no máximo limit árvores.\nCada uma é Tree(addr, handle_lo, handle_hi, type, x, y, life) e pode ser passada direto para gather para cortar madeira."
 },
 {
  "name": "events",
  "category": "observe",
  "status": "verified",
  "mechanism": "Anel de eventos Local\\War3Events_<pid> (comparação entre publicações + eventos de dano capturados pelo runtime)",
  "latency": "Snapshot enviado",
  "signature": "events() -> 'list'",
  "doc": "O que aconteceu desde a última chamada: unit.appeared / unit.died / unit.removed / unit.damaged / order.changed /\nhero.levelup / owner.changed / item.appeared / item.removed / game.started (estes vêm da comparação entre publicações, precisão = período de publicação de 50 ms),\nalém dos eventos de nível de engine damage / killed (o runtime os registra na thread do jogo no momento em que acontecem, então há um para **cada golpe**):\n    damage: handle = quem apanhou, .source_addr = quem bateu (use snapshot().unit_by_addr para obter a unidade), .value = vida realmente perdida,\n            .raw_damage = dano antes da armadura, .attack_type (normal/pierce/siege/magic/chaos/hero/spell), .damage_type\n    killed: este golpe a matou, .source_addr = quem matou\ne ainda production.done, obtido pelo runtime acompanhando a tabela de produção (precisão = período de publicação): unidade = a construção, .done_code = código de quatro caracteres do que terminou,\n    .done_kind = 'training' (unidade/herói/revivência) / 'research' / 'construction' (construção pronta) / 'upgrade' (subir de tier/melhorar torre), .value = quantos segundos de jogo levou\nCompletados em 09-25:\n    spell.cast: unidade = quem lançou, .spell código de quatro caracteres da habilidade, b nível, value recarga em segundos, x,y ponto de lançamento (detectado quando a habilidade entra em recarga, precisão = período de publicação)\n    player.left: .player número do jogador que saiu / foi removido por derrota; game.ended: saída da partida\n    selection.changed: a seleção do jogador local mudou (use g.selection() para obter as unidades)\n    message: uma linha numa caixa de mensagens da tela (dicas do jogo, chat, sistema): .text texto completo, .frame número da caixa de mensagens,\n             .chat = {'channel', 'sender', 'text'} (quando é chat; o que o jogador digita no chat é lido daqui)\n    ui.click / ui.hover / hotkey / mouse.world: interface e entrada (g.ui), .key é a key do canvas / a tecla de atalho como foi registrada\nCada um é Event(seq, kind, clock, addr, handle, type, owner, a, b, x, y, value, extra).\nNo modo justo (fair=True) só vêm: eventos das suas próprias unidades, eventos de unidades visíveis agora (ou que estavam visíveis até 1 segundo atrás), dano contra nós / causado por nós\ne os eventos locais de interface / mensagens / partida."
 },
 {
  "name": "selection",
  "category": "observe",
  "status": "verified",
  "mechanism": "selAddrs na área de extensão do bloco de mundo W3P (o runtime inclui a seleção do jogador local em cada publicação)",
  "latency": "Snapshot enviado",
  "signature": "selection() -> 'list'",
  "doc": "Unidades que o jogador local tem selecionadas agora (a unidade principal vem primeiro; no máximo 12). Quando a seleção muda, é emitido o evento selection.changed."
 },
 {
  "name": "messages",
  "category": "observe",
  "status": "verified",
  "mechanism": "Memória compartilhada Local\\War3Msgs_<pid> (mensagens na tela capturadas pelo runtime)",
  "latency": "Snapshot enviado",
  "signature": "messages() -> 'list'",
  "doc": "Mensagens novas nas caixas de mensagens da tela desde a última chamada: [{'text', 'frame', 'repeat', 'seq', 'game_ms'}].\nDicas do jogo (“Você precisa de mais fazendas”, “Não é possível construir aí”), chat e mensagens do sistema estão todos aqui; frame indica qual caixa de mensagens.\nSão as mesmas mensagens dos eventos message do fluxo de eventos (cada um com seu próprio cursor)."
 },
 {
  "name": "ui",
  "category": "control",
  "status": "verified",
  "mechanism": "W3P 74 input_enable + memória compartilhada Local\\War3Input_<pid> (o runtime recebe a entrada da janela)",
  "latency": "Memória compartilhada",
  "signature": "ui()",
  "doc": "Interface e entrada (openwar3.ui.UI): botões e cartões de escolha clicáveis, teclas de atalho, clique no chão para escolher uma posição, para onde o mouse aponta.\nO clique dado sobre um botão não chega ao jogo; só entrada local + desenho local, seguro também em partidas multijogador."
 },
 {
  "name": "tech",
  "category": "observe",
  "status": "verified",
  "mechanism": "Consulta W3P q_tech (contagem de tecnologia do jogador no engine)",
  "latency": "Via rápida",
  "signature": "tech(code: 'str', player: 'int | None' = None) -> 'int | None'",
  "doc": "Nível de pesquisa / número de construções concluídas (a cadeia de melhorias conta: um Castelo também conta como htow). player é o nosso por padrão; dá para consultar qualquer jogador."
 },
 {
  "name": "can_do",
  "category": "observe",
  "status": "verified",
  "mechanism": "Consulta W3P q_feasible (verificação de viabilidade do engine)",
  "latency": "Via rápida",
  "signature": "can_do(u, code: 'str') -> 'int | None'",
  "doc": "O veredito de viabilidade do engine: 0/220 pode dar a ordem; 3 comida 8 falta ouro 9 falta madeira 32 fila cheia 183 falta pré-requisito 185 altar revivendo 221 item inexistente/em construção.\n⚠ Para trabalhador construindo, sempre retorna 221 — não serve para avaliar o local (use build_near)."
 },
 {
  "name": "can_do_many",
  "category": "observe",
  "status": "verified",
  "mechanism": "Consulta W3P q_feasible × N, enviada em um lote",
  "latency": "Via rápida × 1",
  "signature": "can_do_many(pairs) -> 'list'",
  "doc": "Faz muitos can_do de uma vez: pairs = [(unidade, código de quatro caracteres), ...]; retorna a lista de códigos de veredito na mesma ordem (os que não puderam ser consultados são None).\nAo planejar o que construir/treinar em um tick, pergunte tudo de uma vez primeiro: é N vezes mais rápido que um can_do por vez (cérebro de referência, 09-23: planejamento de construção 76 -> 25 ms)."
 },
 {
  "name": "tech_many",
  "category": "observe",
  "status": "verified",
  "mechanism": "Consulta W3P q_tech × N, enviada em um lote",
  "latency": "Via rápida × 1",
  "signature": "tech_many(codes, player: 'int | None' = None) -> 'dict'",
  "doc": "Consulta de uma vez várias contagens de tecnologia/construção: {código de quatro caracteres: quantidade ou None}."
 },
 {
  "name": "visible",
  "category": "observe",
  "status": "verified",
  "mechanism": "Consulta W3P q_visible (visível / névoa de guerra / máscara preta)",
  "latency": "Via rápida",
  "signature": "visible(x: 'float', y: 'float') -> 'bool | None'",
  "doc": "Se nós vemos este ponto agora (fora da névoa de guerra/máscara preta). Um bot em modo justo deve usar só inimigos visíveis."
 },
 {
  "name": "gold_left",
  "category": "observe",
  "status": "inferred",
  "mechanism": "Consulta W3P q_mine_gold (ouro restante na mina, segundo o engine)",
  "latency": "Via rápida",
  "signature": "gold_left(mine) -> 'int | None'",
  "doc": "Quanto ouro ainda resta na mina."
 },
 {
  "name": "enemy_ai_plan",
  "category": "observe",
  "status": "verified",
  "mechanism": "Consulta W3P q_captain (o capitão do computador que a unidade inimiga está seguindo)",
  "latency": "Via rápida",
  "signature": "enemy_ai_plan(enemy_unit) -> 'dict | None'",
  "doc": "O capitão da IA do computador: para onde ele está levando as tropas (você sabe onde ele vai atacar sua base antes de ele sair). Só funciona contra adversários controlados pelo computador; retorna None se a unidade não estiver seguindo um capitão."
 },
 {
  "name": "batch",
  "category": "command",
  "status": "verified",
  "mechanism": "Os comandos do bloco são acumulados em um lote e enviados de uma vez quando o bloco termina (executados no mesmo frame, esperando a thread do jogo uma única vez)",
  "latency": "Via rápida × 1",
  "signature": "batch() -> 'Batch'",
  "doc": "Junta os comandos de um tick em um lote:\n\n    with g.batch() as b:\n        g.attack(archers, target)          # retorna Pending, que só vira recibo quando o bloco termina\n        g.move(wounded, *home)\n        g.cast(hero, \"thunderclap\")\n    print(b.sent, b.wait_ms, [r.reason for r in b.receipts])\n\nCada comando enviado sozinho espera a thread do jogo processar uma vez (cerca de 10 ms); um lote espera uma única vez — com isso o cérebro de referência (09-23) caiu de 48 -> 26 ms por rodada.\n* A arbitragem continua comando a comando (unidades reservadas por outro recebem na hora um recibo held e não entram no lote);\n* Comandos dentro do bloco retornam Pending: ler .ok antes do fim do bloco lança erro (o recibo ainda não existe); depois do fim, use como um Receipt;\n* Exceção dentro do bloco = o lote inteiro é descartado (status 97 cancelled), e as unidades reservadas são liberadas;\n* Consultas (can_do / tech / visible …), build_near e buy não entram no lote e continuam sendo feitas na hora — o resultado delas é usado na hora;\n  para perguntar muitas coisas de uma vez, use can_do_many / tech_many;\n* with g.batch() aninhados se juntam ao lote mais externo; acima de 16 comandos, o runtime divide automaticamente em partes (uma espera por parte)."
 },
 {
  "name": "order_of",
  "category": "observe",
  "status": "verified",
  "mechanism": "Ordem do snapshot + comandos deste processo que acabaram de ser aceitos (recibos)",
  "latency": "Snapshot enviado",
  "signature": "order_of(u) -> 'int | None'",
  "doc": "A ordem atual da unidade, **incluindo a que você acabou de dar neste tick** (enquanto o snapshot não alcança, usa a nova ordem do recibo).\n⚠ Partida real de 09-23: hello_bot tinha acabado de mandar um camponês construir uma Fazenda, e no mesmo tick rush_bot viu no snapshot que ele estava \"ocioso\" e o mandou construir um Quartel; a Fazenda foi abandonada no meio várias vezes.\n  Para escolher unidades \"ociosas/que não estão construindo\", use isto em vez de u.order."
 },
 {
  "name": "move",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P point: move (bits de extra = modo de fila)",
  "latency": "Via rápida",
  "signature": "move(units, x: 'float', y: 'float', queue: 'str | None' = None)",
  "doc": "Vai até (x,y) sem atacar no caminho (use para recuar). Aceita uma unidade ou uma lista (a ordem sai para todas no mesmo frame).\nqueue='after': vai depois de terminar o que está fazendo (inserido após a ordem atual). values[0] do recibo = quantas ordens a unidade tem na fila depois do comando (incluindo a atual)."
 },
 {
  "name": "attack_move",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P point: attack em um ponto",
  "latency": "Via rápida",
  "signature": "attack_move(units, x: 'float', y: 'float', queue: 'str | None' = None)",
  "doc": "Atacar-mover (A no chão): ataca os inimigos que encontrar no caminho. queue igual a move."
 },
 {
  "name": "attack",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P target: comando de alvo (botão direito, smart)",
  "latency": "Via rápida",
  "signature": "attack(units, target, force: 'bool' = False, queue: 'str | None' = None)",
  "doc": "Ataca target. Por padrão usa o botão direito (em um inimigo = atacar este aqui; medido em 09-23: o alvo da ordem e o alvo da tarefa são ele).\n⚠ O alvo precisa estar no campo de visão; os que você não vê são rejeitados (código de motivo 1001).\nforce=True usa a ordem de ataque 0x0F (necessária para atacar aliados/bichos neutros) — medido: ela só troca para a ordem de ataque sem memorizar o alvo,\ne a unidade vai atacar outros inimigos por perto; não use para atacar um alvo específico."
 },
 {
  "name": "stop",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P immediate: stop",
  "latency": "Via rápida",
  "signature": "stop(units)",
  "doc": "Para tudo o que está fazendo (ID de ordem 0x000D0004) e limpa também as ordens na fila."
 },
 {
  "name": "hold",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P immediate: holdposition",
  "latency": "Via rápida",
  "signature": "hold(units, queue: 'str | None' = None)",
  "doc": "Manter posição (não persegue; só ataca o que estiver no alcance)."
 },
 {
  "name": "patrol",
  "category": "command",
  "status": "inferred",
  "mechanism": "W3P point: patrol",
  "latency": "Via rápida",
  "signature": "patrol(units, x: 'float', y: 'float', queue: 'str | None' = None)",
  "doc": "Patrulha entre a posição atual e (x,y)."
 },
 {
  "name": "attack_ground",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P point: attackground (unidades de cerco / morteiros / demolidores)",
  "latency": "Via rápida",
  "signature": "attack_ground(units, x: 'float', y: 'float', queue: 'str | None' = None)",
  "doc": "Atacar o chão: a artilharia dispara numa área (contra unidades invisíveis, inimigos atrás das árvores, para bloquear uma passagem). Só unidades que podem atacar o chão aceitam."
 },
 {
  "name": "cancel",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P immediate: cancel",
  "latency": "Via rápida",
  "signature": "cancel(building)",
  "doc": "Cancela: o último espaço da fila de treino/pesquisa (devolve o dinheiro), a construção em obra (devolve 75%), a sede em melhoria."
 },
 {
  "name": "path",
  "category": "command",
  "status": "verified",
  "mechanism": "Um lote: o primeiro trecho é executado na hora, o resto é inserido em ordem inversa com queue='after' (o engine só sabe inserir depois da ordem atual)",
  "latency": "Via rápida × 1",
  "signature": "path(units, points, attack: 'bool' = False)",
  "doc": "Passa por uma sequência de pontos em ordem (Shift com vários pontos: waypoints, contornar torres, rotas de reconhecimento). attack=True faz cada trecho ser um atacar-mover.\nUm único envio; um recibo por ponto (na ordem de points)."
 },
 {
  "name": "gather",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P target: harvest (mina de ouro ou árvore)",
  "latency": "Via rápida",
  "signature": "gather(workers, target, queue: 'str | None' = None)",
  "doc": "Minerar ouro/cortar madeira (target é uma mina de ouro ou uma árvore de trees()). ⚠ Só designe trabalhadores ociosos (idle_workers): dar nova ordem a quem tem tarefa interrompe o ciclo de coleta.\nUso profissional: voltar a minerar depois de construir = build(...) seguido de gather(worker, mine, queue='after')."
 },
 {
  "name": "repair",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P target: repair",
  "latency": "Via rápida",
  "signature": "repair(workers, building, queue: 'str | None' = None)",
  "doc": "Reparar / ajudar a construir (as obras dos Humanos e dos Orcs param se ninguém estiver construindo)."
 },
 {
  "name": "build",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P build: ordem de construção; confirma lendo a ordem do trabalhador no mesmo frame",
  "latency": "Via rápida",
  "signature": "build(worker, code: 'str', x: 'float', y: 'float', queue: 'str | None' = None)",
  "doc": "Manda o trabalhador construir code em (x,y) (coordenadas alinhadas a 32). Recibo aceito = a ordem do trabalhador já é esta construção (ou a ordem de começar a obra);\ncom queue='after' = entrou na fila de ordens do trabalhador (values[0] do recibo = tamanho da fila).\n⚠ Aceito ≠ construído: o engine também aceita na hora um ponto dentro de uma floresta, e o trabalhador só falha quando chega lá (medido em 09-23); se o dinheiro for gasto em outra coisa, a fundação também não sai.\nSe não souber onde cabe, use build_near (ele acompanha o resultado e bloqueia os pontos que falharam). Para construir várias em sequência, use build_queue."
 },
 {
  "name": "build_queue",
  "category": "command",
  "status": "verified",
  "mechanism": "Um lote: a primeira na hora, o resto em ordem inversa com queue='after'",
  "latency": "Via rápida × 1",
  "signature": "build_queue(worker, plan)",
  "doc": "Um trabalhador constrói várias em sequência (Shift): plan = [(código de quatro caracteres, x, y), ...]. Um único envio; recibos na ordem de plan.\n⚠ O dinheiro só é descontado quando a obra começa (não ao enfileirar) — se você enfileirar 3 mas só tiver dinheiro para 1, as outras duas falham quando o trabalhador chegar."
 },
 {
  "name": "build_near",
  "category": "command",
  "status": "verified",
  "mechanism": "build ponto a ponto + acompanhamento (fundação apareceu = sucesso; trabalhador abandonou a ordem sem fundação = ponto bloqueado)",
  "latency": "Via rápida × pontos testados",
  "signature": "build_near(worker, code: 'str', x: 'float', y: 'float', min_r: 'float' = 450, max_r: 'float' = 1500, max_tries: 'int' = 24)",
  "doc": "Procura em volta de (x,y), do mais perto para o mais longe, um ponto onde caiba code e constrói. **Não bloqueia**; pode chamar a cada tick:\n  * já existe uma tentativa em andamento para este tipo de construção (o trabalhador está a caminho) -> retorna esse ponto, sem repetir a ordem;\n  * a última deu certo (a fundação apareceu) -> desta vez procura um ponto novo, se precisar;\n  * a última falhou (o trabalhador chegou e viu que não cabia, o engine cancelou a ordem, não há fundação) -> esse ponto fica bloqueado por 45 segundos e passa para o próximo;\n  * falta dinheiro -> retorna None direto (sem tentar, sem bloquear); se esgotar as tentativas, retorna None.\n⚠ Por que acompanhar: na partida real de 09-23, o engine **aceitou na hora** um ponto dentro de uma floresta, e o trabalhador só falhou ao chegar lá (o recibo do mesmo frame não tem como saber);\n  além disso, a verificação de local do engine sempre retorna 221 quando é um trabalhador construindo, então não dá para \"consultar\" antes de construir. Só pontos obviamente ocupados (bem no meio da sede) são rejeitados na hora."
 },
 {
  "name": "can_afford",
  "category": "observe",
  "status": "verified",
  "mechanism": "Nossos recursos no snapshot enviado + preços de units.json",
  "latency": "Snapshot enviado",
  "signature": "can_afford(code: 'str') -> 'bool'",
  "doc": "Se o ouro/madeira atuais bastam para comprar code (unidades, construções; pelos preços de units.json). O que não estiver na tabela de preços conta como acessível.\n⚠ Os códigos de quatro caracteres de subir de tier têm preço acumulado na tabela, então aqui o resultado é conservador; no fim, vale o recibo do engine."
 },
 {
  "name": "train",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P immediate: código de quatro caracteres; quando rejeitado, traz o código de motivo da viabilidade",
  "latency": "Via rápida",
  "signature": "train(building, code: 'str')",
  "doc": "Treina unidade / pesquisa tecnologia / melhora a sede (subir de tier = dar à própria sede o código de quatro caracteres da sede alvo, como 'hkee').\nQuando rejeitado, o reason do recibo diz por quê (falta comida, falta ouro, falta madeira, fila cheia, falta pré-requisito…)."
 },
 {
  "name": "learn",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P learn: só conta como aprendida quando os pontos de habilidade diminuem",
  "latency": "Via rápida",
  "signature": "learn(hero, ability: 'str')",
  "doc": "O herói aprende uma habilidade (código de quatro caracteres, como 'AHbz' Nevasca)."
 },
 {
  "name": "cast",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P target / point / immediate (escolhido pelos parâmetros)",
  "latency": "Via rápida",
  "signature": "cast(u, spell, target=None, x: 'float | None' = None, y: 'float | None' = None)",
  "doc": "Lança uma magia. spell é uma string de ordem ('thunderbolt' Storm Bolt, 'blizzard', 'holybolt' Luz Sagrada…, veja data/order-ids.txt) ou um ID de ordem.\nCom target = em uma unidade; com x,y = no chão; sem nenhum dos dois = sem alvo (Trovoada, Escudo Divino, invocar Elemental da Água).\nRecibo aceito só significa que o engine aceitou; para saber se a magia saiu, veja se cooldown() entrou em recarga ou se apareceu algo em buffs()."
 },
 {
  "name": "rally",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P rally",
  "latency": "Via rápida",
  "signature": "rally(building, x: 'float | None' = None, y: 'float | None' = None, target=None)",
  "doc": "Define o ponto de encontro (em um ponto, ou em uma unidade/mina de ouro)."
 },
 {
  "name": "revive",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P revive: lista de heróis mortos -> o altar lança a revivência no herói morto",
  "latency": "Via rápida",
  "signature": "revive(altar, hero=None)",
  "doc": "Revive no altar um herói morto (sem hero, revive o primeiro da lista).\nMotivos comuns de rejeição (aparecem no reason do recibo): falta comida (heróis também ocupam comida), falta dinheiro, morreu há pouco (só dá para reviver cerca de 3 segundos de jogo depois da morte),\nrevivência já em andamento (ao aceitar, o engine limpa aquele espaço na hora)."
 },
 {
  "name": "pick_up",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P target: botão direito no item",
  "latency": "Via rápida",
  "signature": "pick_up(hero, item)",
  "doc": "O herói vai pegar um item do chão (item vem de items_on_ground). Depois de pegar, o item aparece no inventário e o chão emite o evento item.removed."
 },
 {
  "name": "use_item",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P use_item (pelo número do espaço)",
  "latency": "Via rápida",
  "signature": "use_item(hero, slot: 'int', target=None, x: 'float | None' = None, y: 'float | None' = None)",
  "doc": "Usa o item do espaço slot (0~5) do inventário; pode levar uma unidade-alvo ou um ponto-alvo.\n⚠ Ao usar um item em um ponto (por exemplo, a Torre de Marfim), o engine retorna 0 mesmo quando dá certo, então o recibo sempre conta como aceito — confira se aquele espaço do inventário esvaziou."
 },
 {
  "name": "drop_item",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P item_drop (copiado de JASS UnitDropItemPoint: dropitem 0xD0021 em um ponto + alvo imediato do item)",
  "latency": "Via rápida",
  "signature": "drop_item(hero, slot: 'int', x: 'float', y: 'float')",
  "doc": "Larga o item do espaço slot do inventário em (x,y) (o herói vai até lá e o deixa)."
 },
 {
  "name": "give_item",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P item_drop (copiado de JASS UnitDropItemTarget: dropitem em uma unidade)",
  "latency": "Via rápida",
  "signature": "give_item(hero, slot: 'int', to)",
  "doc": "Dá o item do espaço slot do inventário a to (outro herói / unidade; ele vai até lá e entrega). Dar a uma loja = vender (veja sell_item)."
 },
 {
  "name": "sell_item",
  "category": "command",
  "status": "verified",
  "mechanism": "Igual a give_item, com a loja como alvo (medido: o Staff of Sanctuary foi vendido por 125 de ouro)",
  "latency": "Via rápida",
  "signature": "sell_item(hero, slot: 'int', shop)",
  "doc": "Vende o item do espaço slot do inventário para a loja (o herói precisa ir até perto da loja; só aceita itens vendáveis, pagando metade do preço)."
 },
 {
  "name": "move_item",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P target: ordem 0xD0022+número do espaço, alvo = item (copiado de JASS UnitDropItemSlot)",
  "latency": "Via rápida",
  "signature": "move_item(hero, slot: 'int', to_slot: 'int')",
  "doc": "Troca de espaço dentro do inventário (move o espaço slot para o espaço to_slot; se os dois estiverem ocupados, eles trocam). Útil para organizar as teclas de atalho."
 },
 {
  "name": "buy",
  "category": "command",
  "status": "inferred",
  "mechanism": "W3P buy: a loja vende ao herói que está ao lado",
  "latency": "Via rápida",
  "signature": "buy(shop, item_code: 'str')",
  "doc": "Compra um item na loja (para o herói parado ao lado da loja). Se faltar pré-requisito de tecnologia, o engine retorna 0 e não desconta o dinheiro."
 },
 {
  "name": "call_to_arms",
  "category": "command",
  "status": "verified",
  "mechanism": "W3P immediate: townbellon/off",
  "latency": "Via rápida",
  "signature": "call_to_arms(hall, on: 'bool' = True)",
  "doc": "Chamado às Armas dos Humanos: os camponeses viram Milícia (a sede de tier 1, Town Hall, não tem essa habilidade; só funciona em Keep/Castle)."
 },
 {
  "name": "set_speed",
  "category": "control",
  "status": "verified",
  "mechanism": "Ação 47 (25~800%)",
  "latency": "Canal de controle",
  "signature": "set_speed(percent: 'int') -> 'bool'",
  "doc": "Velocidade do jogo (100 = velocidade normal)."
 },
 {
  "name": "pause",
  "category": "control",
  "status": "verified",
  "mechanism": "W3P pause",
  "latency": "Via rápida",
  "signature": "pause(on: 'bool' = True)",
  "doc": "Pausa / retoma o jogo. Com o jogo pausado, o relógio do engine para, mas a via rápida continua aceitando ordens (o despacho de eventos continua rodando)."
 },
 {
  "name": "set_publish_period",
  "category": "control",
  "status": "verified",
  "mechanism": "requestedPeriodMs do bloco de mundo",
  "latency": "Snapshot enviado",
  "signature": "set_publish_period(ms: 'int') -> 'None'",
  "doc": "Período de publicação do estado do mundo (16~1000 ms, padrão 50). Uma coleta leva cerca de 0.5 ms, então até 33 ms funciona; há um único valor compartilhado pela máquina inteira, e vale a última escrita."
 },
 {
  "name": "say",
  "category": "control",
  "status": "verified",
  "mechanism": "Ação 56",
  "latency": "Canal de controle",
  "signature": "say(u, text: 'str', seconds: 'float' = 4.0) -> 'bool'",
  "doc": "Mostra um balão de fala sobre a unidade (para transmissões/depuração; não afeta o jogo). Retorna False se o balão não apareceu; o motivo fica em g.last_say_error."
 },
 {
  "name": "message",
  "category": "control",
  "status": "inferred",
  "mechanism": "Ação 45",
  "latency": "Canal de controle",
  "signature": "message(text: 'str') -> 'bool'",
  "doc": "Escreve uma linha na área de mensagens no canto inferior esquerdo do jogo (só visível na máquina local). Só funciona depois que o próprio jogo tiver mostrado alguma mensagem (a DLL captura a caixa de mensagens nesse momento)."
 },
 {
  "name": "end_game",
  "category": "control",
  "status": "verified",
  "mechanism": "Ação 22",
  "latency": "Canal de controle",
  "signature": "end_game() -> 'bool'",
  "doc": "Encerra este processo do jogo (farm.py --keep abre a próxima partida automaticamente conforme next_game.json)."
 },
 {
  "name": "canvas",
  "category": "control",
  "status": "verified",
  "mechanism": "W3P 73 canvas_enable + memória compartilhada Local\\War3Canvas_<pid> (o runtime desenha a cada frame logo antes de o jogo desenhar o cursor do mouse; o cursor fica por cima)",
  "latency": "Memória compartilhada",
  "signature": "canvas()",
  "doc": "Canvas: desenha na tela do jogo caixas de texto, painéis, barras de progresso, imagens, círculos e rotas no chão (openwar3.canvas.Canvas).\nQuem desenha é o próprio runtime, sem criar handles de jogo nem alterar o estado do jogo — seguro também em partidas multijogador; estilo livre (texto em chinês, cantos arredondados, transparência)."
 },
 {
  "name": "press_to_continue",
  "category": "control",
  "status": "verified",
  "mechanism": "PostMessage WM_KEYDOWN/UP de espaço para a janela do jogo (sem roubar o foco)",
  "latency": "Mensagem de janela",
  "signature": "press_to_continue() -> 'bool'",
  "doc": "Aperta espaço uma vez na tela de carregamento \"Pressione qualquer tecla para continuar\". Muitos mapas RPG / de história exigem uma tecla depois de carregar para começar (medido em 09-24 no WarChasers:\nsem apertar, o jogo fica parado na tela de carregamento, com relógio de jogo 0 e a via rápida sem esvaziar). O openwar3.run aperta sozinho enquanto espera a partida começar; em geral não é preciso chamar à mão."
 },
 {
  "name": "map_data",
  "category": "observe",
  "status": "verified",
  "mechanism": "Arquivo do mapa (o caminho --map do lançador): w3u/w3t/w3a + wts; em mapas protegidos, lê os TXT de dentro do mapa",
  "latency": "Leitura de arquivo (~0.1 s na 1ª vez)",
  "signature": "map_data()",
  "doc": "Os dados do mapa em jogo (openwar3.mapdata.MapData): name_of('HC07') para nomes de unidades/itens/habilidades personalizados, hero_names, tooltip.\nA maioria das unidades de mapas RPG é criada pelo próprio mapa e não está na tabela de nomes embutida; se o jogo não foi aberto pelo lançador (o arquivo do mapa não é encontrado), retorna None."
 },
 {
  "name": "jass",
  "category": "sandbox",
  "status": "verified",
  "mechanism": "W3P 70 jass (o runtime procura a native pelo nome na tabela de natives, 1291 no total)",
  "latency": "Via rápida",
  "signature": "jass()",
  "doc": "Chama qualquer native JASS pelo nome: g.jass.CreateUnit(g.jass.Player(1), \"Hpal\", x, y, 270.0).\nParâmetros I/R/B/S/H são convertidos automaticamente (objetos de unidade/item podem ser passados direto); em partidas multijogador, só as funções de leitura podem ser chamadas. Detalhes em openwar3/jass.py e docs/COMPANION_ZH.md."
 },
 {
  "name": "player_slots",
  "category": "sandbox",
  "status": "verified",
  "mechanism": "JASS GetPlayerController / GetPlayerSlotState / IsPlayerAlly",
  "latency": "Via rápida",
  "signature": "player_slots() -> 'list[dict]'",
  "doc": "Os 16 slots de jogador: controller (user = humano / computer / neutral…), state (empty / playing / left), human, me, ally (se é nosso aliado).\nServe para achar um slot vazio para o companheiro em mapas RPG e para saber se a partida é solo."
 },
 {
  "name": "spawn",
  "category": "sandbox",
  "status": "verified",
  "mechanism": "JASS CreateUnit + W3P 72 handle -> unidade",
  "latency": "Via rápida",
  "signature": "spawn(code: 'str', x: 'float', y: 'float', player: 'int | None' = None, facing: 'float' = 270.0)",
  "doc": "Cria uma unidade em (x,y) (player é o jogador local por padrão) e retorna a unidade do snapshot (espera a próxima publicação do mundo, ~50 ms); se não conseguir criar, retorna None.\nA unidade retornada tem um atributo extra, jass_handle. ⚠ Só funciona em partidas solo (em partidas multijogador, causa dessincronização)."
 },
 {
  "name": "set_alliance",
  "category": "sandbox",
  "status": "verified",
  "mechanism": "JASS SetPlayerAlliance",
  "latency": "Via rápida",
  "signature": "set_alliance(a: 'int', b: 'int', allied: 'bool' = True, vision: 'bool' = True, control: 'bool' = False, xp: 'bool' = False, both: 'bool' = True) -> 'None'",
  "doc": "Define a aliança do jogador a com b: allied = não se atacam + pedem ajuda um ao outro; vision = visão compartilhada; control = controle de unidades compartilhado (b pode comandar as unidades de a);\nxp = experiência compartilhada. both=True define os dois sentidos de uma vez (control só de a -> b)."
 },
 {
  "name": "set_player_name",
  "category": "sandbox",
  "status": "verified",
  "mechanism": "JASS SetPlayerName",
  "latency": "Via rápida",
  "signature": "set_player_name(player: 'int', name: 'str') -> 'None'",
  "doc": "Muda o nome do jogador (o que aparece no placar, no chat e no painel de aliados). Serve para dar nome ao companheiro."
 },
 {
  "name": "show_text",
  "category": "sandbox",
  "status": "verified",
  "mechanism": "JASS DisplayTimedTextToPlayer",
  "latency": "Via rápida",
  "signature": "show_text(text: 'str', seconds: 'float' = 6.0, player: 'int | None' = None) -> 'None'",
  "doc": "Mostra uma linha de texto no canto inferior esquerdo da tela (o tipo de texto que os gatilhos do mapa usam), por padrão só para o jogador local. Aceita códigos de cor |cffRRGGBB."
 }
]