docs: m3-befunde in tickets 01, 08 und 14 zurueckgetragen, dreizehnte registry ins glossar

This commit is contained in:
2026-08-12 17:08:27 +02:00
parent 217a7acbbc
commit b25dd1c49a
5 changed files with 97 additions and 3 deletions

View File

@@ -153,6 +153,32 @@ Fähigkeiten. Das Vokabular bleibt hier unverändert.
`damage` · `change_attribute` · `apply_modifier` · `remove_modifier` · `give_item` · `remove_item` ·
`start_quest` (08) · `flee` (14) · `run_script` (13) · `give_exp` (15) — **zehn**.
### Nachtrag aus M3: benannte Modifikatoren brauchen eine Registry
Oben steht `{ action = "apply_modifier", modifier = "core:poisoned", … }` — eine Aktion referenziert
einen Modifikator **per ID**. Aufgefallen beim Bauen von M3: **Keine der zwölf Registries definiert
benannte Modifikatoren.** `core:poisoned` hatte kein Zuhause.
Das ist die **dreizehnte Registry**, `[[modifiers]]`:
```toml
[[modifiers]]
id = "poisoned"
name = "Vergiftet"
symbol = "☠"
turns = 4
attributes = [ { attribute = "damage", percent = -15 } ]
triggers = [ { on = "turn_started",
actions = [ { action = "damage", amount = 2, damage_type = "poison", target = "self" } ] } ]
```
Das innere Feld heißt bewusst `attributes` und nicht `modifiers` — ein Modifikator, der Modifikatoren
enthält, liest sich wie ein Fehler. Und `effects` wäre der Begriff, den dieses Ticket ausdrücklich
verworfen hat.
Ein Modifikator ist damit ein **Bündel**: mehrere Umrechnungen plus Trigger plus Dauer. Gift senkt den
Schaden *und* tickt — das wäre mit einer einzelnen Umrechnung nicht ausdrückbar.
### Bewusst nicht entschieden
Das **Bedingungsvokabular** für `condition`. Es kommt aus Ticket 08, damit Quests und Modifikatoren

View File

@@ -71,6 +71,28 @@ Level ≥ 3" wird zu zwei Einträgen dupliziert — oder der Modder hängt beide
Bedingungen werden an **zwei** Stellen ausgewertet: beim Feuern eines Triggers und beim Auslesen eines
Attributs (bedingte Modifikatoren aus 01). Letzteres erzwingt den Nachtrag an Ticket 07, siehe unten.
### Nachträge aus M3
Beim Bauen kamen zwei Prüfungen dazu, die oben fehlten:
- **`health_below`** mit einem Anteil (0.0–1.0). Die `ai`-Listen aus 14 brauchen „unter 30 % Leben", und
das ist mit `attribute` nicht ausdrückbar: `health` ist **Zustand**, kein Attribut (01), und ein
*Verhältnis* lässt sich mit `at_least`/`below` auf einem absoluten Wert nicht formulieren.
- **`chance`** am Trigger. Dieses Ticket nennt „Flammendes Schwert: 20 % Chance, Brennen auszulösen" als
Motivbeispiel, hat aber kein Feld dafür festgelegt. Ohne es bräuchte jeder Wahrscheinlichkeitseffekt
ein Script — und Scripting liegt laut 13 hinter 1.0.
### Offene Lücke aus M3: ein Trigger sieht sein Event nicht
Die Events oben tragen Nutzlast (Schadenstyp, Prefab-ID, Menge) — **die Bedingungsgrammatik kann sie
nicht prüfen.** `Check` sieht nur Wesen, nie das Ereignis. Damit ist „Dornenpanzer reflektiert nur
Feuer" oder „nur bei kritischen Treffern" derzeit nicht formulierbar.
In M3 wurden `Event.damage_type` und `Event.label` deshalb gestrichen: Felder, die niemand auslesen
kann, sind Kommentare in Feldform. Was fehlt, ist eine Prüfungsfamilie über die Nutzlast — etwa
`{ check = "damage_type", value = "fire" }`. Zu entscheiden, bevor Affixe und Gegenstands-Trigger
kommen (M4), denn dort werden solche Effekte erwartet.
### Neun Events, feste Liste
| Event | Nutzlast über `source`/`target` hinaus |
@@ -144,6 +166,12 @@ Gegen Zyklen — Trigger A löst B aus, B löst A aus — ein schlichter Zähler
Zug**, danach Abbruch mit Warnzeile. Kein Zyklenerkennungs-Graph; der Zähler fängt denselben Fall und
ist eine Zeile.
**Befund aus M3: die Warteschlange war ein Stack.** Die erste Umsetzung nahm `Vec::pop()`, also LIFO —
ein Folge-Event drängelte sich vor die schon wartenden. Genau das sollte die Warteschlange verhindern
(„hängt es hinten an, statt den Stack zu vertiefen"). Jetzt `VecDeque` mit `pop_front`. Die Formulierung
oben war richtig, die Umsetzung nicht, und **kein Test hat es gemerkt** — Reihenfolge fällt erst auf,
wenn zwei Trigger um dasselbe Ziel konkurrieren.
Trigger werden bei jedem Event **gesucht, nicht indiziert** — dieselbe Entscheidung wie „kein
Attribut-Cache" in 07 und aus demselben Grund: Ein Index müsste bei jedem An- und Ablegen, jedem Loot
und jedem Affix-Wechsel gepflegt werden, also genau das Invalidierungsproblem, das 07 vermieden hat.

View File

@@ -102,6 +102,15 @@ p_flucht = clamp(0.5 + (dexterity(Held) - dexterity(schnellster Gegner)) * FLUCH
geminderten multipliziert, ist mathematisch belanglos: `roh × kf × (1−r)` ist dasselbe wie
`roh × (1−r) × kf`. Festzulegen war nur, dass beide multiplikativ *bleiben*.
**Befund aus M3: die Trefferformel war toter Code.** `hit_chance` existierte, war getestet — und wurde
nie aufgerufen. **Jeder Angriff traf.** Gemeldet hat es nur der Compiler
(`warning: function 'hit_chance' is never used`); kein Test schlug an, weil alle Tests die Formel
*direkt* prüften statt über einen Angriff.
Lehre für die Formeln unten: Eine Formel, die nur von ihrem eigenen Unit-Test aufgerufen wird, ist
nicht verdrahtet. Der Test, der das jetzt abfängt, führt 200 Angriffe gegen einen absurd flinken
Gegner und verlangt, dass mindestens einer verfehlt *und* mindestens einer trifft.
Drei Klemmbereiche, jeder mit Grund:
- **Resistenz bei `0.8`** — bei `1.0` wäre ein Wesen immun, und Immunität ist keine Zahl, sondern ein