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:
| Camada | O que é | Para quem |
|---|---|---|
Canal JASS g.jass | As 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ência | g.spawn, g.set_alliance, g.player_slots, g.show_text, g.map_data: as tarefas mais comuns, já empacotadas | Quem quer escrever os próprios scripts auxiliares |
| Framework de companheiro | openwar3.companion.Companion + openwar3.talk.Talk: herde, mude algumas propriedades e você tem um parceiro que segue, ajuda na luta, cura e conversa | Quem 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
- Escolha o mapa: no Farsight, página “Instâncias e partidas” → “Próxima partida” → Mapa, escolha um mapa RPG (tudo o que estiver em
ScenarioeDownload, dentro deMapsna pasta do jogo, aparece na lista, por exemplo(4)WarChasers). - Escolha o esquema: no menu “Esquema de IA” do card da instância, escolha Exemplo de companheiro (buddy) → “Selecionar”.
- 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
| mode | Quem é o companheiro | Descrição |
|---|---|---|
ally (padrão) | Ocupa um slot de jogador vazio como seu aliado | Tem 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 |
own | Criado em seu nome | Você pode comandá-lo manualmente a qualquer momento; quando você não está cuidando dele, a IA o controla por você |
adopt | Assume uma unidade que já existe no mapa | Sobrescreva adopt(g) para retornar essa unidade (o mascote ou o seguidor que o mapa dá a você) |
voice | Não cria unidade, só fala | Faz 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:
| Ordem | Comportamento | Condição | Ajuste |
|---|---|---|---|
| 1 | Recuar | A própria vida abaixo de 25% e inimigos por perto: recua para trás do mestre | retreat_at |
| 2 | Curar | Vida do mestre abaixo do valor definido, habilidade fora de recarga, distância de até 900 | heal (None desliga) |
| 3 | Ajudar na luta | Inimigos perto do mestre: quem está atacando o mestre > quem o mestre está atacando > o mais próximo | assist_radius, ou sobrescreva pick_target |
| 4 | Seguir | Se estiver longe demais do mestre, vai atrás; se estiver muito longe, volta correndo sem ficar lutando | follow_distance, leash |
| 5 | Conversar | Sem inimigos por perto, solta uma frase a cada 1 ~ 2.5 minutos | Tabela 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
-followpara ele seguir você,-staypara ficar de guarda onde está,-healpara curar na hora,-hipara cumprimentar. Para mudar a lista de comandos, alterecommands; para mudar as reações, sobrescrevaon_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.
| Evento | Quando | Evento | Quando |
|---|---|---|---|
hello | Acabou de chegar | master_low | Mestre com pouca vida |
poke | O mestre clicou nele com o botão direito | master_levelup | Mestre subiu de nível |
fight | Começou a luta | master_died / master_back | Mestre caiu / reviveu |
kill | Matou um monstro (diz o nome dele) | buddy_low / buddy_died / buddy_back | O próprio companheiro com pouca vida / caiu / voltou |
healed | Curou o mestre | idle / item | Conversa à 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
- 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.
- 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_dataconsulta nomes,g.jasschama qualquer função. Essa é justamente a parte que fica para você decidir.