Salta al contenuto principale

Framework in codice

Schemi dei Framework

Dichiara input, azioni, valori derivati, Frame, contenuti e Framework figli per un tipo di Elemento.

Un file Framework

Ogni file in schemas/ dichiara un Framework. Le pagine di riferimento fanno crescere il Keeper dell'avvio rapido. Ora ha una scelta Calling, un valore derivato Guard, un preset già pronto, una sezione e un Lamp figlio.

manifest/project.json
schemas/keeper.json
schemas/lamp.json
rules/keeper.ts
rules/lamp.ts
rules/hurt.ts
rules/heal.ts
rules/guard.ts
layouts/keeper.json
layouts/lamp.json
scenarios/new-keeper.json
scenarios/heal.json
scenarios/new-lamp.json

Campi principali

CampoObbligatorioSignificato
idSìL'id univoco del Framework. Lo usano il manifest e gli scenari.
nameSìIl nome mostrato.
elementTypeSìCosa costruisce: character per i personaggi giocabili, item per gli oggetti, spell per gli incantesimi. Sono ammessi tipi personalizzati.
constructorSìIl percorso del file di regole che costruisce un nuovo Elemento.
inputsNoCosa inserisce il giocatore quando crea un Elemento. Ogni chiave corrisponde a un parametro del constructor.
actionsNoCose che un Elemento può fare. Ognuna ha un id, un nome e un file di regole.
ledgerFunctionsNoValori derivati. Ognuno punta a un campo memorizzato e ha un file di regole.
framesSìDa uno a sedici Frame. Esattamente uno ha default impostato su true.
sectionsNoRaggruppa gli input in passaggi della procedura guidata di creazione.
contentNoPreset già pronti. Ognuno ha un id, un nome e dei valori di input.
childFrameworksNoCollega il nome di uno slot figlio a un altro Framework dello stesso gioco.

In un GAME, il Framework di ingresso nel manifest deve essere un personaggio giocabile.

The entry framework must be a character constructor.

Input

Un input personalizza un parametro del constructor e la sua chiave deve corrispondere al nome del parametro. La maggior parte degli input ha bisogno solo di una label. Le altre chiavi restringono ciò che un giocatore può inserire.

schemas/keeper.json
  "inputs": {
    "name": { "label": "Name" },
    "max_hp": {
      "label": "Maximum HP",
      "description": "How much damage a Keeper can take.",
      "defaultValue": 10,
      "min": 1,
      "max": 40
    },
    "calling": {
      "label": "Calling",
      "options": ["Guard", "Scout"],
      "defaultValue": "Guard"
    },
    "lamp": { "label": "Lamp", "defaultValue": "lamp", "hidden": true }
  },
  1. Righe 8-14 min e max limitano un numero. Uno scenario o un preset fuori dai limiti fallisce.
  2. Righe 15-19 options dà un elenco fisso di scelte. Il valore predefinito deve essere una di esse.
  3. Riga 20 Un input hidden contiene un valore che il giocatore non vede mai. Qui collega la Lamp figlia.
schemas/keeper.json
schemas/keeper.json
{
  "id": "keeper",
  "name": "Keeper",
  "elementType": "character",
  "constructor": "rules/keeper.ts",
  "inputs": {
    "name": { "label": "Name" },
    "max_hp": {
      "label": "Maximum HP",
      "description": "How much damage a Keeper can take.",
      "defaultValue": 10,
      "min": 1,
      "max": 40
    },
    "calling": {
      "label": "Calling",
      "options": ["Guard", "Scout"],
      "defaultValue": "Guard"
    },
    "lamp": { "label": "Lamp", "defaultValue": "lamp", "hidden": true }
  },
  "childFrameworks": { "lamp": "lamp" },
  "sections": [
    {
      "id": "basics",
      "title": "Basics",
      "inputs": ["name", "max_hp", "calling", "lamp"]
    }
  ],
  "actions": [
    { "id": "hurt", "name": "Take damage", "source": "rules/hurt.ts" },
    { "id": "heal", "name": "Heal", "source": "rules/heal.ts" }
  ],
  "ledgerFunctions": [
    {
      "id": "guard",
      "name": "Guard",
      "field": "guard",
      "source": "rules/guard.ts"
    }
  ],
  "frames": [
    { "id": "sheet", "source": "layouts/keeper.json", "default": true }
  ],
  "content": [
    {
      "id": "mira-scout",
      "name": "Mira the Scout",
      "parameters": {
        "name": "Mira",
        "max_hp": 8,
        "calling": "Scout",
        "lamp": "lamp"
      }
    }
  ]
}
ChiaveSignificato
labelIl testo mostrato al giocatore.
descriptionTesto di aiuto, fino a 1024 caratteri.
defaultValueIl valore usato quando il giocatore non tocca l'input.
optionsUn elenco fisso da 2 a 128 scelte di testo, ognuna fino a 128 caratteri. Il valore predefinito deve essere una di esse.
optionLabelsEtichette mostrate al giocatore per le opzioni. Il valore memorizzato resta l'opzione.
min, maxLimiti per un numero. min non deve superare max.
minLength, maxLengthLimiti per la lunghezza di un testo.
requiredImposta su false per lasciare che il giocatore lasci l'input vuoto.
hiddenTiene l'input fuori dalla procedura guidata. Un input nascosto ha bisogno di un defaultValue.
visibleWhenMostra l'input solo finché vale una condizione su altri input.

Un valore predefinito che non è tra le opzioni viene rifiutato.

{
  "calling": {
    "label": "Calling",
    "options": ["Guard", "Scout"],
    "defaultValue": "Mage"
  }
}
Default for calling must be one of its options ("Guard", "Scout").

Anche scenari e preset devono rispettare i limiti.

Parameter max_hp 99 is not between 1 and 40.

Un input nascosto senza valore predefinito fallisce con:

Input name is hidden, so it must declare a defaultValue.

Azioni e funzioni del registro

Ogni azione ha un id, un nome e un file sorgente. Ogni funzione del registro ha gli stessi, più un campo. Il campo è il valore memorizzato che la funzione ricava e deve essere un campo scalare restituito dal constructor. Una funzione del registro legge i valori e restituisce quello derivato. Non scrive mai.

schemas/keeper.json
  "ledgerFunctions": [
    {
      "id": "guard",
      "name": "Guard",
      "field": "guard",
      "source": "rules/guard.ts"
    }
  ],
  1. Righe 36-39 id e name identificano la funzione, field è il valore salvato che ricava e source è il suo file di regole.
rules/guard.ts
function base_guard(calling: string): number {
  return calling === "Guard" ? 3 : 1;
}

export function guard(currentValue: number, self: ElementRef): number {
  const calling: string = element.var(self, "calling") as string;
  return base_guard(calling);
}
  1. Righe 1-3 Una funzione di supporto. Non ha export e la funzione del registro la chiama per nome.
  2. Riga 5 Una funzione del registro riceve il suo valore attuale e l'Elemento.
  3. Riga 6 Leggi i valori salvati con element.var.
  4. Riga 7 Restituisci il valore derivato.
Ledger function must target an existing scalar field.

Gli id devono essere unici all'interno di un Framework. Azioni e funzioni del registro condividono un unico insieme di id.

Duplicate semantic identity or source path.

Due funzioni del registro possono condividere un file sorgente. Un'azione e una funzione del registro no:

action source does not match its owner contract

Input delle azioni

Gli input di un'azione sono i parametri della sua funzione. La firma li dichiara e lo schema può aggiungere una voce per ciascuno. Ogni input è un numero, un testo o un valore vero/falso.

Al giocatore viene chiesto ogni input in una finestra quando usa il pulsante. La finestra indica un input con il nome del suo parametro, a meno che la voce non gli dia un'etichetta. Una voce può anche dare una descrizione e un valore predefinito. Un input numerico può avere un minimo e un massimo. Un input di testo può avere un elenco di opzioni o opzioni da una tabella. La finestra mostra tutto, e il server rifiuta un valore fuori dai limiti o dalle opzioni. Un pulsante non può fornire un valore. Dai a ogni parametro un nome che il giocatore possa leggere: roll, non r.

schemas/keeper.json
    {
      "id": "check_lore",
      "name": "Lore check",
      "source": "rules/check-lore.ts",
      "inputs": { "roll": { "label": "Roll", "min": 1, "max": 100 } }
    }
  1. Riga 24 La voce dell'input del tiro. La finestra mostra l'etichetta e il server applica minimo e massimo.

Un minimo e un massimo non impediscono un numero con decimali. Se la regola richiede un numero intero, lo controlla da sé, e un passo dello scenario lo dimostra. Un passo dello scenario fuori dai limiti viene rifiutato.

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. Riga 1 Il tiro è un input che il giocatore scrive.
  2. Riga 2 Ignora un numero con decimali. I limiti dell'input lo tengono già tra 1 e 100.
  3. Righe 4-5 Scrivi il bersaglio e il tiro.

Un input di testo può invece offrire un elenco di scelte da una tabella del regolamento. La pagina Tabelle del regolamento mostra come.

Un input di testo può invece scegliere per nome uno dei campi memorizzati del Framework, con un elenco fields. La pagina Scrivere le regole mostra un'azione che lo usa.

Un input numerico può chiedere al server di tirarlo. Aggiungi una chiave roll con un'espressione di dadi, come "roll": "1d100" o "roll": "2d6+1". La finestra mostra allora un pulsante Tira al posto di un campo da digitare. Il server tira e la finestra mostra il totale, che il giocatore non può cambiare. Solo un parametro numerico può essere tirato. Il compilatore rifiuta un roll insieme a un defaultValue e rifiuta un'espressione di dadi che il server non accetta.

Un input tirato si spende una sola volta: l'azione prende il tiro quando viene eseguita, e un nuovo tiro annulla i precedenti non spesi di quell'input. Un tiro è legato alla sua campagna e scade dopo 15 minuti. La pagina Layout e schede mostra cosa vede il tavolo.

Riga di registro di un'azione

Un'azione può pubblicare una riga di risultato nel registro del tavolo. Aggiungi un modello di registro alla voce dell'azione nello schema, per esempio "log": "{name} takes {amount} damage, defense {defense}". Il server riempie ogni segnaposto {name} come testo semplice dopo il salvataggio dell'azione. Un browser non invia mai la riga.

Un segnaposto può indicare un input dell'azione, o un campo numerico, di testo o vero/falso dello stesso Framework. Il compilatore rifiuta un input nascosto, un campo di salute (hp, current_hp, hit_points, temp_hp, current_health, max_health, hitPoints, e i campi di salute e di condizione del blocco di combattimento), un nome che non esiste, un modello di più di 200 caratteri, più di 16 segnaposto, una graffa isolata e un carattere di controllo. Quando un input e un campo hanno lo stesso nome, vince l'input. I valori sono quelli dopo l'azione, quindi un campo scritto dall'azione mostra il suo nuovo valore. Un campo calcolato non viene ricalcolato, quindi mostra il valore che aveva prima dell'azione.

Un giocatore che agisce sul proprio personaggio vede la riga allo stesso pubblico dei suoi tiri di dado: tutti, solo lui, oppure lui e i GM. In ogni altro caso, compreso un GM che agisce su qualsiasi personaggio, la riga arriva solo ai membri della campagna che possono leggere quel personaggio. Se il pubblico è tutti ma un membro non può leggere il personaggio, la riga va come messaggio privato ai membri che possono. Il tavolo mostra la riga con l'etichetta Gioco, così nessuno la scambia per il messaggio di un giocatore.

Preset di contenuto

Un preset è un insieme di valori di input con un nome, come un personaggio già pronto. Fabled controlla ogni preset come uno scenario, quindi ogni valore deve rispettare il suo input. Un gioco può contenere fino a 512 preset per Framework.

schemas/keeper.json
  "content": [
    {
      "id": "mira-scout",
      "name": "Mira the Scout",
      "parameters": {
        "name": "Mira",
        "max_hp": 8,
        "calling": "Scout",
        "lamp": "lamp"
      }
    }
  ]
  1. Righe 47-48 Un preset ha un id e un name.
  2. Righe 49-54 I valori di input che il preset compila. Devono rispettare i limiti degli input.

Sezioni

Le sezioni raggruppano gli input in passaggi della procedura guidata di creazione. Quando un Framework dichiara sezioni, ogni input deve essere elencato in una di esse, compresi gli input nascosti.

schemas/keeper.json
  "sections": [
    {
      "id": "basics",
      "title": "Basics",
      "inputs": ["name", "max_hp", "calling", "lamp"]
    }
  ],
  1. Righe 25-27 Una sezione ha un id, un title e gli input che mostra. Ogni input deve stare in una sezione.
Input lamp is not assigned to any section.

Framework figli

Un Framework figlio è una parte di un Elemento più grande, come il Lamp che porta un Keeper. Il figlio ha il proprio schema, constructor, Frame e scenario.

schemas/keeper.json
  "childFrameworks": { "lamp": "lamp" },
  1. Riga 22 Collega lo slot figlio lamp al Framework con l'id lamp.
schemas/lamp.json
{
  "id": "lamp",
  "name": "Lamp",
  "elementType": "item",
  "constructor": "rules/lamp.ts",
  "inputs": {
    "name": { "label": "Name" },
    "oil": { "label": "Oil", "defaultValue": 5 }
  },
  "frames": [{ "id": "card", "source": "layouts/lamp.json", "default": true }]
}
  1. Righe 6-9 Gli input del figlio. Anche un figlio ha bisogno di un name.
  2. Riga 10 Il figlio ha un suo Frame.

Il genitore collega il nome di uno slot a un id di Framework in childFrameworks. Un input nascosto contiene il riferimento. Il constructor restituisce gli input del figlio accanto ai propri valori.

rules/keeper.ts
export function construct(
  name: string,
  max_hp: number,
  calling: string,
  lamp: FrameworkRef,
): {
  values: { name: string; max_hp: number; hp: number; calling: string; guard: number };
  children: { lamp: { framework: FrameworkRef; params: { name: string; oil: number } } };
} {
  return {
    values: { name: name, max_hp: max_hp, hp: max_hp, calling: calling, guard: 0 },
    children: { lamp: { framework: lamp, params: { name: "Lantern", oil: 5 } } },
  };
}
  1. Riga 5 lamp è un FrameworkRef. La piattaforma trasforma in uno di questi l'id del Framework preso dall'input.
  2. Righe 6-9 Il tipo di ritorno elenca i valori salvati e gli slot figlio.
  3. Riga 11 I valori iniziali del Keeper.
  4. Riga 12 Lo slot figlio: quale Framework costruire e gli input da dargli.

Ogni Framework, figli compresi, deve restituire un nome.

must define the required 'name' value, normally as a string constructor field.

Uno slot che punta a un Framework fuori dal gioco fallisce con:

Invalid child Framework constraint: lamp.

Campi avanzati

CampoScopo
listsContenuti che un personaggio ottiene durante il gioco.
contentChoicesPunti di scelta in cui il giocatore sceglie una voce di un Framework di contenuti.
slotsSelettori per scelte ripetibili.
poolsRiserve condivise da cui attingono gli input numerici.
modifiersNumeri che la piattaforma ricava dagli effetti dei contenuti.
effectsCome un Framework di contenuti indica un modificatore e una quantità.
levellingFar salire o scendere un personaggio di un livello dopo la creazione.
rebuildCosa fa una ricostruzione allo stato di gioco, come un cambio di livello.
combatI ruoli che gli strumenti di combattimento del tavolo leggono.
catalogueRecord di contenuti tenuti fuori dal sorgente e aggiunti a blocchi.
tablesTabelle di dati condivise.
playReadsValori derivati che il tavolo legge dagli attributi salvati.
rulesAttacksAttacchi che ogni personaggio possiede.
optionsFromPrende le opzioni di un input da un'altra fonte.
rich, richTextSegna un input di testo come testo formattato.

Avanti