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.

Faire bouger les personnages : patrouilles et marches

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

Faire marcher un personnage tout seul : un garde qui patrouille dans une salle, une infirmière qui va d'une salle à l'autre et attrape le héros, Papi qui rentre chez lui une fois la clé trouvée. Tout ici est le DSL tel qu'il est : un script, walk, moveActor, un événement et des conditions.

Ce que vous obtenez

Un personnage qui va et vient entre deux endroits d'une salle, ou d'une salle à l'autre, en boucle ou quand quelque chose arrive ; des règles qui savent où il est ; et un jeu que le solveur prouve toujours, parce que la patrouille fait partie de l'état qu'il explore.

Contexte et versions

Prérequis

Où lancer les commandes

Écrivez le contenu dans les fichiers du jeu (rooms/<salle>.ts, game.ts, cast.ts). Vérifiez depuis la racine du projet : npm run validate et npm run solve dans le dépôt du moteur, npx quest-n-click validate et npx quest-n-click solve dans un projet créé.

Étapes : un garde qui patrouille dans une salle

  1. Un script en boucle dans la salle. Le garde marche jusqu'au coffre, attend, marche jusqu'à la fenêtre, attend, indéfiniment : { walk: [x, y], who: 'guard' } et { wait } dans un script en loop: true. Une boucle doit contenir un wait, waitUntil ou waitEvent.
  1. Dire où il est avec un drapeau. Les conditions ne savent pas lire la position d'un personnage dans une salle ; laissez le script poser un drapeau à chaque arrêt ({ set: ['guard_at', 'vault'] }), et les règles le tester ({ flag: 'guard_at', eq: 'vault' }).
  1. Arrêter la patrouille quand elle ne compte plus : while: '!vault_open' ramène le script au début et l'arrête une fois le coffre ouvert (ou { stopScript: 'guard_patrol' } depuis une règle).
  1. Les règles. Le cas particulier d'abord : ouvrir le coffre pendant qu'il est devant, c'est se faire prendre ; sinon, ça marche.
{
  "room": {
    "id": "vault_hall",
    "name": "La salle du coffre",
    "decor": "decor/vault_hall",
    "actors": { "guard": { "char": "guard" } },
    "hotspots": { "vault": { "name": "porte du coffre" } },
    "look": { "guard": "Un garde. Il marche. Il se retourne. Il marche.", "vault": "La porte du coffre." },
    "on": [
      { "id": "hall.vault-watched", "verb": "open", "a": "vault", "if": { "flag": "guard_at", "eq": "vault" },
        "do": [{ "say": ["guard", "Hé ! Éloigne-toi de cette porte."] }] },
      { "id": "hall.vault-open", "verb": "open", "a": "vault", "if": "!vault_open",
        "do": ["Vite, pendant qu'il est à la fenêtre.", { "set": "vault_open" }] }
    ],
    "scripts": [
      { "id": "guard_patrol", "loop": true, "while": "!vault_open",
        "stepIds": ["to-vault", "at-vault", "wait-vault", "to-window", "at-window", "wait-window"],
        "do": [
          { "walk": [520, 340], "who": "guard" },
          { "set": ["guard_at", "vault"] },
          { "wait": 4000 },
          { "walk": [140, 340], "who": "guard" },
          { "set": ["guard_at", "window"] },
          { "wait": 4000 }
        ] }
    ],
    "hints": [{ "id": "hall.hint", "until": "vault_open", "lines": ["Attends que le garde soit à la fenêtre."] }]
  },
  "layout": {
    "walk": { "area": [[20, 300], [620, 300], [630, 395], [10, 395]] },
    "entries": { "default": [320, 360] },
    "hotspots": { "vault": { "rect": [480, 140, 90, 170], "approach": [470, 350] } },
    "actors": { "guard": { "x": 140, "y": 340 } }
  },
  "characters": {
    "guard": { "name": "Garde", "color": "#8fd3ff", "kind": ["person"], "sprites": { "idle": ["guard/idle"], "walk": ["guard/walk1", "guard/walk2"] } }
  }
}

Les coordonnées de walk … who sont le seul endroit où l'on tape une position : un déplacement scripté. L'endroit où le garde s'arrête est gardé dans la sauvegarde.

Étapes : une patrouille d'une salle à l'autre (l'infirmière)

  1. Donnez au personnage une salle de départ. Un personnage déclaré comme acteur dans plusieurs salles se montre normalement dans toutes (Grand-mère dans la cuisine et dans le jardin). Donnez-lui une room, et seul son acteur de cette salle se montre ; moveActor l'envoie ailleurs :

    // cast.ts
    edna: { name: 'Edna', color: '#f0f', room: 'kitchen', sprites: { idle: ['edna/idle'], walk: […] } },
    // hall.ts et kitchen.ts déclarent tous deux   actors: { edna: { char: 'edna' } }   et la placent dans leur layout.
    // n'importe où :  { moveActor: ['edna', 'hall'] }             à sa position du layout dans le hall
    //                 { moveActor: ['edna', 'hall'], at: 'door' } à ce point d'entrée (ou ce point) du hall
    // conditions :    { actorIn: ['edna', 'hall'] }
    

    Le personnage disparaît de la salle qu'il quitte et apparaît dans celle qu'il rejoint, à l'écran si le joueur y est. Le validateur refuse moveActor vers une salle où le personnage n'est pas un acteur, ou pour un personnage sans room.

  1. Un script du jeu la déplace et le dit. Après chaque déplacement, il déclenche un événement ; un écouteur du jeu attrape le héros quand ils se croisent, et le onEnter de chaque salle l'attrape quand c'est lui qui entre :

    // game.ts
    scripts: [{ id: 'edna_patrols', loop: true, stepIds: ['w1', 'to-hall', 'moved-1', 'w2', 'to-kitchen', 'moved-2'], do: [
      { wait: 6000 }, { moveActor: ['edna', 'hall'] }, { emit: 'edna_moved' },
      { wait: 6000 }, { moveActor: ['edna', 'kitchen'] }, { emit: 'edna_moved' },
    ] }],
    events: [{ id: 'edna.catches', on: 'edna_moved',
      if: { any: [{ all: [{ room: 'hall' }, { actorIn: ['edna', 'hall'] }] }, { all: [{ room: 'kitchen' }, { actorIn: ['edna', 'kitchen'] }] }] },
      do: [{ say: ['edna', 'Je te tiens !'] }, { goto: 'dungeon' }] }],
    // hall.ts (et pareil dans kitchen.ts) :
    onEnter: [{ if: { actorIn: ['edna', 'hall'] }, then: [{ say: ['edna', 'Je te tiens !'] }, { goto: 'dungeon' }] }],
    

    Le cachot a une brique descellée qui ramène au hall. Le jeu entier est une fixture de test, tests/fixtures/classics.ts (mansion), et la mécanique est décrite dans mécaniques classiques.

  1. Points de reprise. Un point de reprise démarre avec chaque personnage mobile dans sa salle de départ ; where: { edna: 'hall' } en place un ailleurs. Un point de reprise qui a des goals doit être un état que le jeu atteint vraiment quand ces buts tiennent : npm run solve -- --prove --chapters prouve chaque chapitre depuis chaque état frontière atteignable et signale un point de reprise qui n'en reproduit aucun, avec les dimensions qui diffèrent (room: house vs market). used, seen et players[].used permettent à un point de reprise de nommer ce qu'une vraie partie laisse derrière elle (un objet déjà utilisé, un écouteur once déjà déclenché) (chapitres).

Limite · turn-by-turn Une patrouille va tour par tour, pas en temps réel. Les scripts tournent entre les actions du joueur, jamais pendant : l'infirmière ne peut pas entrer pendant que le héros parle ou marche. C'est ce qui permet au solveur de prouver l'énigme (il attend qu'elle parte, puis traverse) et à une sauvegarde de tout restaurer à l'identique. Une séquence qui ne doit pas être interrompue va dans une cutscene.

Limite · no-position-condition Aucune condition ne lit l'endroit où se tient un personnage dans une salle, ni ce qu'il voit. Représentez « le garde est à la porte » par un drapeau que pose le script, et « le garde est dans cette salle » par { actorIn }.

Garantie · patrol-turn-by-turn · référencée · exécutée sur aff6caab7514 · portée vérifiée par son auteur (IA), pas relue Un personnage que déplace un script attrape le héros tour par tour : quand le héros entre dans sa salle, ou quand elle entre dans la sienne.

Résultat attendu, et comment le vérifier

Garantie · patrol-solved · référencée · exécutée sur aff6caab7514 · portée vérifiée par son auteur (IA), pas relue Le solveur prouve le jeu de l'infirmière en patrouille : il laisse tourner son script et attend qu'elle parte.

Erreurs fréquentes

SymptômeCauseCorrection
loop script "…" never waits (add a wait, waitUntil or waitEvent)Une boucle sans pauseAjoutez un { wait }
schema v3 script needs one stable step id per command (n expected)stepIds absent ou de la mauvaise longueurUn id par commande de premier niveau
character "…" has no starting room ("room" in its definition), needed to move it between roomsmoveActor sur un personnage sans roomAjoutez room à sa définition
"…" is not an actor in <salle>: declare it there (actors) and place it in the layoutmoveActor vers une salle qui ne le déclare pasDéclarez-y l'acteur et placez-le
Le personnage se montre dans toutes les salles à la foisPas de room sur le personnageDonnez-lui une salle de départ
Le personnage glisse au lieu de marcherPas de pose walkAjoutez walk à ses sprites
La patrouille s'arrête pour de bon après un échange d'objetUn while vérifié avant chaque commandeGagnez le nouvel objet avant de perdre l'ancien

Fichiers ou données modifiés

rooms/<salle>.ts (le script, les règles et le onEnter de la salle), game.ts (scripts du jeu, écouteurs, points de reprise), cast.ts (la salle de départ), layout/<salle>.json (l'acteur placé dans chaque salle). Dans une sauvegarde : la position du script, l'endroit où se trouve chaque personnage mobile (where), la position de chaque acteur et les drapeaux que pose le script.

Reprise ou retour arrière

Retirer un script ou renommer ses étapes change ce que contient une ancienne sauvegarde : ajoutez une étape de migration (renameScript, renameScriptStep, dropScript) quand des joueurs ont des sauvegardes (état et sauvegardes).

Références


Modifier la source de cette page · Signaler une erreur