Salta al contenuto principale

Framework in codice

Avvio rapido: il tuo primo framework in codice

Scrivi un piccolo gioco con un personaggio, due azioni e una scheda. Poi verificalo e crea un personaggio in The Forge.

Prima di iniziare

  • Per creare un Gioco serve un piano a pagamento.
  • Crea un gioco privato in The Forge. Scegli «CREA IL TUO GIOCO», poi «Editor manuale», e crea il gioco.
  • Scrivi i file in un editor qualsiasi, poi incollali nell'editor del sorgente, oppure scrivili direttamente lì.

Il gioco

Lantern Keepers ha un personaggio, il Keeper. Un Keeper ha un nome, i PF massimi e i PF attuali. Due azioni cambiano i PF: Subisci danno e Cura. La cura non supera mai il massimo.

Passo 1: il manifest

Il manifest dà un nome al progetto ed elenca ogni Framework e ogni file di scenario. Conserva il projectId che l'editor ha creato per il tuo gioco. Il valore in questo esempio è solo un segnaposto.

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"]
}
  1. Riga 3 Conserva il projectId che l'editor ha creato per il tuo gioco. Questo è solo un segnaposto.
  2. Righe 4-5 GAME è un gioco completo con un personaggio giocabile. native-game-v1 è l'unico profilo.
  3. Riga 7 Il Framework che un giocatore crea per primo.
  4. Righe 8-9 Qui sono elencati tutti i file Framework e tutti i file di scenario.

Passo 2: il Framework

Un file Framework dichiara cosa inserisce il giocatore (input), cosa può fare il Keeper (azioni) e quale Frame lo disegna. Il costruttore e ogni azione puntano a un file di regole.

schemas/keeper.json
{
  "id": "keeper",
  "name": "Keeper",
  "elementType": "character",
  "constructor": "rules/keeper.ts",
  "inputs": {
    "name": { "label": "Name" },
    "max_hp": { "label": "Maximum HP", "defaultValue": 10 }
  },
  "actions": [
    { "id": "hurt", "name": "Take damage", "source": "rules/hurt.ts" },
    { "id": "heal", "name": "Heal", "source": "rules/heal.ts" }
  ],
  "frames": [
    { "id": "sheet", "source": "layouts/keeper.json", "default": true }
  ]
}
  1. Righe 2-5 Chi è il Framework: il suo id, il nome mostrato, il tipo di Elemento che costruisce e il file di regole che ne costruisce uno.
  2. Righe 6-9 Gli input che il giocatore compila. Ogni chiave corrisponde a un parametro del costruttore.
  3. Righe 10-13 Le azioni. Ognuna punta a un file di regole.
  4. Righe 14-16 I Frame che disegnano un Keeper. Ce n'è uno solo predefinito.

Passo 3: il costruttore

Il costruttore trasforma gli input nei valori iniziali. Il suo tipo di ritorno elenca ogni campo salvato. Un nuovo Keeper parte con i PF al massimo.

rules/keeper.ts
export function construct(
  name: string,
  max_hp: number,
): { name: string; max_hp: number; hp: number } {
  return { name: name, max_hp: max_hp, hp: max_hp };
}
  1. Righe 1-4 Gli input e i campi che il costruttore restituisce.
  2. Riga 5 I valori iniziali. Un nuovo Keeper parte con i PF al massimo.

Passo 4: le azioni

Un'azione riceve prima l'Elemento, poi i propri input. Modifica i valori solo tramite il registro. Subisci danno aggiunge una quantità negativa. Quando un giocatore usa il pulsante di un'azione con input, come amount, Fabled li chiede in una finestra di dialogo.

rules/hurt.ts
export function hurt(self: ElementRef, amount: number): void {
  ledger.add(self, "hp", 0 - amount);
}
  1. Riga 2 ledger.add cambia hp di una quantità. Aggiungere un numero negativo sottrae.

Cura legge due valori, poi imposta i nuovi PF. Non fa nulla se il Keeper ha già i PF al massimo, quindi in quel caso non scrive nulla.

rules/heal.ts
export function heal(self: ElementRef, amount: number): void {
  const hp: number = element.var(self, "hp") as number;
  const max_hp: number = element.var(self, "max_hp") as number;
  if (hp < max_hp) {
    ledger.set(self, "hp", math.min(max_hp, hp + amount));
  }
}
  1. Righe 2-3 Leggi i PF attuali e i PF massimi. element.var dà un valore che converti con as number.
  2. Riga 4 Non fare nulla se il Keeper ha già i PF al massimo, così in quel caso non scrivi niente.
  3. Riga 5 Imposta hp al più piccolo tra il massimo e il valore curato.

Passo 5: il Frame

Il Frame disegna la scheda. Il testo mostra i valori con collegamenti come {{element.attributes.name}}. Ogni pulsante indica un alias di azione dichiarato dal contratto dati.

layouts/keeper.json
{
  "version": "1.0",
  "metadata": {
    "name": "Keeper sheet",
    "bindingMode": "bound",
    "frameworkRef": "keeper",
    "context": "BLADE",
    "template": "standard-blade"
  },
  "dataContract": {
    "requiredBindings": [
      { "binding": "element.attributes.name", "expectedType": "string" },
      { "binding": "element.attributes.hp", "expectedType": "number" },
      { "binding": "element.attributes.max_hp", "expectedType": "number" }
    ],
    "optionalBindings": [],
    "actions": [
      { "name": "hurt", "functionRef": "hurt" },
      { "name": "heal", "functionRef": "heal" }
    ],
    "regions": []
  },
  "layout": {
    "kind": "container",
    "children": [
      { "kind": "text", "text": "{{element.attributes.name}}" },
      {
        "kind": "text",
        "text": "HP: {{element.attributes.hp}} / {{element.attributes.max_hp}}"
      },
      { "kind": "button", "label": "Take damage", "actionRef": "hurt" },
      { "kind": "button", "label": "Heal", "actionRef": "heal" }
    ]
  }
}
  1. Righe 3-9 metadata dà un nome al Frame e dice a quale Framework appartiene.
  2. Righe 10-22 dataContract elenca i campi che il Frame legge e le azioni che i suoi pulsanti possono chiamare.
  3. Righe 23-34 layout è l'albero che viene disegnato. Il testo mostra i campi con le doppie parentesi graffe e i pulsanti indicano un alias di azione.

Passo 6: gli scenari

Uno scenario costruisce un Keeper e controlla i suoi valori iniziali.

scenarios/new-keeper.json
{
  "id": "new-keeper",
  "framework": "keeper",
  "parameters": { "name": "Mira", "max_hp": 10 },
  "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10 }
}
  1. Riga 4 Gli input con cui costruire il Keeper.
  2. Riga 5 Ogni valore che il costruttore deve restituire.

Questo scenario subisce danno, cura oltre il massimo e cura di nuovo a PF pieni. Ogni passo elenca le scritture esatte che l'azione deve fare. L'ultimo passo non ne elenca nessuna.

scenarios/heal.json
{
  "id": "heal-after-damage",
  "framework": "keeper",
  "parameters": { "name": "Mira", "max_hp": 10 },
  "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10 },
  "actions": [
    {
      "action": "hurt",
      "parameters": { "amount": 6 },
      "expectedWrites": [{ "op": "ledger.add", "key": "hp", "value": -6 }]
    },
    {
      "action": "heal",
      "parameters": { "amount": 9 },
      "expectedWrites": [{ "op": "ledger.set", "key": "hp", "value": 10 }]
    },
    { "action": "heal", "parameters": { "amount": 3 }, "expectedWrites": [] }
  ]
}
  1. Righe 7-11 Passo uno: subisci 6 danni. L'azione deve fare esattamente una scrittura, un ledger.add di -6.
  2. Righe 12-16 Passo due: cura 9. Il risultato è limitato al massimo, quindi hp viene impostato a 10.
  3. Riga 17 Passo tre: cura ancora a PF pieni. La guardia salta la scrittura, quindi non ci si aspetta nessuna scrittura.

Portalo in The Forge

  • Apri il tuo gioco in The Forge.
  • Nel passaggio «Gameplay», scegli «Avanzate: sorgente e versioni» per aprire l'editor del sorgente.
  • Se il gioco non ha ancora un progetto, scegli «Scriverlo da solo» per partire da esempi modificabili. Non scegliere «Inizia a creare il mio gioco», che è il percorso con l'IA. Sostituisci i file di esempio con i file qui sopra: usa «Aggiungi file» per i nuovi percorsi e «Rimuovi file» per i file che non ti servono. Conserva il projectId che l'editor ha inserito nel manifest.
  • Scegli Verifica e anteprima. Correggi ogni problema elencato, poi verifica di nuovo.
  • Scegli Salva progetto.
  • Scegli Prepara il mio gioco. Questo crea Framework, Frame e contenuti privati dalla tua versione salvata.
  • Scegli Crea un personaggio in questo gioco e prova Subisci danno e Cura sulla scheda.

Rompilo di proposito

Cambia il campo che Cura scrive da hp a mana. Il compilatore si ferma e indica il problema.

export function heal(self: ElementRef, amount: number): void {
  const hp: number = element.var(self, "hp") as number;
  const max_hp: number = element.var(self, "max_hp") as number;
  if (hp < max_hp) {
    ledger.set(self, "mana", math.min(max_hp, hp + amount));
  }
}

La verifica segnala:

Action "keeper.heal" calls ledger.set(self, "mana", ...), and "mana" is not declared on this schema. Declared scalar fields: hp, max_hp, name.

Rimetti il nome hp e la verifica passa. Le regole possono scrivere solo i campi restituiti dal costruttore.

Avanti