Code-Frameworks
Framework-Schemas
Deklariere Eingaben, Aktionen, abgeleitete Werte, Frames, Inhalte und untergeordnete Frameworks für eine Art von Element.
Eine Framework-Datei
Jede Datei in schemas/ deklariert ein Framework. Die Referenzseiten bauen den Keeper aus dem Schnellstart weiter aus. Er hat jetzt eine Calling-Auswahl, einen abgeleiteten Guard-Wert, ein fertiges Preset, einen Abschnitt und eine untergeordnete Lamp.
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.jsonKernfelder
| Feld | Pflicht | Bedeutung |
|---|---|---|
| id | Ja | Die eindeutige ID des Frameworks. Das Manifest und die Szenarien nutzen sie. |
| name | Ja | Der Anzeigename. |
| elementType | Ja | Was es erstellt: character für spielbare Charaktere, item für Objekte, spell für Zauber. Eigene Typen sind erlaubt. |
| constructor | Ja | Der Pfad der Regeldatei, die ein neues Element erstellt. |
| inputs | Nein | Was der Spieler beim Erstellen eines Elements eingibt. Jeder Schlüssel passt zu einem Konstruktor-Parameter. |
| actions | Nein | Dinge, die ein Element tun kann. Jede hat eine ID, einen Namen und eine Regeldatei. |
| ledgerFunctions | Nein | Abgeleitete Werte. Jeder zielt auf ein gespeichertes Feld und hat eine Regeldatei. |
| frames | Ja | Ein bis sechzehn Frames. Genau einer hat default auf true. |
| sections | Nein | Gruppiert Eingaben zu Schritten des Erstellungsassistenten. |
| content | Nein | Fertige Presets. Jedes hat eine ID, einen Namen und Eingabewerte. |
| childFrameworks | Nein | Ordnet einem Namen für einen untergeordneten Platz ein anderes Framework desselben Spiels zu. |
In einem GAME muss das Einstiegs-Framework im Manifest ein spielbarer Charakter sein.
The entry framework must be a character constructor.Eingaben
Eine Eingabe passt einen Konstruktor-Parameter an, und ihr Schlüssel muss zum Namen des Parameters passen. Die meisten Eingaben brauchen nur ein label. Die anderen Schlüssel schränken ein, was ein Spieler eingeben kann.
"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 }
},- Zeilen 8-14 min und max begrenzen eine Zahl. Ein Szenario oder Preset außerhalb der Grenzen schlägt fehl.
- Zeilen 15-19 options gibt eine feste Liste von Auswahlmöglichkeiten vor. Der Standardwert muss eine davon sein.
- Zeile 20 Eine hidden-Eingabe hält einen Wert, den der Spieler nie sieht. Hier verbindet sie die untergeordnete Lamp.
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"
}
}
]
}| Schlüssel | Bedeutung |
|---|---|
| label | Der Text, der dem Spieler angezeigt wird. |
| description | Hilfetext, bis zu 1024 Zeichen. |
| defaultValue | Der Wert, der gilt, wenn der Spieler die Eingabe nicht anfasst. |
| options | Eine feste Liste von 2 bis 128 Textoptionen, jede bis zu 128 Zeichen lang. Der Standardwert muss eine davon sein. |
| optionLabels | Beschriftungen der Optionen für den Spieler. Gespeichert wird weiterhin die Option. |
| min, max | Grenzen für eine Zahl. min darf max nicht übersteigen. |
| minLength, maxLength | Grenzen für die Länge eines Textes. |
| required | Setze es auf false, damit der Spieler die Eingabe leer lassen darf. |
| hidden | Hält die Eingabe aus dem Assistenten heraus. Eine versteckte Eingabe braucht einen defaultValue. |
| visibleWhen | Zeigt die Eingabe nur, solange eine Bedingung für andere Eingaben gilt. |
Ein Standardwert, der keine der Optionen ist, wird abgelehnt.
{
"calling": {
"label": "Calling",
"options": ["Guard", "Scout"],
"defaultValue": "Mage"
}
}Default for calling must be one of its options ("Guard", "Scout").Auch Szenarien und Presets müssen in die Grenzen passen.
Parameter max_hp 99 is not between 1 and 40.Eine versteckte Eingabe ohne Standardwert schlägt fehl mit:
Input name is hidden, so it must declare a defaultValue.Aktionen und Ledger-Funktionen
Jede Aktion hat eine ID, einen Namen und eine Quelldatei. Jede Ledger-Funktion hat dasselbe, dazu ein field. Das Feld ist der gespeicherte Wert, den die Funktion ableitet, und es muss ein skalares Feld sein, das der Konstruktor zurückgibt. Eine Ledger-Funktion liest Werte und gibt den abgeleiteten zurück. Sie schreibt nie.
"ledgerFunctions": [
{
"id": "guard",
"name": "Guard",
"field": "guard",
"source": "rules/guard.ts"
}
],- Zeilen 36-39 id und name bezeichnen die Funktion, field ist der gespeicherte Wert, den sie ableitet, und source ist ihre Regeldatei.
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);
}- Zeilen 1-3 Eine Hilfsfunktion. Sie hat kein export, und die Ledger-Funktion ruft sie beim Namen auf.
- Zeile 5 Eine Ledger-Funktion erhält ihren aktuellen Wert und das Element.
- Zeile 6 Lies gespeicherte Werte mit element.var.
- Zeile 7 Gib den abgeleiteten Wert zurück.
Ledger function must target an existing scalar field.IDs müssen innerhalb eines Frameworks eindeutig sein. Aktionen und Ledger-Funktionen teilen sich einen Satz von IDs.
Duplicate semantic identity or source path.Zwei Ledger-Funktionen dürfen eine Quelldatei teilen. Eine Aktion und eine Ledger-Funktion dürfen das nicht:
action source does not match its owner contractAktions-Eingaben
Die Eingaben einer Aktion sind die Parameter ihrer Funktion. Die Signatur deklariert sie, und das Schema kann für jede einen Eintrag ergänzen. Jede Eingabe ist eine Zahl, ein Text oder ein Wahr/Falsch-Wert.
Der Spieler wird in einem Dialog nach jeder Eingabe gefragt, wenn er die Schaltfläche benutzt. Der Dialog nennt eine Eingabe nach ihrem Parameternamen, sofern der Eintrag kein Label vergibt. Ein Eintrag kann außerdem eine Beschreibung und einen Standardwert festlegen. Eine Zahleneingabe kann ein Minimum und ein Maximum haben. Eine Texteingabe kann eine Liste von Optionen oder Optionen aus einer Tabelle haben. Der Dialog zeigt alles an, und der Server lehnt einen Wert außerhalb der Grenzen oder Optionen ab. Eine Schaltfläche kann keinen Wert übergeben. Benenne jeden Parameter so, dass ein Spieler ihn lesen kann: roll statt r.
{
"id": "check_lore",
"name": "Lore check",
"source": "rules/check-lore.ts",
"inputs": { "roll": { "label": "Roll", "min": 1, "max": 100 } }
}- Zeile 24 Der Eintrag für die Wurf-Eingabe. Der Dialog zeigt das Label, und der Server erzwingt Minimum und Maximum.
Minimum und Maximum verhindern keine Zahl mit Dezimalstellen. Braucht die Regel eine ganze Zahl, prüft sie das selbst, und ein Szenarioschritt belegt es. Ein Szenarioschritt außerhalb der Grenzen wird abgelehnt.
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);
}
}- Zeile 1 Der Wurf ist eine Eingabe, die der Spieler tippt.
- Zeile 2 Ignoriere eine Zahl mit Dezimalstellen. Die Grenzen der Eingabe halten sie bereits zwischen 1 und 100.
- Zeilen 4-5 Schreibe das Ziel und den Wurf.
Eine Text-Eingabe kann stattdessen eine Auswahl aus einer Regelwerk-Tabelle bieten. Die Seite Regelwerk-Tabellen zeigt, wie.
Eine Texteingabe kann stattdessen mit einer fields-Liste eines der gespeicherten Felder des Frameworks nach Namen wählen. Die Seite Regeln schreiben zeigt eine Aktion, die das nutzt.
Eine Zahleneingabe kann den Server bitten, sie zu würfeln. Füge einen Schlüssel roll mit einem Würfelausdruck hinzu, etwa "roll": "1d100" oder "roll": "2d6+1". Der Dialog zeigt dann eine Schaltfläche Würfeln statt eines Feldes zum Tippen. Der Server würfelt, und der Dialog zeigt die Summe, die der Spieler nicht ändern kann. Nur ein Zahlenparameter lässt sich würfeln. Der Compiler lehnt roll zusammen mit defaultValue ab und lehnt einen Würfelausdruck ab, den der Server nicht akzeptiert.
Eine gewürfelte Eingabe wird nur einmal verbraucht: Die Aktion nimmt den Wurf beim Ausführen, und ein neuer Wurf macht frühere unverbrauchte Würfe dieser Eingabe ungültig. Ein Wurf ist an seine Kampagne gebunden und läuft nach 15 Minuten ab. Die Seite Layouts und Bögen zeigt, was der Tisch sieht.
Protokollzeile einer Aktion
Eine Aktion kann eine Ergebniszeile im Tischprotokoll veröffentlichen. Füge dem Eintrag der Aktion im Schema eine Protokollvorlage hinzu, zum Beispiel "log": "{name} takes {amount} damage, defense {defense}". Der Server füllt jeden Platzhalter {name} als reinen Text, nachdem die Aktion gespeichert wurde. Ein Browser sendet die Zeile nie.
Ein Platzhalter darf eine Eingabe der Aktion oder ein Zahlen-, Text- oder Wahr/Falsch-Feld desselben Frameworks benennen. Der Compiler lehnt eine versteckte Eingabe ab, ein Gesundheitsfeld (hp, current_hp, hit_points, temp_hp, current_health, max_health, hitPoints sowie die Gesundheits- und Zustandsfelder des Kampfblocks), einen Namen, der nicht existiert, eine Vorlage mit mehr als 200 Zeichen, mehr als 16 Platzhalter, eine einzelne Klammer und ein Steuerzeichen. Teilen eine Eingabe und ein Feld einen Namen, gewinnt die Eingabe. Die Werte stammen von nach der Aktion, ein von der Aktion geschriebenes Feld zeigt also seinen neuen Wert. Ein berechnetes Feld wird nicht neu berechnet und zeigt den Wert von vor der Aktion.
Wer mit der eigenen Figur handelt, sieht die Zeile im selben Publikum wie die eigenen Würfelwürfe: alle, nur er oder sie selbst, oder er oder sie und die SL. In jedem anderen Fall, auch wenn die SL mit einer beliebigen Figur handelt, erreicht die Zeile nur die Kampagnenmitglieder, die diese Figur lesen dürfen. Ist das Publikum „alle“, aber ein Mitglied darf die Figur nicht lesen, geht die Zeile als private Nachricht an die Mitglieder, die es dürfen. Der Tisch zeigt die Zeile unter dem Label Spiel, damit sie niemand für die Nachricht einer Spielerin oder eines Spielers hält.
Inhalts-Presets
Ein Preset ist ein benannter Satz von Eingabewerten, zum Beispiel ein fertiger Charakter. Fabled prüft jedes Preset wie ein Szenario, daher muss jeder Wert zu seiner Eingabe passen. Ein Spiel kann bis zu 512 Presets pro Framework enthalten.
"content": [
{
"id": "mira-scout",
"name": "Mira the Scout",
"parameters": {
"name": "Mira",
"max_hp": 8,
"calling": "Scout",
"lamp": "lamp"
}
}
]- Zeilen 47-48 Ein Preset hat eine id und einen name.
- Zeilen 49-54 Die Eingabewerte, die das Preset ausfüllt. Sie müssen in die Grenzen der Eingaben passen.
Abschnitte
Abschnitte gruppieren Eingaben zu Schritten des Erstellungsassistenten. Deklariert ein Framework Abschnitte, muss jede Eingabe in einem davon stehen, versteckte Eingaben eingeschlossen.
"sections": [
{
"id": "basics",
"title": "Basics",
"inputs": ["name", "max_hp", "calling", "lamp"]
}
],- Zeilen 25-27 Ein Abschnitt hat eine id, einen title und die Eingaben, die er zeigt. Jede Eingabe muss in einem Abschnitt stehen.
Input lamp is not assigned to any section.Untergeordnete Frameworks
Ein untergeordnetes Framework ist ein Teil eines größeren Elements, zum Beispiel die Lamp, die ein Keeper trägt. Das untergeordnete Framework hat ein eigenes Schema, einen eigenen Konstruktor, einen eigenen Frame und ein eigenes Szenario.
"childFrameworks": { "lamp": "lamp" },- Zeile 22 Ordnet dem untergeordneten Platz lamp das Framework mit der id lamp zu.
{
"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 }]
}- Zeilen 6-9 Die Eingaben des untergeordneten Elements. Auch ein untergeordnetes Element braucht einen name.
- Zeile 10 Das untergeordnete Element hat seinen eigenen Frame.
Das übergeordnete Framework ordnet in childFrameworks einem Platznamen eine Framework-ID zu. Eine versteckte Eingabe hält den Verweis. Der Konstruktor gibt die Eingaben des untergeordneten Frameworks neben seinen eigenen Werten zurück.
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 } } },
};
}- Zeile 5 lamp ist eine FrameworkRef. Die Plattform macht aus der Framework-id in der Eingabe eine solche.
- Zeilen 6-9 Der Rückgabetyp listet die gespeicherten Werte und die untergeordneten Plätze auf.
- Zeile 11 Die Startwerte des Keeper.
- Zeile 12 Der untergeordnete Platz: welches Framework gebaut wird und welche Eingaben es bekommt.
Jedes Framework, auch untergeordnete, muss einen name zurückgeben.
must define the required 'name' value, normally as a string constructor field.Ein Platz, der auf ein Framework außerhalb des Spiels zeigt, schlägt fehl mit:
Invalid child Framework constraint: lamp.Fortgeschrittene Felder
| Feld | Zweck |
|---|---|
| lists | Inhalte, die ein Charakter im Spiel erhält. |
| contentChoices | Auswahlpunkte, an denen der Spieler einen Eintrag eines Inhalts-Frameworks wählt. |
| slots | Auswahlfelder für wiederholbare Entscheidungen. |
| pools | Gemeinsame Pools, aus denen Zahlen-Eingaben schöpfen. |
| modifiers | Zahlen, die die Plattform aus den Effekten von Inhalten ableitet. |
| effects | Wie ein Inhalts-Framework einen Modifikator und einen Betrag nennt. |
| levelling | Einen Charakter nach der Erstellung eine Stufe hoch- oder runterstufen. |
| rebuild | Was ein Umbau mit dem Spielzustand macht, zum Beispiel bei einem Stufenwechsel. |
| combat | Die Rollen, die die Kampfwerkzeuge des Tabletops lesen. |
| catalogue | Inhaltsdatensätze, die außerhalb der Quelle liegen und in Teilen hinzugefügt werden. |
| tables | Gemeinsame Datentabellen. |
| playReads | Abgeleitete Werte, die das Tabletop aus gespeicherten Attributen liest. |
| rulesAttacks | Angriffe, die jeder Charakter hat. |
| optionsFrom | Nimmt die Optionen einer Eingabe aus einer anderen Quelle. |
| rich, richText | Markiert eine Text-Eingabe als Rich-Text. |

