# Die Daten

Alles, was in den Seiten steht, steht in Datendateien daneben. Die HTML-Seite
ist nur die Anzeige: sie liest die Datei beim Öffnen und baut daraus die Seite.
Ändern, speichern, neu laden — kein Neubau nötig.

Jede Datendatei ist eine Zeile JavaScript um reines JSON herum:

```
window.KAPITEL =
{ … }
;
```

Wer die erste und die letzte Zeile abschneidet, hat eine gültige JSON-Datei.
Der Umweg ist nötig, weil ein Browser eine Seite von der Festplatte sonst
keine Datei nachladen lässt.

## Welche Datei was enthält

| Datei | Inhalt | Schema |
|---|---|---|
| `Kampagne/<Kapitel>.js` | ein Spielabend: Szenen, Schritte, alle Fragmente | `Regelwerk/kampagne.schema.json` |
| `Welt/<Kapitel>.js` | ein Weltkapitel — **dasselbe Format** | `Regelwerk/kampagne.schema.json` |
| `Regelwerk/verweise.js` | das Wörterbuch aller Querverweise | `Regelwerk/verweise.schema.json` |
| `Regelwerk/bestiarium.js` | alle Kreaturenbögen | — |
| `Regelwerk/personen.js` | alle Personen | — |
| `Regelwerk/daten.js` | Gegenstände, Preise, Händler, Kampfstile, **Rezepte** | — |

Kampagne und Welt benutzen dasselbe Format und denselben Renderer. Ein Kapitel
der Welt hat nur keine Zeitangaben: seine Szenen haben weder `nr` noch `dauer`.

## Die Gliederung eines Kapitels

```
kapitel        datei · eyebrow · titel · sub
  vorspann     id · bausteine[] · teile[]
  szenen[]     art "szene": id · titel · nr · dauer · marke · bausteine[] · teile[]
               art "teil":   fasst mehrere Szenen zusammen, hat zusätzlich szenen[]
    teile[]    die Schritte: id · titel · stufe · bausteine[] · teile[]
```

Jeder Knoten hat eine `id`. Das ist zugleich das Sprungziel auf der Seite
(`#<id>`), und alle Verweise zeigen darauf.

## Die Fragmente

In jedem `bausteine[]` steht eine Folge von Fragmenten, und jedes hat genau
einen `typ`. Die Häufigkeiten zählen **alle 29 Kapiteldateien** — Welt und Kampagne
zusammen:

| `typ` | Felder | Was es ist | Kommt vor |
|---|---|---|---|
| `absatz` | text | Ein Absatz Fließtext. | 1422× |
| `beat` | nr, titel, inhalt, marke | Ein Spielbeat mit eigenem Lesezeichen. | 122× |
| `bild` | stil, bilder, lz | `stil`: einzel · galerie · karte. `lz` ist die Beschriftung im Lesezeichenbaum. | 121× |
| `code` | text | Vorformatierter Text. | 7× |
| `gefahr` | inhalt, titel, marke | Roter Kasten: wo es kippen kann. | 22× |
| `gegner` | zeilen | Wer auftritt. `ziel` verweist ins Wörterbuch — Name und Gefahrstufe kommen von dort. | 15× |
| `liste` | punkte, geordnet | Aufzählung; `geordnet: true` nummeriert. | 129× |
| `merksatz` | inhalt, titel, marke | Violetter Kasten: Rastpunkt. | 61× |
| `nsc` | inhalt, titel, marke | Blauer Kasten: eine Person. | 28× |
| `probe` | fertigkeit, ergebnisse, spalte | Ein Wurf mit vier festen Ausgängen. | 5× |
| `regel` | inhalt, titel, marke | Grüner Kasten: Regel. | 119× |
| `sl` | inhalt, titel, marke | Ockerner Kasten: für die Spielleitung. | 76× |
| `tabelle` | kopf, zeilen, titel | Freie Tabelle. `kopf` ist die Kopfzeile. | 112× |
| `uebersicht` | was, spalten, zeilen | Die Schritte oder Szenen daneben — jede Zeile verweist auf ihren Eintrag. | 8× |
| `vorlesen` | inhalt, titel, marke | Vorlesetext (eingerückter Kasten). | 58× |
| `wahl` | frage, optionen, titel, einleitung, marke | Eine Verzweigung: eine Frage, mehrere Fälle. | 14× |
| `wuerfeltabelle` | wuerfel, zeilen, spalte | Zufallstabelle. | 1× |
| `zitat` | inhalt, titel, marke | Wie vorlesen, aber mit Überschrift. | 9× |

Verschachtelt wird nur über `inhalt` (in den Kästen und Beats) und über
`optionen[].inhalt` (in einer Wahl). Ein unbekannter `typ` wird nicht still
als Kasten gezeichnet, sondern fällt auf.

## Die Schreibweise im Text

Jedes Feld, das Text enthält, benutzt dieselbe kleine Schreibweise:

| Zeichen | Bedeutung |
|---|---|
| `**fett**` | fett |
| `*kursiv*` | kursiv |
| `` `code` `` | dicktengleich |
| `[Text](ziel)` | ein Link; `ziel` ist `#lesezeichen` oder ein Dateipfad |
| `▣` | feststehend — daran wird nicht mehr gerüttelt |
| `✎` | ergänzt, damit es aufgeht — änderbar |
| `▢` | noch offen |

Steht eine Marke im Feld `marke`, gilt sie dem ganzen Kasten. Steht sie
mitten im Text, meint sie nur diesen einen Satz.

## Das Wörterbuch

`Regelwerk/verweise.js` sagt, worauf verwiesen werden kann und wo es steht:

```json
{
  "ziele": {
    "kreatur/arakchor": {
      "art": "kreatur",
      "id": "arakchor",
      "titel": "Arakchor",
      "datei": "Regelwerk/Bestiarium-Mythras.html",
      "daten": "Regelwerk/bestiarium.js",
      "gefahr": "5"
    }
  },
  "namen": {
    "Arakchore": "kreatur/arakchor"
  }
}
```

* **`ziele`** — die verweisbaren Personen (16) und Kreaturen (32). `datei` ist
  die Seite, die sie anzeigt, `daten` die Datei, in der sie stehen.
* **`namen`** — 149 Schreibweisen, die im Fließtext **automatisch** verlinkt
  werden. Wer im Text „Arakchore" schreibt, bekommt den Link geschenkt.
* **`dateien`** — welche Seite welche Datendatei anzeigt.

**Gegenstände und Kampfstile stehen nicht im Wörterbuch, sondern nur in
`daten.js`.** Jede Seite liest `daten.js` mit und macht beim Laden aus jedem
Eintrag mit `id` selbst ein Ziel (`gegenstand/<id>` → *Was es gibt*,
`kampfstil/<id>` → *Kampfstile*) und aus seinem Namen eine Schreibweise. Ein
neuer Gegenstand oder Stil ist deshalb **eine Zeile in `daten.js`** und überall
verlinkt. In `verweise.js` stehen für sie nur **zusätzliche** Schreibweisen:
„Orkäxte" → `gegenstand/orkaxt`, „Breitschwert" → `gegenstand/kampfschwert`,
„Armbrustschützen" → `kampfstil/armbrustschuetze`.

**Jede Seite verlinkt jede Art** — `kapitel.html`, das Regelwerk und auch die
fest geschriebenen Seiten (*Fünf Schablonen*, *Traglast und Bruch*). Ausnahmen:

* Das Bestiarium setzt `window.OHNE = ["kreatur"]` und verlinkt keine Kreaturen
  — es ist selbst die Seite, auf die sie zeigen.
* Ein Eintrag verlinkt sich nie auf sich selbst: Die Zeile „Orkschwert" in
  *Was es gibt* und die Karte „Clankämpfer" unter *Kampfstile* bleiben ohne Link.
* Ein Kampfstil mit `"imText": false` wird im Fließtext nicht verlinkt, weil
  sein Name dort meist etwas anderes heißt („Clanführer", „Der Älteste").

Die Wortgrenze kennt Umlaute (`Erz` greift nicht in „Erzähler"), und
`Holzf&auml;lleraxt` im Quelltext ist dasselbe Wort wie „Holzfälleraxt".
**Bewusst ohne Schreibweise** sind „Bogen", „Axt", „Schild", „Pfeil" und
„Werkzeug" — sie meinen im Text meist etwas anderes.

Eine Adresse entsteht so:

```
WURZEL + ziele[x].datei + "#" + ziele[x].id
```

`WURZEL` ist der Weg von der eigenen Seite zum Adventure-Ordner — `"../"` für
alles in `Welt/`, `Kampagne/` und `Regelwerk/`. Eine Seite kann einzelne
Dateien über `URLKARTE` woanders hinschicken; die Artefaktfassungen tun das.

Im Feld `ziel` eines `gegner`-Eintrags steht genau so ein Schlüssel
(`"kreatur/arakchor"`). Deshalb steht die Gefahrstufe **nicht** in der Kampagne:
sie kommt aus dem Bestiarium und kann gar nicht auseinanderlaufen.


## Kampfstile

Drei Angaben, jede an genau einer Stelle:

| Was | Wo |
|---|---|
| welche Stile es gibt | `daten.js` → `kampfstile` |
| welche Waffe in welchem Stil steht, und was ohne ihn gilt | `daten.js` → bei der Waffe `"stile": [...]`, `"ohne": "gar nicht"` |
| wer einen Stil führt, mit welchem Wert | beim Kämpfer: `bestiarium.js` / `personen.js` → `"stil"` |

Eine Waffe kann in beliebig vielen Stilen stehen, ein Kämpfer beliebig viele
Stile führen — eine Zeile je Stil, jede mit eigenem Prozentwert:

```json
"stil": [["Armbrustschütze", "80 %", "Armbrust", "armbrustschuetze"],
         ["Formation und Schild", "55 %", "Kurzschwert, Dolch", "formation-und-schild"]]
```

Die vierte Stelle ist die id aus `kampfstile`; über sie verweist das Feld auf
seinen Eintrag, auch wenn der Stil beim Kämpfer anders heißt („Er hat nie
gekämpft"). Die Seite *Kampfstile*, die Spalte *Kampfstil* in *Was es gibt*
und die Liste „Wer ihn führt" rechnen sich daraus selbst aus.

**Einen neuen Stil anlegen:** eine Zeile unter `kampfstile`, die id bei den
Waffen unter `stile`, und beim Kämpfer eine Zeile unter `stil`. Sonst nichts.

## Prüfen

```
python3 sys/pruef_html.py
```

prüft tote Links, fehlende Bilder, doppelte Lesezeichen — und validiert jede
Kapiteldatei gegen `kampagne.schema.json`. Jedes andere Werkzeug kann dasselbe
tun: JSON herausschneiden und gegen das Schema validieren.
