Docs Extensões de jogo

Companheiro de RPG

Dê ao jogador um companheiro de IA em mapas RPG e personalizados: ele segue você, ajuda a matar monstros, cura você quando sua vida está baixa e conversa com você. Quatro modos; herde uma classe, mude algumas propriedades e você tem o seu próprio companheiro.

Não é só para partidas de confronto. Em mapas RPG e personalizados, você pode ter um companheiro de IA: ele anda com você, ajuda a matar monstros, cura você quando sua vida está baixa e, quando não há nada acontecendo, puxa conversa — as falas podem vir de um LLM local.

Como usar é decisão sua. Isso se divide em três camadas de API, de baixo para cima, e qualquer uma delas pode ser usada diretamente:

CamadaO que éPara quem
Canal JASS g.jassAs 1291 funções JASS que autores de mapas podem usar, chamadas direto pelo nome (criar unidades, definir aliados, dar itens, mudar nomes, exibir texto, reviver heróis…)Quem quer criar a própria jogabilidade
APIs de conveniênciag.spawn, g.set_alliance, g.player_slots, g.show_text, g.map_data: as tarefas mais comuns, já empacotadasQuem quer escrever os próprios scripts auxiliares
Framework de companheiroopenwar3.companion.Companion + openwar3.talk.Talk: herde, mude algumas propriedades e você tem um parceiro que segue, ajuda na luta, cura e conversaQuem quer um companheiro

Só para partidas offline, em rede local ou criadas por você. Criar unidades e definir aliados são mudanças no mundo que esta máquina faz por conta própria: numa partida solo (contra o computador), tudo bem; numa partida multijogador, os outros jogadores perdem a sincronia. Por isso, em partidas multijogador o canal JASS só libera funções de leitura, e o companheiro recua automaticamente para “só falar”.

O jeito mais rápido: ativar com um clique no Farsight

  1. Escolha o mapa: no Farsight, página “Instâncias e partidas” → “Próxima partida” → Mapa, escolha um mapa RPG (tudo o que estiver em Scenario e Download, dentro de Maps na pasta do jogo, aparece na lista, por exemplo (4)WarChasers).
  2. Escolha o esquema: no menu “Esquema de IA” do card da instância, escolha Exemplo de companheiro (buddy) → “Selecionar”.
  3. Inicie o teste: quando o jogo abrir, jogue você mesmo na janela do jogo. O companheiro — um paladino chamado “Luz” — aparece ao seu lado.

Também dá para usar a linha de comando:

python tools/play.py --bot brains/examples/buddy.py --inst 20 --rpg --map "<pasta do jogo>\Maps\Scenario\(4)WarChasers.w3m"

--rpg (no manifesto do esquema, "judge": false) significa não decidir vitória e derrota pelas regras de confronto: em RPG, heróis mortos podem reviver, e não existe “perdeu todas as construções, perdeu a partida”. Muitos mapas RPG param em “Pressione qualquer tecla para continuar” depois de carregar; quando o SDK percebe que “está numa partida, mas o relógio do jogo continua em 0”, ele mesmo aperta espaço (g.press_to_continue(), que só envia uma mensagem de tecla para a janela do jogo, sem roubar o foco).

Escreva o seu próprio companheiro

from openwar3.companion import Companion
from openwar3.talk import Talk

class MyBuddy(Companion):
    mode = "ally"                        # modo, veja a tabela abaixo
    unit = "Hpal"                        # o que criar: qualquer código de quatro caracteres, inclusive os definidos pelo mapa
    nickname = "Luz"
    heal = ("holybolt", "AHhb", 0.55)    # (ordem de feitiço, habilidade a aprender, cura quando a vida do mestre ficar abaixo disto); None = não cura
    follow_distance = 350
    talk = Talk(persona="Um paladino alegre e animado, que adora torcer pelo seu mestre")

Quatro modos

modeQuem é o companheiroDescrição
ally (padrão)Ocupa um slot de jogador vazio como seu aliadoTem cor e nome próprios (o placar e o painel de aliados mostram o nickname); você não consegue selecioná-lo, ele age sozinho. O framework configura aliança + visão compartilhada automaticamente
ownCriado em seu nomeVocê pode comandá-lo manualmente a qualquer momento; quando você não está cuidando dele, a IA o controla por você
adoptAssume uma unidade que já existe no mapaSobrescreva adopt(g) para retornar essa unidade (o mascote ou o seguidor que o mapa dá a você)
voiceNão cria unidade, só falaFaz companhia e dá avisos; não altera o mundo, funciona até em partidas multijogador

Sem slot vazio, ally recua automaticamente para own; em partidas multijogador, ou se não for possível criar a unidade, recua para voice.

No modo ally, o companheiro se liga à “melhor unidade daquele slot no momento” (heróis primeiro), e não a uma unidade fixa. Nos testes, um mapa tratou o companheiro como um jogador de verdade: removeu o paladino e entregou um herói do mapa — o companheiro assumiu esse herói na hora e aprendeu as habilidades que o mapa definiu para ele. Quando o herói morre, a prioridade é revivê-lo ali mesmo; se o próprio mapa o reviver, ele continua usando esse herói.

O que ele faz a cada tick

Verifica em ordem e faz a primeira coisa que se aplicar:

OrdemComportamentoCondiçãoAjuste
1RecuarA própria vida abaixo de 25% e inimigos por perto: recua para trás do mestreretreat_at
2CurarVida do mestre abaixo do valor definido, habilidade fora de recarga, distância de até 900heal (None desliga)
3Ajudar na lutaInimigos perto do mestre: quem está atacando o mestre > quem o mestre está atacando > o mais próximoassist_radius, ou sobrescreva pick_target
4SeguirSe estiver longe demais do mestre, vai atrás; se estiver muito longe, volta correndo sem ficar lutandofollow_distance, leash
5ConversarSem inimigos por perto, solta uma frase a cada 1 ~ 2.5 minutosTabela de falas

“Inimigo” segue as relações de aliança do próprio jogo (atualizadas a cada 20 segundos). Mapas RPG costumam ter vários aliados, então não dá para simplesmente tratar “todos os jogadores menos eu” como inimigos.

Hooks que podem ser sobrescritos: find_master (quem é o mestre; por padrão, o herói de nível mais alto do jogador local), adopt, pick_target, on_poke (o mestre clicou com o botão direito no companheiro), além de on_start / on_tick / on_event / on_end do Bot. Curas, ajudas na luta, abates, seguimentos, recuos, falas e revividas são contados em self.stats e impressos no fim.

Como chamá-lo

  • Comandos de chat: digite no chat -follow para ele seguir você, -stay para ficar de guarda onde está, -heal para curar na hora, -hi para cumprimentar. Para mudar a lista de comandos, altere commands; para mudar as reações, sobrescreva on_command.
  • Clique com o botão direito no companheiro: dispara on_poke. No exemplo, a reação é: se o mestre não estiver com a vida cheia, ele cura um pouco; senão, diz alguma coisa.
  • Diálogo com retrato: o cumprimento, a queda do mestre, a subida de nível do mestre e a volta do companheiro são ditos pelo diálogo com retrato do próprio jogo (o retrato na parte de baixo passa a ser o do companheiro e aparece uma legenda na tela); as outras falas saem em balões sobre a cabeça.
  • Painel de status: um painel no lado esquerdo da tela mostra a barra de vida do companheiro, o que ele está fazendo, o humor (feliz / empolgado / tenso / com medo / triste), os abates e o número de curas. Ele é desenhado com o canvas e é seguro em partidas multijogador.

Falas e LLM local

Talk escolhe falas conforme o evento e as mostra num balão sobre a cabeça; no modo voice, ou quando não dá para mostrar o balão, a fala aparece no canto inferior esquerdo da tela. Cada frase também vai para o log do esquema, para você conferir depois o que ele disse.

EventoQuandoEventoQuando
helloAcabou de chegarmaster_lowMestre com pouca vida
pokeO mestre clicou nele com o botão direitomaster_levelupMestre subiu de nível
fightComeçou a lutamaster_died / master_backMestre caiu / reviveu
killMatou um monstro (diz o nome dele)buddy_low / buddy_died / buddy_backO próprio companheiro com pouca vida / caiu / voltou
healedCurou o mestreidle / itemConversa à toa / pegou um item

As falas podem usar os placeholders {master}, {me}, {map}, {enemy}, {level} e {item}; para mudar as falas, altere talk.lines; a recarga fica em talk.cooldown.

Conectar um LLM local: Talk(llm=LocalLLM(url, model)); qualquer API compatível com OpenAI serve (LM Studio, Ollama…). O modelo responde numa thread em segundo plano, e a fala só sai quando a resposta chega; se ele não estiver ligado, estourar o tempo ou der erro, o companheiro usa as falas fixas, sem travar o jogo. As requisições vão só para o endereço local que você informar, e o conteúdo é o que acontece no jogo (como o mestre se chama, que monstros ele matou).

Nomes de unidades em mapas personalizados

A maioria das unidades, itens e heróis de mapas RPG é criada pelo próprio mapa (códigos de quatro caracteres como HC07 e I00A) e não está na tabela de nomes embutida. g.map_data lê direto o arquivo do mapa da partida atual:

md = g.map_data
md.name_of("HC07")        # 'Optimus Primo' — nomes alterados pelo mapa têm prioridade
md.hero_names("HC07")     # lista de títulos
md.hero_skills("OC10")    # habilidades que o mapa definiu para este herói
md.tooltip("I00A")        # texto de descrição

Mapas protegidos ou otimizados (muitos RPGs populares) não trazem os arquivos padrão de dados de objetos; nesse caso, os nomes são lidos dos dados de texto do mapa. Nos testes, todos os 38 mapas RPG / personalizados desta máquina foram lidos com sucesso, e 37 deles forneceram nomes de unidades.

Compartilhe como esquema

O companheiro é só uma subclasse de openwar3.Bot, então pode virar um esquema de IA e ser compartilhado. No manifesto, acrescente dois campos:

{"id": "my-buddy", "name": "Meu companheiro", "entry": "my_buddy.py", "fair": false, "judge": false}

"fair": false: para usar o canal JASS (criar unidades, definir aliados); "judge": false: não decidir vitória e derrota pelas regras de confronto.

Registro de testes

2026-09-24, instância de teste, mapa WarChasers, velocidade 2×:

  • Canal JASS: todas as 18 verificações passaram — slots de jogador, conversão de ida e volta entre unidades e handles, valores de retorno reais, parâmetros de string, criar unidade num slot vazio, definir aliados, mudar nome, remover unidade; chamadas pela via do jogador e com o número errado de parâmetros foram corretamente rejeitadas.
  • Companheiro: passou sozinho pelo “Pressione qualquer tecla para continuar” → apareceu ao lado do mestre e cumprimentou → foi atrás dele até o círculo de poder da escolha de herói, recebeu um herói do mapa e o assumiu → seguiu (a 200 ~ 400 do mestre) → lutou contra monstros e, ao matar um, disse “Mandou bem!” → recuou com pouca vida → morreu, foi revivido pelo mapa e continuou seguindo.

O que ainda falta

  1. Não dá para ler qualquer texto que o jogador digita no chat. Os comandos de chat fixos já funcionam; para o companheiro conversar livremente com você, ainda é preciso obter o texto em si.
  2. O companheiro não entende a jogabilidade de um mapa específico (missões, lojas, história). Ele faz o genérico: seguir, ajudar na luta, curar. Para entender um mapa, escreva isso na subclasse, conforme aquele mapa — g.map_data consulta nomes, g.jass chama qualquer função. Essa é justamente a parte que fica para você decidir.