quest'n'click 4.5.0 · r1

Construite pour quest'n'click 4.5.0, révision r1. Si elle est publiée, et laquelle est la courante, est dit par publication.json.

Un mini-jeu sur mesure

English · Type : procédure · Public : auteur · Contexte : dépôt du moteur, projet créé · Statut : prête · S'applique depuis : 4.2.5

Quand aucun des mini-jeux intégrés ne convient, un jeu écrit le sien en TypeScript et le lance avec la même commande minigame. Cette page en déroule un, du contrat jusqu’aux vérifications.

Vue d’ensemble : les mini-jeux comme greffons

Le moteur fournit pipes, cables, pick, hide, runner, stroke, scratch et la roue de codes. Un jeu ajoute les siens dans les minigames qu’exporte son module (index.ts) : un objet { run(ctx): Promise<void>; required?: string[] } où ctx donne l’élément de superposition, l’échelle, les images, les sons, les params de la commande et un signal d’annulation ; required nomme les params que le validateur vérifie. { minigame: 'duel', params: { … }, then: [ … ] } le joue ensuite comme un mini-jeu intégré. C’est le format des greffons : le même contrat que les mini-jeux intégrés.

Le module public quest-n-click/minigames donne ce qu’il faut à un greffon :

NomQuoi
Minigamele contrat : run(ctx), et ce que le validateur lit (required, textParams, bindings)
MinigameCtxce qu’un mini-jeu reçoit : sa zone, images, sons, paramètres, libellés, un signal d’abandon
minigamesceux du moteur (pipes, stroke, pick, hide, runner, scratch, cables, code-wheel), chacun chargé quand il démarre
MINIGAME_CSSleur style commun

Les signatures complètes sont dans l’API publique et dans src/engine/minigames/types.ts.

Ce que vous obtenez

Un mini-jeu à vous, enregistré sous un id, qu’un script ou une règle lance avec { minigame: '<id>', params: { … }, then: [ … ] } ; le validateur vérifie ses paramètres, ses images et ses sons comme ceux d’un mini-jeu intégré, et le lecteur l’affiche par-dessus la scène avec le cadre de la voix des indices.

Contexte et versions

Un projet créé (le module est game/index.ts) ou un jeu du dépôt du moteur (games/<id>/index.ts). Le contrat est public depuis la 4.0 ; le module s’appelle quest-n-click/minigames depuis la 4.2.5 (web-scumm/minigames avant, retiré en 4.3.0 : quest-n-click migrate le renomme). MinigameCtx.random existe depuis la 4.1.15, le résultat qu’un mini-jeu peut signaler depuis la 4.1.16.

Prérequis

Où lancer les commandes

Sur votre machine, dans le dossier du projet créé (npx quest-n-click …) ou dans le dépôt du moteur (GAME=<id> npm run …). Le code va dans le module du jeu : game/index.ts dans un projet créé, games/<id>/index.ts dans le dépôt du moteur.

Étapes

  1. Écrire run(ctx). Il remplit ctx.root (un élément par-dessus la scène, en 16:10 ; ctx.u est le nombre de pixels par unité logique, la largeur de la racine / 640), et renvoie une promesse qui se résout quand le joueur a gagné ou passé. Un mini-jeu n’échoue jamais de lui-même : la promesse se résout, et then s’exécute. Nettoyez quand ctx.signal est annulé (l’hôte ferme le mini-jeu). Les images viennent des params par ctx.img(id) et ctx.size(id) ; les sons par ctx.sfx(id) ; ctx.instruct(texte) affiche une réplique dans le cadre de la voix des indices ; ctx.labels.skip est le libellé « Passer » du jeu.

    // game/index.ts dans un projet créé (games/<id>/index.ts dans le dépôt du moteur)
    import type { Minigame } from 'quest-n-click/minigames';
    
    const swordFight: Minigame = {
      required: ['foe'],
      textParams: ['intro', 'win'],
      bindings: { images: ['foe'], sfx: ['hit'] },
      run(ctx) {
        return new Promise<void>((resolve) => {
          const box = document.createElement('div');
          ctx.root.append(box);
          const done = () => {
            box.remove();
            resolve();
          };
          ctx.signal.addEventListener('abort', done, { once: true });
          if (typeof ctx.params.intro === 'string') ctx.instruct(ctx.params.intro);
          const foe = document.createElement('img');
          foe.src = ctx.img(String(ctx.params.foe));
          foe.style.height = `${120 * ctx.u}px`;
          let hits = 0;
          foe.addEventListener('click', () => {
            if (typeof ctx.params.hit === 'string') ctx.sfx(ctx.params.hit);
            hits += 1;
            if (hits === 3) {
              if (typeof ctx.params.win === 'string') ctx.instruct(ctx.params.win);
              setTimeout(done, 1200);
            }
          });
          const skip = document.createElement('button');
          skip.textContent = ctx.labels.skip;
          skip.addEventListener('click', () => {
            box.dispatchEvent(new CustomEvent('mg-skip', { bubbles: true }));
            done();
          });
          box.append(foe, skip);
        });
      },
    };
    
  1. Déclarer ce que lisent les outils. required : les params qu’un { minigame } doit donner, sinon le validateur le refuse. textParams : les chemins pointés des chaînes visibles par le joueur dans params (* parcourt chaque élément d’un tableau ou valeur d’un objet, comme dans rounds.*.prompt). bindings : les params qui nomment une image (images) ou un son de audio.sfx (sfx), dont le validateur vérifie l’existence. Tout param texte qui a la forme d’un id d’image (dossier/nom) est aussi vérifié comme image.
  1. L’enregistrer dans les minigames du module, que le modèle exporte déjà vide :

    export const minigames: Record<string, Minigame> = { sword_fight: swordFight };
    

    Le lecteur, le Studio et le validateur lisent tous cet export, fusionné par-dessus les mini-jeux intégrés. Un id qui est déjà celui d’un mini-jeu intégré le remplace pour ce jeu.

  1. Le jouer depuis une règle, un sujet ou un script :

    { minigame: 'sword_fight', params: { foe: 'pirate/r1c1', hit: 'clang', intro: 'Trois touches !', win: 'Touché.' },
      then: [{ set: 'pirate_beaten' }] }
    
  1. Dire comment il s’est terminé (facultatif). Le lecteur écrit le résultat dans le drapeau réservé minigame.<id> : skipped quand un événement mg-skip remonté est sorti de la zone du mini-jeu (comme l’envoie le bouton Passer ci-dessus), won sinon. Pour donner un autre résultat (passed, failed, disabled), émettez depuis ctx.root un new CustomEvent('mg-record', { bubbles: true, detail: { result: 'failed' } }) remonté avant de résoudre. L’histoire le lit avec { flag: 'minigame.sword_fight', eq: 'failed' }.
  1. Vérifier.

    # dans le projet créé
    npx quest-n-click validate
    npx quest-n-click dev
    
    # dans le dépôt du moteur
    GAME=mygame npm run validate
    GAME=mygame npm run dev
    

Limite · custom-minigame-solver Le solveur n’exécute pas run : il compte le mini-jeu gagné, exécute then, et n’enregistre aucun drapeau minigame.<id>. Mettez l’effet du mini-jeu sur la partie dans then (ou dans les commandes qui suivent), jamais dans run : la sauvegarde, le solveur et la rediffusion ne connaissent que les commandes.

Limite · custom-minigame-i18n-extract npm run i18n -- extract (npx quest-n-click i18n extract) écrit les textes des params des mini-jeux intégrés, pas ceux d’un mini-jeu propre au jeu : en 4.4.0 il lit le jeu sans les minigames de son module. Ses textParams sont appliqués quand le lecteur charge une traduction, mais c’est à vous d’ajouter leurs chemins au fichier de langue, et une extraction suivante peut les ranger en _stale.

Limite · custom-minigame-random ctx.random (un tirage dans le flux minigame:<id> de la partie, le même pour la même graine) est fourni par le lecteur du moteur et peut manquer sous un autre hôte. Préférez-le à Math.random quand une disposition ou un rythme doivent se rejouer à l’identique.

Résultat attendu, et comment le vérifier

validate ne dit rien sur la commande. Dans le jeu, la règle ouvre le mini-jeu par-dessus la scène, la voix des indices affiche intro, trois touches affichent win et le ferment, et pirate_beaten est posé ; Passer le ferme tout de suite et then s’exécute aussi. Le journal de développement (?dev) montre le changement du drapeau minigame.sword_fight.

Erreurs fréquentes

SymptômeCauseCorrection
unknown minigame: "sword_fight" (known: …)Absent des minigames du module, ou une faute dans l’idL’enregistrer sous cet id exact
minigame "sword_fight": missing required param "foe"La commande ne donne pas un param listé dans requiredAjouter le param, ou le retirer de required
minigame "sword_fight": "foe" must be an image idUn param de bindings.images n’est pas une chaîneDonner un id d’image, dossier/nom
minigame "sword_fight": unknown sound "clang" (audio.sfx)Un param de bindings.sfx ne nomme aucun son de audio.sfxDéclarer le son (Bruitages)
… image not found: "pirate/r1c1"L’image n’est pas découpée ou pas préparéenpm run assets (npx quest-n-click assets)
Le mini-jeu ne se ferme jamaisLa promesse de run ne se résout jamaisLa résoudre à la victoire, au passage et à l’annulation de ctx.signal

Fichiers ou données modifiés

Le module du jeu (game/index.ts, ou games/<id>/index.ts) et le contenu qui le lance. Les sauvegardes d’un joueur gagnent le drapeau minigame.<id> une fois le mini-jeu joué.

Revenir en arrière

Retirez la commande, puis l’entrée de minigames : le validateur refuse une commande dont le mini-jeu n’est plus enregistré, il montre donc chaque endroit oublié. Un drapeau minigame.<id> dans d’anciennes sauvegardes y reste, sans effet tant que l’histoire ne le lit pas.

Références


Modifier la source de cette page · Signaler une erreur