Docs Extensions de jeu

Compagnon RPG

Donnez au joueur un compagnon IA dans les cartes RPG et personnalisées : il vous suit, combat à vos côtés, vous soigne quand votre vie est basse et vous fait la conversation. Quatre modes ; héritez d’une classe, changez quelques attributs, et vous avez votre propre compagnon.

Il n’y a pas que les parties de mêlée. Dans les cartes RPG et personnalisées, vous pouvez vous adjoindre un compagnon IA : il vous suit, combat les monstres avec vous, vous soigne quand votre vie est basse et, quand il ne se passe rien, vous fait un brin de conversation — ses répliques peuvent même venir d’un LLM local.

C’est à vous de décider comment vous en servir. L’ensemble se découpe en trois couches d’interfaces, de la plus basse à la plus haute, et chacune s’utilise directement :

CoucheCe que c’estIdéal pour
Canal JASS g.jassLes 1291 fonctions JASS dont disposent les créateurs de cartes, appelées directement par leur nom (créer des unités, définir des alliances, donner des objets, renommer, afficher du texte, ressusciter des héros…)Inventer votre propre gameplay
Interfaces pratiquesg.spawn, g.set_alliance, g.player_slots, g.show_text, g.map_data : les opérations courantes, déjà empaquetéesÉcrire vos propres scripts d’assistance
Framework de compagnonopenwar3.companion.Companion + openwar3.talk.Talk : héritez-en, changez quelques attributs, et vous obtenez un compagnon qui vous suit, combat, soigne et discuteAvoir un compagnon

Uniquement pour les parties en solo ou hébergées vous-même en réseau local. Créer des unités ou définir des alliances, c’est modifier le monde unilatéralement depuis votre machine : aucun problème en partie solo (contre l’ordinateur) ; en multijoueur, les autres joueurs seraient désynchronisés. En partie multijoueur, le canal JASS n’autorise donc que les fonctions en lecture seule, et le compagnon se replie automatiquement sur « parler seulement ».

Démarrage le plus rapide : un clic dans Farsight

  1. Choisir la carte : dans Farsight, page « Instances et lancement » → « Réglages de la partie suivante » → Carte, choisissez une carte RPG (toutes celles des dossiers Scenario et Download sous Maps dans le dossier du jeu sont listées, par exemple (4)WarChasers).
  2. Choisir le schéma : dans la liste déroulante « Schéma d’IA » de la fiche de l’instance, choisissez Exemple de compagnon (buddy) → « Sélectionner ».
  3. Lancer le test : une fois le jeu démarré, jouez vous-même dans la fenêtre du jeu. Le compagnon — un paladin nommé « Lumi » — apparaît à côté de vous.

Vous pouvez aussi passer par la ligne de commande :

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

--rpg ("judge": false dans le manifeste du schéma) signifie que la partie n’est pas jugée selon les règles de mêlée : dans un RPG, les héros peuvent ressusciter, et il n’y a pas de « défaite quand tous les bâtiments sont détruits ». Beaucoup de cartes RPG s’arrêtent après le chargement sur « Appuyez sur une touche pour continuer » ; quand le SDK constate qu’il est « en partie, mais l’horloge du jeu reste à 0 », il appuie lui-même sur Espace (g.press_to_continue(), qui envoie seulement un message de touche à la fenêtre du jeu, sans lui donner le focus).

Écrire votre propre compagnon

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

class MyBuddy(Companion):
    mode = "ally"                        # mode, voir le tableau ci-dessous
    unit = "Hpal"                        # quoi créer : n'importe quel code à quatre caractères, même ceux propres à la carte
    nickname = "Lumi"
    heal = ("holybolt", "AHhb", 0.55)    # (nom d'ordre du sort, compétence à apprendre, seuil de vie du maître déclenchant le soin) ; None = pas de soin
    follow_distance = 350
    talk = Talk(persona="Petit paladin plein d'entrain, qui adore encourager son maître")

Quatre modes

modeQui est le compagnonRemarques
ally (par défaut)Occupe un emplacement de joueur libre et devient votre alliéA sa propre couleur et son propre nom (le tableau des scores et le panneau des alliés affichent nickname) ; vous ne pouvez pas le commander, il se bat seul. Le framework configure automatiquement l’alliance + la vision partagée
ownCréé sous votre contrôleVous pouvez le commander à la main à tout moment ; quand vous ne vous en occupez pas, l’IA le dirige à votre place
adoptPrend le contrôle d’une unité déjà présente sur la carteSurchargez adopt(g) pour renvoyer cette unité (un familier ou un suivant que la carte vous donne)
voiceNe crée pas d’unité, ne fait que parlerConversation, rappels ; ne modifie pas le monde, donc utilisable aussi en multijoueur

Sans emplacement libre, ally se replie automatiquement sur own ; en multijoueur, ou si l’unité ne peut pas être créée, il se replie sur voice.

Un compagnon ally s’attache à « la meilleure unité de cet emplacement à l’instant » (héros en priorité), et non à une unité fixe. En test, une carte a traité le compagnon comme un vrai joueur, supprimé le paladin et distribué un héros de la carte — le compagnon a tout simplement pris le contrôle de ce héros, et apprend aussi les compétences que la carte lui attribue. Quand le héros meurt, il le ressuscite de préférence sur place ; si la carte le ressuscite elle-même, il continue simplement à l’utiliser.

Ce qu’il fait à chaque tick

Il vérifie les conditions dans l’ordre et applique la première qui est remplie :

RangComportementConditionRéglage
1RepliSa propre vie est sous 25 % et des ennemis sont proches : il se replie derrière le maîtreretreat_at
2SoinLa vie du maître est sous le seuil fixé, la compétence est rechargée, le maître est à 900 au plusheal (None pour désactiver)
3AssistanceDes ennemis autour du maître : celui qui attaque le maître > celui que le maître attaque > le plus procheassist_radius, ou surcharger pick_target
4SuiviTrop loin du maître, il le rattrape ; au-delà d’une certaine distance, il revient en courant sans s’attarder au combatfollow_distance, leash
5BavardageSans ennemi autour, une réplique toutes les 1 à 2.5 minutesTable des répliques

Les « ennemis » sont déterminés d’après les relations d’alliance dans le jeu (rafraîchies toutes les 20 secondes). Les cartes RPG comptent souvent plusieurs joueurs alliés : on ne peut pas simplement considérer « tous les joueurs sauf moi » comme des ennemis.

Hooks que vous pouvez surcharger : find_master (qui est le maître ; par défaut, le héros de plus haut niveau du joueur local), adopt, pick_target, on_poke (le maître a fait un clic droit sur le compagnon), ainsi que on_start / on_tick / on_event / on_end du Bot. Le nombre de soins, d’assistances, d’éliminations, de suivis, de replis, de répliques et de résurrections est enregistré dans self.stats et affiché à la fin.

Comment l’appeler

  • Commandes de chat : dans la zone de chat, tapez -follow (suis-moi), -stay (reste ici en garde), -heal (soigne-moi tout de suite) ou -hi (salut). Pour changer la table des commandes, modifiez commands ; pour changer les réactions, surchargez on_command.
  • Clic droit sur le compagnon : déclenche on_poke. Dans l’exemple, la réaction est la suivante : si le maître n’a pas toute sa vie, il lui donne un soin ; sinon, il dit une phrase.
  • Dialogue avec portrait : les salutations, la chute du maître, sa montée de niveau et le retour du compagnon passent par le dialogue avec portrait du jeu lui-même (le portrait en bas de l’écran devient celui du compagnon, et un sous-titre s’affiche à l’écran) ; tout le reste apparaît en bulle au-dessus de sa tête.
  • Panneau d’état : un panneau sur la gauche de l’écran affiche la barre de vie du compagnon, ce qu’il est en train de faire, son humeur (joyeux / excité / tendu / effrayé / triste), ses éliminations et ses soins. Il est dessiné avec le canevas, donc sans risque en multijoueur.

Parler, et le LLM local

Talk choisit les répliques selon les événements et les affiche en bulle au-dessus de la tête ; en mode voice, ou quand une bulle ne peut pas s’afficher, elles apparaissent en bas à gauche de l’écran. Chaque réplique est aussi écrite dans le journal du schéma : vous pouvez vérifier après coup ce qu’il a dit.

ÉvénementQuandÉvénementQuand
helloÀ son arrivéemaster_lowLe maître a peu de vie
pokeLe maître fait un clic droit sur luimaster_levelupLe maître monte de niveau
fightDébut d’un combatmaster_died / master_backLe maître tombe / ressuscite
killIl tue un monstre (et dit son nom)buddy_low / buddy_died / buddy_backLe compagnon lui-même a peu de vie / tombe / revient
healedIl a soigné le maîtreidle / itemBavardage / objet ramassé

Les répliques peuvent contenir les espaces réservés {master}, {me}, {map}, {enemy}, {level}, {item} ; pour changer les répliques, modifiez directement talk.lines ; le temps de recharge se règle dans talk.cooldown.

Brancher un LLM local : Talk(llm=LocalLLM(url, model)), avec n’importe quelle API compatible OpenAI (LM Studio, Ollama…). Le modèle répond dans un thread en arrière-plan, et la réplique n’est dite qu’une fois la réponse arrivée ; si le modèle n’est pas lancé, dépasse le délai ou renvoie une erreur, une réplique fixe est utilisée, sans jamais bloquer le jeu. Les requêtes ne partent que vers l’adresse locale que vous indiquez, et contiennent ce qui se passe dans la partie (le nom du maître, les monstres tués).

Noms des unités des cartes personnalisées

La plupart des unités, objets et héros des cartes RPG sont créés par la carte elle-même (codes à quatre caractères comme HC07, I00A) et ne figurent pas dans la table de noms intégrée. g.map_data lit directement le fichier de carte de la partie en cours :

md = g.map_data
md.name_of("HC07")        # 'Optimus Primo' — le nom modifié par la carte est prioritaire
md.hero_names("HC07")     # liste des noms propres
md.hero_skills("OC10")    # compétences que la carte attribue à ce héros
md.tooltip("I00A")        # texte descriptif

Les cartes protégées ou optimisées (beaucoup de RPG populaires) n’incluent pas les fichiers standard de données d’objets ; les noms sont alors lus dans les données texte de la carte. En test, les 38 cartes RPG / personnalisées de cette machine ont toutes été analysées avec succès, et les noms d’unités ont été obtenus pour 37 d’entre elles.

En faire un schéma à partager

Un compagnon n’est qu’une sous-classe de openwar3.Bot : vous pouvez en faire un schéma d’IA et le partager. Ajoutez deux champs au manifeste :

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

"fair": false : nécessaire pour utiliser le canal JASS (créer des unités, définir des alliances) ; "judge": false : ne pas juger la partie selon les règles de mêlée.

Relevé de test

2026-09-24, instance de test, carte WarChasers, vitesse ×2 :

  • Les 18 vérifications du canal JASS sont toutes passées : emplacements de joueur, conversions aller-retour entre unités et handles, valeurs de retour de type real, arguments de type chaîne, création d’une unité dans un emplacement libre, définition d’alliances, renommage, suppression d’unités ; les appels depuis une voie player et les mauvais nombres d’arguments ont bien été rejetés.
  • Compagnon : a passé seul l’écran « Appuyez sur une touche pour continuer » → est apparu à côté du maître et l’a salué → l’a suivi dans le cercle de pouvoir de sélection des héros, a reçu un héros de la carte et en a pris le contrôle → suivi (à 200 à 400 du maître) → a combattu des monstres et lancé « Joli ! » après en avoir tué un → s’est replié avec peu de vie → mort, ressuscité par la carte, il a repris le suivi.

Pas encore fait

  1. Impossible de lire le texte libre que le joueur tape dans le chat. Les commandes de chat fixes fonctionnent déjà ; pour que le compagnon discute vraiment librement avec vous, il faut encore accéder au texte lui-même.
  2. Le compagnon ne connaît pas le gameplay d’une carte précise (quêtes, boutiques, scénario). Il fait du générique : suivre, assister, soigner ; pour qu’il comprenne une carte donnée, écrivez-le dans votre sous-classe pour cette carte — g.map_data retrouve les noms, g.jass appelle n’importe quelle fonction. C’est précisément la part qui vous revient.