Aller au contenu principal

Frameworks de code

Recette : jet contre une compétence

Comparez un jet en pourcentage à la valeur d'une compétence et indiquez un palier de réussite.

Objectif

Laissez un joueur lancer un d100 contre une compétence Lore et voir un palier : Flawless, Strong, Pass ou Miss. Un jet plus bas est meilleur.

Comment le jet arrive dans la règle

Les règles ne peuvent pas lancer de dés. Le joueur lance sur la fiche et saisit le nombre dans une action. La règle valide le nombre et l'écrit. La boucle de jeu compte quatre étapes.

  1. Cliquer sur le badge de dés

    La fiche affiche Roll: [[1d100]]. Un clic lance un d100.

  2. Lire le jet dans le journal

    Le résultat apparaît dans le journal des dés de la table et dans le chat. Rien n'est encore écrit sur le personnage.

  3. Lancer l'action et saisir le jet

    Utilisez le bouton Lore check. Fabled demande le jet dans une boîte de dialogue. Saisissez le nombre lu dans le journal.

  4. Le palier se met à jour

    La règle écrit le jet et la cible. La fonction de registre calcule le palier, et la fiche l'affiche.

Le changement

Ajoutez au schéma une entrée Lore, l'action et la fonction de registre. Une entrée de constructeur peut avoir des bornes.

schemas/keeper.json
    "lore": {
      "label": "Lore",
      "description": "The percentile a Lore check must roll under.",
      "defaultValue": 40,
      "min": 1,
      "max": 99
    }
  1. Lignes 13-14 Ces bornes s'appliquent quand un personnage est créé. Elles ne s'appliquent pas au jet.
schemas/keeper.json
    {
      "id": "check_lore",
      "name": "Lore check",
      "source": "rules/check-lore.ts",
      "inputs": { "roll": { "label": "Roll", "min": 1, "max": 100 } }
    }
  1. Lignes 21-23 L'action de test Lore et son fichier de règles.
  2. Ligne 24 L'entrée Roll : un libellé et des bornes. La boîte de dialogue les affiche et le serveur les impose.
schemas/keeper.json
  "ledgerFunctions": [
    {
      "id": "last_band",
      "name": "Last band",
      "field": "last_band",
      "source": "rules/band.ts"
    }
  ]
  1. Lignes 32-35 La fonction de registre déduit last_band des deux champs que l'action écrit.

Le constructeur renvoie les nouveaux champs avec des valeurs provisoires. La fonction de registre remplace last_band quand l'Élément est construit.

rules/keeper.ts
export function construct(
  name: string,
  max_hp: number,
  lore: number,
): {
  name: string;
  max_hp: number;
  hp: number;
  lore: number;
  last_roll: number;
  last_target: number;
  last_band: string;
} {
  return {
    name: name,
    max_hp: max_hp,
    hp: max_hp,
    lore: lore,
    last_roll: 0,
    last_target: 0,
    last_band: "",
  };
}
  1. Lignes 10-12 Les champs que l'action écrit et que la fonction de registre lit.
  2. Lignes 19-21 Valeurs de départ : pas encore de jet.

Les bornes de l'entrée gardent le jet entre 1 et 100. L'action ignore un nombre à décimales et écrit sinon le jet et la cible.

rules/check-lore.ts
export function check_lore(self: ElementRef, roll: number): void {
  if (math.floor(roll) === roll) {
    const lore: number = element.var(self, "lore") as number;
    ledger.set(self, "last_target", lore);
    ledger.set(self, "last_roll", roll);
  }
}
  1. Ligne 1 Le jet est une entrée que le joueur saisit.
  2. Ligne 2 Ignorer un nombre à décimales. Les bornes de l'entrée le gardent déjà entre 1 et 100.
  3. Lignes 4-5 Écrivez la cible et le jet. La bande en découle.

Une fonction de registre les transforme en bande. Chaque compétence peut l'utiliser, car chaque action de compétence écrit les deux mêmes champs.

rules/band.ts
export function last_band(currentValue: string, self: ElementRef): string {
  const roll: number = element.var(self, "last_roll") as number;
  const target: number = element.var(self, "last_target") as number;
  if (roll === 0) {
    return "No roll yet";
  }
  if (roll <= math.floor(target / 5)) {
    return "Flawless";
  }
  if (roll <= math.floor(target / 2)) {
    return "Strong";
  }
  if (roll <= target) {
    return "Pass";
  }
  return "Miss";
}
  1. Lignes 2-3 Lisez les deux champs que l'action a écrits.
  2. Lignes 4-6 Avant tout jet, dites-le.
  3. Lignes 7-15 Un jet plus bas est meilleur. Chaque bande est une part de la cible.
  4. Ligne 16 Au-dessus de la cible, c'est un échec.

Pour une autre compétence, copiez check-lore.ts, puis changez le nom de la fonction et le champ dans l'appel element.var. Chaque compétence a besoin de sa propre action, car un nom de champ est écrit en toutes lettres dans le code.

Affichez les dés, la bande et un bouton sur la fiche. Listez la bande dans requiredBindings et l'action dans les alias.

layouts/keeper.json
      { "binding": "element.attributes.last_band", "expectedType": "string" }
  1. Ligne 15 La bande est du texte.
layouts/keeper.json
      { "name": "check_lore", "functionRef": "check_lore" }
  1. Ligne 21 L'alias que le bouton utilise.
layouts/keeper.json
      { "kind": "text", "text": "Roll: [[1d100]]" },
      {
        "kind": "text",
        "text": "Last check: {{element.attributes.last_band}}"
      },
      { "kind": "button", "label": "Lore check", "actionRef": "check_lore" }
  1. Ligne 35 Le badge de dés. Un clic lance et montre le résultat dans le journal de dés.
  2. Lignes 36-39 La bande que la fonction de registre a déduite.
  3. Ligne 40 Le bouton qui ouvre la boîte de dialogue pour le jet.

Testez-la

Testez chaque palier, et un jet hors de la plage. Chaque étape liste les deux écritures. expectedValuesAfter vérifie le palier après l'action.

scenarios/lore.json
    {
      "action": "check_lore",
      "parameters": { "roll": 6 },
      "expectedWrites": [
        { "op": "ledger.set", "key": "last_target", "value": 40 },
        { "op": "ledger.set", "key": "last_roll", "value": 6 }
      ],
      "expectedValuesAfter": { "last_band": "Flawless" }
    },
  1. Ligne 17 Le nombre que le joueur a saisi.
  2. Lignes 18-21 Les deux écritures, dans l'ordre.
  3. Ligne 22 Le palier dérivé après l'action.

La dernière étape est un jet de 40,5. Elle n'écrit rien, donc la garde contre les décimales est prouvée. Un jet de 500 ne peut pas être une étape : il est hors des bornes de l'entrée, et le scénario est refusé.

scenarios/lore.json
    {
      "action": "check_lore",
      "parameters": { "roll": 40.5 },
      "expectedWrites": [],
      "expectedValuesAfter": { "last_band": "Miss" }
    }
  1. Ligne 53 Un nombre à décimales.
  2. Ligne 54 Aucune écriture.
  3. Ligne 55 Le palier reste tel que l'étape précédente l'a laissé.

Listez le fichier de scénario dans le manifeste. Ajoutez les nouveaux champs aux valeurs attendues des autres scénarios.

manifest/project.json
{
  "formatVersion": "fabled.game-project/v1",
  "projectId": "game-your-game-id",
  "projectKind": "GAME",
  "compilerProfile": "native-game-v1",
  "name": "Lantern Keepers",
  "entryFramework": "keeper",
  "frameworks": ["schemas/keeper.json"],
  "scenarios": [
    "scenarios/new-keeper.json",
    "scenarios/heal.json",
    "scenarios/lore.json"
  ]
}
  1. Ligne 12 Le nouveau fichier de scénario.

Suite