Fix contact transitions and dining car access

This commit is contained in:
2026-05-24 18:14:16 +02:00
parent 510901f5bf
commit eef90f3471
8 changed files with 333 additions and 166 deletions
@@ -3877,9 +3877,9 @@ Komplexe Encounter sollen bevorzugt in mehrere parallele Progress-Tracker zerleg
Zusätzlich gibt es unabhängige Facts mit `mark`, `has` und `lacks`. Diese sind nur für zusammengehörige Gruppen einzelner Facts gedacht, die unabhängig voneinander wahr sein können und keine Reihenfolge implizieren, etwa Tutorial gesehen, Mahlzeit am Bahnhof genommen, öffentliche Form gewahrt oder beschädigt. Solche Facts sind keine Callbacks, solange sie nicht ein konkretes abgespieltes Contentstück ersetzen.
Für Choice-Surface-Arbitration gibt es `claim_choice_gate(gate)`. Dieser Helper ist kein Story-State. Er wird am Anfang jedes `provide_choices` zurückgesetzt und gibt innerhalb dieses einen Choice-Aufbaus nur beim ersten gültigen Claim für dasselbe Gate `true` zurück. Er wird verwendet, wenn mehrere Atome gleichzeitig gültig sein können, aber höchstens eines davon auf der Oberfläche erscheinen darf. Der wichtigste Fall sind priorisierte `#auto`-Familien wie Viktors Rückkehrkommentare.
Für Choice-Surface-Arbitration gibt es `claim_choice_gate_if(gate, available)`. Dieser Helper ist kein Story-State. Er wird am Anfang jedes `provide_choices` zurückgesetzt und gibt innerhalb dieses einen Choice-Aufbaus nur beim ersten gültigen Claim für dasselbe Gate `true` zurück. Er wird verwendet, wenn mehrere Atome gleichzeitig gültig sein können, aber höchstens eines davon auf der Oberfläche erscheinen darf. Der wichtigste Fall sind priorisierte `#auto`-Familien wie Viktors Rückkehrkommentare.
Die Reihenfolge der Atome im Bucket ist dann die Priorität. Auto-Choices werden vom Client getrennt behandelt, nicht randomisiert und wie normale Ink-Choices ausgelöst. Ein gemeinsamer Auto-Key wie `#auto:return(2)` taktet die Familie über mehrere Choice-Turns; `claim_choice_gate(return_auto)` verhindert mehrere Treffer im selben Choice-Aufbau. Der Gate-Claim steht immer als letzte Bedingung hinter den normalen Verfügbarkeitsbedingungen, damit eine falsche Kandidaten-Choice das Gate nicht verbraucht. Auf Choice-Zeilen wird für keyed Auto-Tags die Doppelpunktform verwendet, weil `#auto[return](2)` dort von Ink nicht akzeptiert wird.
Die Reihenfolge der Atome im Bucket ist dann die Priorität. Auto-Choices werden vom Client getrennt behandelt, nicht randomisiert und wie normale Ink-Choices ausgelöst. `claim_choice_gate_if(return_auto, available)` verhindert mehrere Treffer im selben Choice-Aufbau. Rückkehrkommentare müssen in ihrer `available`-Bedingung zusätzlich über den Contact Manager gegated werden, zum Beispiel mit `reunion(viktor)`. Die vollständige Verfügbarkeitsbedingung wird als zweiter Parameter übergeben, damit falsche Kandidaten das Gate nicht verbrauchen.
Regel: Wenn die spätere Zeile „du hast damals genau X getan“ bedeutet, ist es ein Callback und soll über ein Ink-Label laufen. Wenn sie „du wirkst auf mich inzwischen wie Y“ bedeutet, ist es eine Heuristik. Wenn sie „diese Wissens- oder Plotlinie ist jetzt mindestens so weit“ bedeutet, ist es ein Encounter State. Wenn sie „dieses unabhängige Ergebnis gilt“ bedeutet, ist es ein Fact. Wenn ein Vorgang auch nur begonnen und vollendet kennt, aber Vollendung den Beginn voraussetzt, ist es bereits ein Encounter State.
@@ -3892,15 +3892,30 @@ Nie zulässig:
## 12.5.2 Companion- und Privacy-Helfer
Für Dialoge, die nur ohne Zeugen oder nur mit einer bestimmten Begleitfigur auftauchen sollen:
Der Contact Manager in `helpers.ink` verwaltet, welche getrackten Figuren gerade anwesend sind, welche Valerie zum ersten Mal trifft, welche sie nach einer Trennung wieder sieht und welche gerade nicht mehr anwesend sind. Kapiteldateien dürfen dafür keine raumspezifischen Flags wie `viktor_returned_to_compartment` oder `seen_viktor_leave` anlegen.
`loc_move_to(location)` setzt Valeries Ort, bewegt alle Figuren in `companions` mit und aktualisiert danach den Kontaktzustand. `companion_join(character)` und `companion_leave(character)` steuern nur, ob eine Figur Valerie bei Traversal folgt. Sie sind keine Erinnerungsflags. Startpositionen von Figuren und die anfängliche Begleitung gehören in das Episode-Setup. Wenn ein Setup mehrere Figuren manuell platziert, wird danach `contact_sync()` verwendet, damit der Anfangszustand ohne künstliche Wiedersehens- oder Erstbegegnungsreaktion gesetzt ist.
Für Dialoge, die nur ohne Zeugen, nur mit einer bestimmten Begleitfigur oder nur direkt nach einem Kontaktwechsel auftauchen sollen:
```ink
{present(viktor): ...}
{first_meeting(viktor): ...}
{reunion(viktor): ...}
{parting(viktor): ...}
{alone(): ...}
{alone_with(viktor): ...}
```
`present(character)` prüft, ob die Figur im aktuellen Raum ist. `alone()` ist wahr, wenn keine getrackte Figur im Raum ist. `alone_with(character)` ist wahr, wenn genau diese Figur anwesend ist. Bei neuen getrackten Figuren muss die Helper-Implementierung erweitert werden, damit `alone()` und `alone_with()` nicht still zu eng bleiben.
`present(character)` prüft, ob die Figur im aktuellen Raum ist. `first_meeting(character)`, `reunion(character)` und `parting(character)` gelten nur für die erste Choice-Oberfläche direkt nach dem Kontaktwechsel. Der nächste Turn löscht diese Transitionen automatisch in `provide_choices`. Kapitelinhalt darf Contact-Transitionen nur abfragen, niemals manuell löschen oder verbrauchen.
```ink
+ {claim_choice_gate_if(return_auto, reunion(viktor) && state_reached(freshen_up_done))} [AUTO: Viktors Rückkehr nach Frischmachen] #auto
...
-> TURN
```
`alone()` ist wahr, wenn keine getrackte Figur im Raum ist. `alone_with(character)` ist wahr, wenn genau diese Figur anwesend ist. Bei neuen getrackten Figuren muss die Helper-Implementierung an einer Stelle erweitert werden, damit Presence, Contact Transitions und Privacy Checks gemeinsam korrekt bleiben.
## 12.6 Conditionals