Checkpoint current interactive fiction changes
This commit is contained in:
@@ -46,7 +46,8 @@ INCLUDE character_generator_generated_includes_and_routers.ink
|
|||||||
|
|
||||||
Es klopft.
|
Es klopft.
|
||||||
|
|
||||||
Viktor hat eure Billete bereits in der Hand, als sich die Tür öffnet. Der Schaffner prüft sie, entwertet sie mit seiner Zange, nickt und gibt sie zurück. Keine Frage nach Namen, Stand oder Auftrag. Nur die kurze Amtsbewegung eines Mannes, der Menschen ausschließlich nach Fahrscheinen ordnet. #gloss[Billete](Fahrkarten. Ein französisches Lehnwort für kleine Scheine oder Zettel, im Eisenbahnverkehr der Monarchie noch ganz zu Hause. "Fahrkarte" wäre verständlicher, aber auch ärmer.)
|
Viktor hat eure Billete bereits in der Hand, als sich die Tür öffnet. Der Schaffner prüft sie, entwertet sie mit seiner Zange, nickt und gibt sie zurück. Keine Frage nach Namen, Stand oder Auftrag. Nur die kurze Amtsbewegung eines Mannes, der Menschen ausschließlich nach Fahrscheinen ordnet.
|
||||||
|
#gloss[Billete](Fahrkarten. Ein französisches Lehnwort für kleine Scheine oder Zettel, im Eisenbahnverkehr der Monarchie noch ganz zu Hause. _Fahrkarte_ wäre verständlicher, aber auch ärmer.)
|
||||||
|
|
||||||
Als die Tür wieder geschlossen ist, betrachtet Viktor die beiden Billete einen Moment zu lange.
|
Als die Tür wieder geschlossen ist, betrachtet Viktor die beiden Billete einen Moment zu lange.
|
||||||
|
|
||||||
@@ -60,12 +61,12 @@ INCLUDE character_generator_generated_includes_and_routers.ink
|
|||||||
|
|
||||||
Du liest das Formular und widmest dich jedem Punkt sorgfältig.
|
Du liest das Formular und widmest dich jedem Punkt sorgfältig.
|
||||||
|
|
||||||
Geschlecht. Du setzt das Kreuz bei weiblich.
|
_Geschlecht._ Du setzt das Kreuz bei _weiblich_.
|
||||||
~ gender = "weiblich"
|
~ gender = "weiblich"
|
||||||
|
|
||||||
Dein Körper hat deine Möglichkeiten von Geburt an festgelegt, lange bevor du alt genug warst, ein Wort dafür zu besitzen. Das Formular fragt nicht nach mehr. Es will nur wissen, in welche Hälfte des Gesetzes es dich legen muss.
|
Dein Körper hat deine Möglichkeiten von Geburt an festgelegt, lange bevor du alt genug warst, ein Wort dafür zu besitzen. Das Formular fragt nicht nach mehr. Es will nur wissen, in welche Hälfte des Gesetzes es dich legen muss.
|
||||||
|
|
||||||
Tag und Jahr der Geburt.
|
_Tag und Jahr der Geburt._
|
||||||
|
|
||||||
* [__Notiere__: 7. April 1877.]
|
* [__Notiere__: 7. April 1877.]
|
||||||
~ birthdate = "7. April 1877"
|
~ birthdate = "7. April 1877"
|
||||||
@@ -167,7 +168,7 @@ INCLUDE character_generator_generated_includes_and_routers.ink
|
|||||||
-
|
-
|
||||||
-> cg_after_birthdate_written ->
|
-> cg_after_birthdate_written ->
|
||||||
|
|
||||||
Geburtsort
|
_Geburtsort_.
|
||||||
|
|
||||||
* [__Geboren in__: Wien.]
|
* [__Geboren in__: Wien.]
|
||||||
~ birthplace = "Wien"
|
~ birthplace = "Wien"
|
||||||
@@ -341,31 +342,31 @@ INCLUDE character_generator_generated_includes_and_routers.ink
|
|||||||
-
|
-
|
||||||
-> cg_after_birthplace_written ->
|
-> cg_after_birthplace_written ->
|
||||||
|
|
||||||
Genossene Bildung
|
_Genossene Bildung_.
|
||||||
|
|
||||||
* [__Notiere__: häuslicher Unterricht, katholisches Mädchenpensionat, Privatstunden in französischer Sprache, Clavierspiel und Zeichnen.]
|
* [__Notiere__: häuslicher Unterricht, katholisches Mädchenpensionat, Privatstunden in französischer Sprache, Klavierspiel und Zeichnen.]
|
||||||
~ class = noble
|
~ class = noble
|
||||||
~ mark(class_def)
|
~ mark(class_def)
|
||||||
~ education = "häuslicher Unterricht, katholisches Mädchenpensionat, Privatstunden in französischer Sprache, Clavierspiel und Zeichnen"
|
~ education = "häuslicher Unterricht, katholisches Mädchenpensionat, Privatstunden in französischer Sprache, Clavierspiel und Zeichnen"
|
||||||
Du schreibst: {education}.
|
Du schreibst: _{education}_.
|
||||||
|
|
||||||
So sieht eine Erziehung aus, wenn man sie auf eine Zeile zwingt. Kein Wort von kalten Schlafsälen. Kein Wort von geraden Rücken, gesenkten Stimmen und den langen Nachmittagen, an denen man lernt, dass ein Fehler in der Haltung schwerer wiegen kann als ein Fehler im Denken.
|
So sieht eine Erziehung aus, wenn man sie auf eine Zeile zwingt. Kein Wort von kalten Schlafsälen. Kein Wort von geraden Rücken, gesenkten Stimmen und den langen Nachmittagen, an denen man lernt, dass ein Fehler in der Haltung schwerer wiegen kann als ein Fehler im Denken.
|
||||||
|
|
||||||
* [__Notiere__: fünf Classen Volksschule, drei Classen Mädchen-Bürgerschule, Fortbildung in Stenographie und Kanzleiarbeit.]
|
* [__Notiere__: fünf Jahre Volksschule, drei Jahre Mädchen-Bürgerschule, Fortbildung in Stenographie und Kanzleiarbeit.]
|
||||||
~ class = middle
|
~ class = middle
|
||||||
~ mark(class_def)
|
~ mark(class_def)
|
||||||
~ education = "fünf Classen Volksschule, drei Classen Mädchen-Bürgerschule, Fortbildung in Stenographie und Kanzleiarbeit"
|
~ education = "fünf Classen Volksschule, drei Classen Mädchen-Bürgerschule, Fortbildung in Stenographie und Kanzleiarbeit"
|
||||||
Du schreibst: {education}.
|
Du schreibst: _{education}_.
|
||||||
|
|
||||||
So sieht Bildung aus, wenn man sie nützlich genug macht, um geduldet zu werden. Rechnen, Schreiben, Stenographie, Kanzleiarbeit. Nicht gelehrt genug, um Titel zu tragen, aber brauchbar genug, um Männer in Ämtern nervös zu machen.
|
So sieht Bildung aus, wenn man sie nützlich genug macht, um geduldet zu werden. Rechnen, Schreiben, Stenographie, Kanzleiarbeit. Nicht gelehrt genug, um akademische Titel zu tragen, aber brauchbar genug, um Männer in Ämtern nervös zu machen.
|
||||||
|
|
||||||
* [__Notiere__: fünf Classen Volksschule, anschließend Dienstbotenstelle in adeligem Haushalt.]
|
* [__Notiere__: fünf Jahre Volksschule, anschließend Dienstbotenstelle in adeligem Haushalt.]
|
||||||
~ class = working
|
~ class = working
|
||||||
~ mark(class_def)
|
~ mark(class_def)
|
||||||
~ education = "fünf Classen Volksschule, anschließend Dienstbotenstelle in adeligem Haushalt"
|
~ education = "fünf Classen Volksschule, anschließend Dienstbotenstelle in adeligem Haushalt"
|
||||||
Du schreibst: {education}.
|
Du schreibst: _{education}_.
|
||||||
|
|
||||||
So sieht Bildung aus, wenn Armut die Schulbank früher beendet als der Verstand. Fünf Klassen, dann fremde Böden, fremde Teller, fremde Klingeln und die Kunst, in Räumen zu verschwinden, in denen man alles hört.
|
So sieht Bildung aus, wenn Armut die Schulbank früher beendet als der Verstand. Fünf Klassen, dann fremde Böden, fremde Teller, fremde Betten und die Kunst, in Räumen zu verschwinden, in denen man alles hört.
|
||||||
|
|
||||||
-
|
-
|
||||||
{
|
{
|
||||||
@@ -388,60 +389,133 @@ INCLUDE character_generator_generated_includes_and_routers.ink
|
|||||||
~ title_display = "—"
|
~ title_display = "—"
|
||||||
~ title_part = "Fräulein"
|
~ title_part = "Fräulein"
|
||||||
}
|
}
|
||||||
Umgangssprache
|
|
||||||
|
|
||||||
Du trägst ein: {everyday_language}.
|
_Umgangssprache_.
|
||||||
|
|
||||||
Es ist nicht nur eine Sprache, sondern eine Art, Türen zu öffnen oder geschlossen zu halten. Ein Wort kann höflich sein, unterwürfig, spöttisch oder tödlich, je nachdem, wer es spricht und wer zuhören muss.
|
Du trägst ein: _{everyday_language}_.
|
||||||
Kenntniß der deutschen Sprache
|
|
||||||
|
|
||||||
Du trägst ein: {german_knowledge}.
|
Es ist nicht nur eine Sprache, sondern eine Art, Türen zu öffnen.
|
||||||
|
|
||||||
|
_Kenntniß der deutschen Sprache_.
|
||||||
|
|
||||||
|
Du schreibst: _{german_knowledge}_.
|
||||||
|
|
||||||
Das ist weniger Auskunft als Voraussetzung. Wer für Wien arbeitet, muss in Wien lesbar sein.
|
Das ist weniger Auskunft als Voraussetzung. Wer für Wien arbeitet, muss in Wien lesbar sein.
|
||||||
Religionsbekenntniß
|
|
||||||
|
|
||||||
Du trägst ein: {religion}.
|
_Religionsbekenntniß_.
|
||||||
|
|
||||||
Nicht, weil ein Formular zwischen Glauben, Gewohnheit und Gehorsam unterscheiden könnte. Es fragt nach dem Bekenntnis, nicht nach dem Zweifel. Nach der Kirche, nicht nach dem, was in der Nacht übrigbleibt, wenn keine Glocke schlägt.
|
Du setzt das Kreuz bei: _{religion}_.
|
||||||
Familienstand
|
|
||||||
|
|
||||||
Du trägst ein: {marital_status}.
|
Das Formular ist nicht daran interessiert, zwischen Glauben, Gewohnheit und Gehorsam zu unterscheiden. Es fragt nach dem Bekenntnis, nicht nach dem Zweifel. Nach der Kirche, nicht nach dem, was übrigbleibt, wenn keine Glocke schlägt.
|
||||||
|
|
||||||
Ein schmales Wort. Beinahe leer.
|
_Familienstand_.
|
||||||
|
|
||||||
Ledig heißt nicht frei. Es heißt nur, dass noch kein Mann amtlich genug zu dir gehört, um anstelle deines Vaters, deiner Familie, deines Standes oder irgendeines Herrn im Amt als Erklärung zu dienen.
|
Du wählst: _ledig_.
|
||||||
Fremdsprachenkenntniß
|
|
||||||
|
Ein einfaches Wort. Auf dem Papier nur ein Zustand, im Leben eine Frist.
|
||||||
|
|
||||||
|
Ledig heißt nicht frei. Vor dem Gesetz vertreten dich noch andere, in den Augen von Frauen bist du nicht vollendet, in den Augen von Junggesellen noch zu haben. Jugend macht daraus Reiz und Gefahr. Alter macht daraus Versagen.
|
||||||
|
|
||||||
|
_Fremdsprachenkenntniß_.
|
||||||
|
|
||||||
{
|
{
|
||||||
- foreign_languages == "":
|
- foreign_languages == "":
|
||||||
Du lässt die Zeile leer.
|
Du lässt die Zeile leer.
|
||||||
|
Sie erfasst nicht, was du verstehst, sondern nur, was ein Amt für vermerkenswert hält.
|
||||||
Auch eine leere Zeile ist eine Aussage. Sie behauptet nicht, was du verstehst, sondern nur, was ein Amt für vermerkenswert hält.
|
|
||||||
- else:
|
- else:
|
||||||
Du trägst ein: {foreign_languages}.
|
Du trägst ein: _{foreign_languages}_.
|
||||||
|
|
||||||
Manche Sprachen lernt man für Bücher, manche für Salons, manche für Dienstbotenflure und Bahnhöfe. Auf dem Papier sieht alles nach Unterricht aus. In der Wirklichkeit verrät jede Sprache zuerst, wo man zuhören durfte.
|
Manche Sprachen lernt man für Bücher, manche für Salons, manche für Dienstbotenflure und Bahnhöfe. Auf dem Papier sieht alles nach Unterricht aus. In der Wirklichkeit verrät jede Sprache vor allem, wo man Zugang hatte.
|
||||||
}
|
}
|
||||||
|
|
||||||
Stand und Beschäftigung
|
_Stand und Beschäftigung_.
|
||||||
|
|
||||||
Die Zeile bleibt einen Augenblick leer, weil du nicht weißt, welches Wort für das, was man mit dir vorhat, weniger falsch wäre.
|
Die Zeile bleibt einen Augenblick leer, weil du nicht weißt, welches Wort für das, was man mit dir vorhat, vorgesehen ist.
|
||||||
|
|
||||||
Du siehst zu Nowak hinüber.
|
* [__Erkundige dich bei Viktor__: „Was soll ich hier eintragen?“] „Was soll ich hier eintragen?“, fragst du auf das Feld deutend.
|
||||||
|
Er nimmt das Formular nicht an sich. Er beugt sich nur leicht vor.
|
||||||
„Was soll ich hier eintragen?“
|
„Standesbezeichnung zuerst. Danach die Verwendung.“
|
||||||
|
* [__Sprich laut mit dir selbst__: „Stand und Beschäftigung?“] „Stand und Beschäftigung?“, murmelst du, während du auf die Zeile starrst.
|
||||||
Er nimmt das Formular nicht an sich. Er beugt sich nur leicht vor, als sei selbst die Erklärung eine Sache, die man nicht zu laut sagen dürfe.
|
„Standesbezeichnung zuerst. Danach die Verwendung.“, unterbricht Viktor deine Gedanken.
|
||||||
|
|
||||||
|
* [__Wahre Form__: „Welche Fassung ist für die Akte gewünscht?“]
|
||||||
|
~ route_inc(route_composure)
|
||||||
|
„Welche Fassung ist für die Akte gewünscht?“, fragst du.
|
||||||
|
Viktor nimmt das Formular nicht an sich. Er beugt sich nur leicht vor.
|
||||||
„Standesbezeichnung zuerst. Danach die Verwendung.“
|
„Standesbezeichnung zuerst. Danach die Verwendung.“
|
||||||
|
|
||||||
„Verwendung?“
|
* [__Prüfe__: „Das Feld fragt nicht nach Arbeit allein.“]
|
||||||
|
~ route_inc(route_detective)
|
||||||
|
Du siehst nicht auf Viktor, sondern auf die Überschrift.
|
||||||
|
„Das Feld fragt nicht nach Arbeit allein“, sagst du. „Es fragt nach der Ordnung, in der man sie lesen soll.“
|
||||||
|
Viktors Blick fällt kurz auf die Zeile.
|
||||||
|
„Standesbezeichnung zuerst. Danach die Verwendung.“
|
||||||
|
|
||||||
|
* [__Entwaffne__: „Hier brauche ich Ihren Instinkt für Amtsworte.“]
|
||||||
|
~ route_inc(route_lover)
|
||||||
|
Du hebst die Feder ein wenig, als wäre sie schwerer, als sie ist.
|
||||||
|
„Ich fürchte, Herr Nowak, hier brauche ich Ihren Instinkt für unverdächtige Amtsworte.“
|
||||||
|
Er sieht dich an, zu kurz für einen Tadel und zu lange für reine Hilfe.
|
||||||
|
„Standesbezeichnung zuerst. Danach die Verwendung.“
|
||||||
|
|
||||||
|
* [__Erkenne__: „Wie nennt man eine Frau, die nicht in die Zeile passt?“]
|
||||||
|
~ route_inc(route_sapphic)
|
||||||
|
Du denkst an all die Frauen, die in Formularen nicht arbeiten, sondern helfen; nicht entscheiden, sondern dienen; nicht handeln, sondern zugeordnet werden.
|
||||||
|
„Wie nennt man eine Frau, die nicht recht in die vorgesehene Zeile passt?“
|
||||||
|
Viktor antwortet ohne Spott.
|
||||||
|
„Standesbezeichnung zuerst. Danach die Verwendung.“
|
||||||
|
|
||||||
|
* [__Verplappere dich__: „Beschäftigt, wie eine Gouvernante mit schlechten Referenzen.“]
|
||||||
|
~ route_inc(route_careless)
|
||||||
|
Die Feder berührt das Papier schon, bevor du dich entschieden hast.
|
||||||
|
„Beschäftigung“, murmelst du. „Das klingt, als hätte man mich angestellt wie eine Gouvernante mit schlechteren Referenzen.“
|
||||||
|
Erst als Viktor den Blick hebt, merkst du, dass du laut gesprochen hast.
|
||||||
|
„Standesbezeichnung zuerst. Danach die Verwendung.“
|
||||||
|
-
|
||||||
|
_Verwendung._ Das Wort bleibt einen Augenblick zwischen euch stehen.
|
||||||
|
|
||||||
|
* [__Versichere dich__: „Verwendung?“]
|
||||||
|
~ route_inc(route_composure)
|
||||||
|
„Verwendung?“
|
||||||
|
Viktor nickt.
|
||||||
„In besonderer Verwendung.“
|
„In besonderer Verwendung.“
|
||||||
|
|
||||||
Das ist offenbar der Ausdruck, auf den man sich geeinigt hat, wenn man weder lügen noch erklären will.
|
* [__Prüfe__: „Durch wen?“]
|
||||||
|
~ route_inc(route_detective)
|
||||||
|
„Durch wen?“
|
||||||
|
Viktor sieht dich an, als sei die Frage berechtigt und die Antwort unerwünscht.
|
||||||
|
„Durch die Stelle, die Sie entsendet. In besonderer Verwendung.“
|
||||||
|
|
||||||
Du trägst ein: {occupation}.
|
* [__Entwaffne__: „Das klingt fast schmeichelhaft.“]
|
||||||
|
~ route_inc(route_lover)
|
||||||
|
„Das klingt fast schmeichelhaft.“
|
||||||
|
„Verlassen Sie sich nicht darauf. In besonderer Verwendung.“
|
||||||
|
Es ist trocken gesprochen, aber nicht kalt.
|
||||||
|
|
||||||
|
* [__Erkenne__: „So nennt man Frauen, wenn man sie braucht.“]
|
||||||
|
~ route_inc(route_sapphic)
|
||||||
|
„So nennt man Frauen, wenn man sie braucht.“
|
||||||
|
Viktor antwortet nicht sofort.
|
||||||
|
Vielleicht, weil es zu allgemein klingt. Vielleicht, weil es zu genau ist.
|
||||||
|
„In besonderer Verwendung.“
|
||||||
|
|
||||||
|
* [__Verplappere dich__: „Also nicht Dienst. Nicht Amt. Nicht Beruf.“]
|
||||||
|
~ route_inc(route_careless)
|
||||||
|
„Also nicht Dienst“, sagst du leiser, als du beabsichtigst. „Nicht Amt. Nicht Beruf.“
|
||||||
|
Viktor hebt den Blick.
|
||||||
|
„In besonderer Verwendung.“
|
||||||
|
Du nickst, als hättest du nur den Wortlaut gesucht und nicht die Stelle, an der er ausweicht.
|
||||||
|
|
||||||
|
* [__Benenne__: „Ein höfliches Wort für Benutzung.“]
|
||||||
|
~ route_inc(route_eccentric)
|
||||||
|
„Ein höfliches Wort für Benutzung.“
|
||||||
|
Viktor hält still. Nicht beleidigt. Aufmerksamer.
|
||||||
|
„Ein notwendiges Wort“, sagt er.
|
||||||
|
„Das macht es nicht höflicher.“
|
||||||
|
„Nein“, sagt er. „Nur verwendbar. In besonderer Verwendung.“
|
||||||
|
-
|
||||||
|
Du schreibst: _{occupation}_.
|
||||||
|
Das ist offenbar der Ausdruck, auf den man sich geeinigt hat, wenn man weder lügen noch erklären will.
|
||||||
|
|
||||||
{
|
{
|
||||||
- class == noble:
|
- class == noble:
|
||||||
@@ -451,43 +525,40 @@ INCLUDE character_generator_generated_includes_and_routers.ink
|
|||||||
- class == working:
|
- class == working:
|
||||||
Ehemaliges Dienstmädchen. Das Wort bleibt stehen, auch wenn man dir nun bessere Handschuhe gibt. In besonderer Verwendung. Das Amt kann aufsteigen lassen, ohne zu vergessen, woher es jemanden geholt hat.
|
Ehemaliges Dienstmädchen. Das Wort bleibt stehen, auch wenn man dir nun bessere Handschuhe gibt. In besonderer Verwendung. Das Amt kann aufsteigen lassen, ohne zu vergessen, woher es jemanden geholt hat.
|
||||||
}
|
}
|
||||||
Titel und Standesbezeichnung
|
|
||||||
|
|
||||||
Du trägst ein: {title_display}.
|
_Titel und Standesbezeichnung_.
|
||||||
|
|
||||||
|
Du trägst ein: _{title_display}_.
|
||||||
|
|
||||||
{
|
{
|
||||||
- class == noble:
|
- class == noble:
|
||||||
Das Wort ist kleiner als der Abstand, den es schafft. Nicht Gräfin. Nicht Fürstin. Nicht bloß Fräulein. Freiin. Ein Titel für Türen, für Vorstellungen, für den Unterschied zwischen einer Zumutung und einer Bitte.
|
Das Wort ist kleiner als der Abstand, den es schafft. Nicht Gräfin. Nicht Fürstin. Nicht bloß Fräulein. Freiin. Ein Titel für Türen, für Vorstellungen, für den Unterschied zwischen einer Zumutung und einer Bitte.
|
||||||
- class == middle:
|
- class == middle:
|
||||||
Der Strich bleibt stehen, wo ein Mann vielleicht ein „Dr.“ vor sich hergetragen hätte. Du bist nicht ungelehrt genug, um ihn nicht zu bemerken.
|
Der Strich bleibt stehen, wo ein Mann vielleicht einen Titel vor sich hergetragen hätte. Du bist nicht ungelehrt genug, das dich das nicht stört.
|
||||||
- class == working:
|
- class == working:
|
||||||
Der Strich ist ehrlich. Kein Titel, kein Prädikat, keine höfliche Abkürzung vor dem Namen. Nur die Stelle, an der andere Menschen Abstand eintragen.
|
Der Strich ist ehrlich. Kein Titel, kein Prädikat, keine höfliche Abkürzung vor dem Namen. Nur der Beweis, dass du dir alles was du bist selbst erkämpfen musstest.
|
||||||
}
|
}
|
||||||
Vor- und Zuname
|
_Vor- und Zuname_.
|
||||||
|
|
||||||
-> cg_route_given_names ->
|
-> cg_route_given_names ->
|
||||||
~ mark(name_def)
|
~ mark(name_def)
|
||||||
~ state_reach(ValerieWorkingMask.name_claimed)
|
~ state_reach(ValerieWorkingMask.name_claimed)
|
||||||
|
|
||||||
Für einen Augenblick ist alles beisammen.
|
Es scheint, als wäre alles beisammen.
|
||||||
|
|
||||||
{given_names} {surname_written_form}
|
_{given_names} {surname_written_form},_
|
||||||
|
_geboren am {birthdate} zu {birthplace},_
|
||||||
|
_weiblich, {religion}, {marital_status},_
|
||||||
|
_{everyday_language}{foreign_languages != "": , {foreign_languages}}_
|
||||||
|
_{occupation}_
|
||||||
|
|
||||||
weiblich
|
Das beschreibt dich nicht ganz. Aber genug von dir, um abgelegt, weitergereicht und bei Bedarf wieder hervorgeholt zu werden.
|
||||||
geboren am {birthdate} zu {birthplace}
|
|
||||||
Umgangssprache {everyday_language}
|
|
||||||
{religion}
|
|
||||||
{marital_status}
|
|
||||||
{title_display}
|
|
||||||
{occupation}
|
|
||||||
|
|
||||||
Da steht nicht du. Nicht ganz. Aber genug von dir, um abgelegt, weitergereicht und bei Bedarf wieder hervorgeholt zu werden.
|
|
||||||
|
|
||||||
Du legst die Feder nieder.
|
Du legst die Feder nieder.
|
||||||
|
|
||||||
Nowak wartet, bis du das Blatt von selbst zu ihm schiebst. Erst dann nimmt er es auf.
|
Viktor wartet, bis du das Blatt von selbst zu ihm schiebst. Erst dann nimmt er es auf.
|
||||||
|
|
||||||
Er liest schnell. Nicht flüchtig. Nur ohne das geringste Bedürfnis, dich vor deinem eigenen Eintrag zu schützen.
|
Er liest schnell. Nicht flüchtig. Ohne Kommentar.
|
||||||
|
|
||||||
{class == noble: „Danke, gnädiges Fräulein.“|„Danke, Fräulein {surname}.“}
|
{class == noble: „Danke, gnädiges Fräulein.“|„Danke, Fräulein {surname}.“}
|
||||||
|
|
||||||
@@ -499,9 +570,7 @@ INCLUDE character_generator_generated_includes_and_routers.ink
|
|||||||
=== train_corridor_tunnel_event ===
|
=== train_corridor_tunnel_event ===
|
||||||
Der Zug stößt in einen Tunnel.
|
Der Zug stößt in einen Tunnel.
|
||||||
|
|
||||||
Das Land draußen erlischt. Die Scheiben verlieren Felder, Himmel und die hellen Fetzen von Ortschaften; übrig bleiben Messinggriffe, dunkles Holz und der bleiche Umriss deines Körpers.
|
Das Land draußen erlischt. Die Scheiben verlieren Felder, Himmel und die hellen Fetzen von Ortschaften. Übrig bleiben Messinggriffe, dunkles Holz und der bleiche Umriss deines Körpers.
|
||||||
|
|
||||||
Ausgerechnet jetzt wird das Glas nützlich. Es zeigt die Frau, die in Eibenreith aussteigen wird.
|
|
||||||
|
|
||||||
* {lacks(body_def)} [__Erkenne__: Eine kleine, schmale Gestalt.]
|
* {lacks(body_def)} [__Erkenne__: Eine kleine, schmale Gestalt.]
|
||||||
~ body = small_slender
|
~ body = small_slender
|
||||||
|
|||||||
@@ -7,38 +7,38 @@
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
=== cg_after_given_names_written ===
|
=== cg_after_given_names_written ===
|
||||||
Du schreibst die Vornamen aus: {given_names}.
|
Du schreibst die Vornamen aus: _{given_names}_.
|
||||||
|
|
||||||
{given_names_meaning_fragment}. {given_names_combination_note}
|
{given_names_meaning_fragment}. {given_names_combination_note}
|
||||||
|
|
||||||
Die Namen liegen vollständig auf dem Papier. Aber kein Mensch hört innerlich auf alle Namen zugleich.
|
Die Namen stehen nun vollständig auf dem Papier. Aber kein Mensch hört innerlich auf alle Namen zugleich.
|
||||||
->->
|
->->
|
||||||
|
|
||||||
=== cg_after_common_name_chosen ===
|
=== cg_after_common_name_chosen ===
|
||||||
{common_name} ist der Name, bei dem du zuerst antwortest.
|
{common_name} ist der Name, auf den du zuerst reagierst.
|
||||||
|
|
||||||
Nicht das ganze Papier. Nicht die ganze Familie. Nur der Teil, der gemeint ist, wenn jemand dich ruft und du dich umdrehst, bevor du entschieden hast, ob du gehorchen willst.
|
Nicht der Name für Akten, Vorstellungen und Besuchskarten. Der andere. Der, bei dem du dich umdrehst, bevor du weißt, ob du gesehen werden willst.
|
||||||
->->
|
->->
|
||||||
|
|
||||||
=== cg_after_surname_written ===
|
=== cg_after_surname_written ===
|
||||||
Du setzt darunter: {surname_written_form}.
|
Du setzt darunter: _{surname_written_form}_.
|
||||||
|
|
||||||
{surname_comment}
|
{surname_comment}
|
||||||
->->
|
->->
|
||||||
|
|
||||||
=== cg_after_birthdate_written ===
|
=== cg_after_birthdate_written ===
|
||||||
Du trägst ein: {birthdate}.
|
Du trägst ein: _{birthdate}_.
|
||||||
|
|
||||||
{age} Jahre. Alt genug, um allein mit einem Offizier erster Klasse durchs Land geschickt zu werden, und doch nicht alt genug, um vor dem Gesetz ganz dir selbst zu gehören.
|
{age} Jahre. Alt genug, um allein mit einem Offizier erster Klasse durchs Land geschickt zu werden, und doch nicht alt genug, um vor dem Gesetz ganz dir selbst zu gehören.
|
||||||
Sternzeichen {zodiac}: {zodiac_note}.
|
Sternzeichen {zodiac}: {zodiac_note}.
|
||||||
|
|
||||||
{zodiac_comment}
|
{zodiac_comment}
|
||||||
|
|
||||||
Kein Feld dafür im Formular. Du musst nicht daran glauben, aber es nicht zu wissen, könnte deiner Glaubwürdigkeit in genau jenen Kreisen schaden, zu denen dein Ruf als Medium dir Zugang verschafft.
|
Kein Feld dafür im Formular. Du brauchst nicht daran zu glauben, aber darüber Bescheid zu wissen, ist wichtig für deine Glaubwürdigkeit in genau jenen Kreisen, zu denen dein Ruf als Medium dir Zugang verschafft.
|
||||||
->->
|
->->
|
||||||
|
|
||||||
=== cg_after_birthplace_written ===
|
=== cg_after_birthplace_written ===
|
||||||
Du schreibst: {birthplace}.
|
Du füllst aus: _{birthplace}_.
|
||||||
|
|
||||||
{birthplace_note}.
|
{birthplace_note}.
|
||||||
|
|
||||||
|
|||||||
@@ -11,818 +11,6 @@ Dieses Dokument ist ausführlich, weil *Eibenreith* kein rein mechanisches Choic
|
|||||||
|
|
||||||
**Regel zur Konfliktauflösung:** Neuere Entscheidungen überschreiben ältere. Entscheidungen des Autors überschreiben Vorschläge des Assistenten, sofern sie nicht später ausdrücklich verworfen wurden. Frühere Ideen werden nur erwähnt, wenn sie als Negativregel oder offene Frage für die weitere Arbeit relevant bleiben.
|
**Regel zur Konfliktauflösung:** Neuere Entscheidungen überschreiben ältere. Entscheidungen des Autors überschreiben Vorschläge des Assistenten, sofern sie nicht später ausdrücklich verworfen wurden. Frühere Ideen werden nur erwähnt, wenn sie als Negativregel oder offene Frage für die weitere Arbeit relevant bleiben.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 0. Rekonstruktionsverfahren und Materialinventar
|
|
||||||
|
|
||||||
Dieser Abschnitt ist der erste Durchgang der GDD-Rekonstruktion. Er dient nicht als endgültiger Leseteil, sondern als Kontrollinstrument: Erst wird vollständig inventarisiert, dann widersprüchliches Material bereinigt, dann werden die endgültigen Referenzkapitel ausgearbeitet. Dieser Abschnitt bleibt solange im Dokument, bis die Arbeitsbibel vollständig genug ist, um ihn als Checkliste in einen Anhang zu verschieben.
|
|
||||||
|
|
||||||
## 0.1 Ziel dieses Dokuments
|
|
||||||
|
|
||||||
Das Ziel ist keine Zusammenfassung der letzten Arbeitsschritte. Das Ziel ist eine **Produktionsbibel** für Schreiben, Design und Ink-Implementierung. Das Dokument muss später beantworten können:
|
|
||||||
|
|
||||||
- Wie muss eine neue Szene klingen?
|
|
||||||
- Welche sozialen und körperlichen Ebenen muss sie bedienen?
|
|
||||||
- Welche Valerie-Route wird mit welchen Verben ausgedrückt?
|
|
||||||
- Welche Relationship-Werte sind gemeint?
|
|
||||||
- Welche States dürfen gesetzt werden?
|
|
||||||
- Welche Inhalte gehören in Glossare statt Haupttext?
|
|
||||||
- Wie werden Episodes, Traversals und Bucket-Content gebaut?
|
|
||||||
- Welche offenen Entscheidungen dürfen nicht versehentlich als entschieden behandelt werden?
|
|
||||||
- Welche alten Ideen sind verworfen und dürfen nicht wieder eingeschleppt werden?
|
|
||||||
|
|
||||||
Die Zielqualität ist daher nicht „kurz“, sondern **vollständig, navigierbar, widerspruchsbereinigt und umsetzbar**.
|
|
||||||
|
|
||||||
## 0.2 Arbeitsmethode ab jetzt
|
|
||||||
|
|
||||||
Die Rekonstruktion erfolgt in echten Durchgängen:
|
|
||||||
|
|
||||||
1. **Materialinventar**
|
|
||||||
Alle erarbeiteten Bereiche werden gesammelt, ohne sie vorschnell zu kürzen.
|
|
||||||
|
|
||||||
2. **Widerspruchsauflösung**
|
|
||||||
Neuere Entscheidungen überschreiben ältere. Autorentscheidungen überschreiben Assistentenvorschläge. Verworfenes wird als Negativregel markiert.
|
|
||||||
|
|
||||||
3. **Zielstruktur**
|
|
||||||
Das GDD wird nicht als Fließzusammenfassung, sondern als Arbeitsbibel gegliedert: Creative Bible, Style Guide, Character Bible, Systems Design, Ink Architecture, UI/Tag-Spec, Content Spec.
|
|
||||||
|
|
||||||
4. **Ausbau der Abschnitte**
|
|
||||||
Jeder große Bereich erhält nicht nur Prinzipien, sondern Beispiele, Gegenbeispiele, Templates und Prüffragen.
|
|
||||||
|
|
||||||
5. **Finale QA**
|
|
||||||
Prüfung gegen die bisher erarbeiteten Inhalte: Fehlt eine Entscheidung? Ist ein alter Stand wieder hineingerutscht? Ist etwas zu abstrakt für spätere Umsetzung?
|
|
||||||
|
|
||||||
## 0.3 Materialinventar: Creative Direction
|
|
||||||
|
|
||||||
Folgende Entscheidungen gehören dauerhaft in die Creative Bible:
|
|
||||||
|
|
||||||
- Das Spiel heißt bzw. arbeitet unter dem Titel *Eibenreith*.
|
|
||||||
- Es ist ein deutschsprachiges, choice-basiertes Ink-Spiel.
|
|
||||||
- Es spielt 1898 in einer kaiserpunkartigen, aber historisch oberflächenplausiblen österreichisch-ungarischen Welt.
|
|
||||||
- Das öffentliche Erscheinungsbild bleibt realistisch; das Übernatürliche ist verborgen, sozial vermittelt und nicht als offenes Fantasy-System sichtbar.
|
|
||||||
- Der Kern ist psychologisches Beziehungsdrama über Identität, Rollen, weibliche Selbstbehauptung, soziale Lesbarkeit und Kontrolle.
|
|
||||||
- Die Ermittlung ist wichtig, aber nicht die Hauptstruktur. Krimi-Logik darf das Projekt nicht dominieren.
|
|
||||||
- Das Verhältnis Valerie/Amalia ist ein Kern des Projekts, nicht Nebenhandlung.
|
|
||||||
- Zentrale Frage: Ist Valerie frei, oder trägt sie nur eine bessere Maske?
|
|
||||||
- Der Horror entsteht durch Unstimmigkeit, Bedeutungslücken, alte Orte, soziale Formen und verdrängte Wahrheit.
|
|
||||||
- Die moderne Welt der Bahn, des Hofes und der Verwaltung trifft auf ältere lokale Ordnungen, verdrängt sie aber nicht vollständig.
|
|
||||||
|
|
||||||
Diese Creative-Direction-Entscheidungen müssen im endgültigen Dokument ausführlicher mit Beispielen, nicht nur als Liste, ausgearbeitet werden.
|
|
||||||
|
|
||||||
## 0.4 Materialinventar: Setting und historische Welt
|
|
||||||
|
|
||||||
Dauerhaft zu dokumentieren:
|
|
||||||
|
|
||||||
- Jahr: 1898. Frühere 1889-Fassungen sind überholt.
|
|
||||||
- Mögliche offene Datierung: vor/nach Sisis Ermordung, kurz vor/nach Kaiserjubiläum, kalter Spätherbst oder Winter.
|
|
||||||
- Wien als Hof-, Amts-, Kultur- und Modernezentrum.
|
|
||||||
- Obersteiermark als geographischer und kultureller Zielraum.
|
|
||||||
- Eibenreith als Dorf, nicht zu glossieren, Bedeutung im Spiel entfalten.
|
|
||||||
- Hohenreith als Jagdhaus/Jagdschlösschen, nicht Hauptsitz der Familie.
|
|
||||||
- Eibenklause, Eibenbrunn/Frauenquelle als ältere mögliche Ortskerne.
|
|
||||||
- Bahn und Telegraph als etablierte Moderne, nicht als Wundertechnik.
|
|
||||||
- Speisewagen möglich, aber nicht blind als selbstverständlich behandeln.
|
|
||||||
- Bahnhofsrestauration plausibel als soziale und praktische Mittagsoption.
|
|
||||||
- Kirche, Pfarrhof, Dorf, Haus und Dienerschaft als soziale Institutionen.
|
|
||||||
- Menschenzoos / Wiener Sehenswürdigkeiten / Sängerknaben / Riesenrad / Prater etc. sind Hintergrundwissen für Valeries Wienbild, aber nicht automatisch Storyinhalt.
|
|
||||||
|
|
||||||
Offen in diesem Bereich:
|
|
||||||
|
|
||||||
- Genaues Datum.
|
|
||||||
- Jahreszeit endgültig festlegen.
|
|
||||||
- Wie stark historische Großereignisse aktiv in die Handlung eingreifen.
|
|
||||||
- Name der gräflichen Familie.
|
|
||||||
- Exakte Lage und Funktion von Eibenbrunn/Frauenquelle.
|
|
||||||
|
|
||||||
## 0.5 Materialinventar: Literarischer Stil
|
|
||||||
|
|
||||||
Der Style Guide muss deutlich ausführlicher werden als bisher. Er muss enthalten:
|
|
||||||
|
|
||||||
- Stilanker: „Der Zug lässt Wien hinter sich, doch Wien hat dich noch nicht freigegeben.“
|
|
||||||
- Ziel: weniger langatmig, aber nicht maximal kurz; eher zwei Drittel bis drei Viertel der ursprünglichen Textlänge bei höherer Dichte.
|
|
||||||
- Gleiche Menge an Implikation und Atmosphäre in weniger, präziserem Text.
|
|
||||||
- Stärkerer Fokus auf Verkörperung: weiblicher Körper, Kleidung, Korsett, Handschuhe, Kosmetik, Haltung, Hunger, Müdigkeit, Kälte, beobachtet werden.
|
|
||||||
- Gesellschaftskritik mit Witz, aber nicht modern-plakativ.
|
|
||||||
- Lovecraftsche Unstimmigkeit ohne direkte Monster- oder Gruselformeln.
|
|
||||||
- Keine bloße Intelligenzbewunderung: Spieler soll sich wie Valerie fühlen, nicht nur ihren Witz bewundern.
|
|
||||||
- Satzzeichen-Regeln für romanartige Prosa: Doppelpunkt, Semikolon, Gedankenstrich sparsam und rhythmisch sinnvoll.
|
|
||||||
- Glossar als Ort für Belehrung, historische Erklärung und bissige Kommentare.
|
|
||||||
- Haupttext soll stärker Szene und Körper tragen.
|
|
||||||
|
|
||||||
Noch fehlend und im nächsten Durchgang zu ergänzen:
|
|
||||||
|
|
||||||
- konkrete gute/schlechte Stilbeispiele,
|
|
||||||
- Absatzmuster,
|
|
||||||
- Beispielrevisionen,
|
|
||||||
- Regeln für Metaphern,
|
|
||||||
- Regeln für wann Glossar statt Haupttext,
|
|
||||||
- Beispiele für konkrete Unheimlichkeit,
|
|
||||||
- Beispiele für Valeries soziale Wahrnehmung.
|
|
||||||
|
|
||||||
## 0.6 Materialinventar: Dialog- und Choice-Stil
|
|
||||||
|
|
||||||
Dauerhafte Regeln:
|
|
||||||
|
|
||||||
- Dialogchoices beginnen mit einem Verb, das soziale/emotionale Absicht beschreibt.
|
|
||||||
- Verboten bzw. zu vermeiden: Sage, Frage, Antworte, Sprich.
|
|
||||||
- Richtige Richtung: Wahre Form, Prüfe, Entwaffne, Verplappere dich, Benenne, Schone, Trotze, Reize, Präzisiere, Zügle dich.
|
|
||||||
- Jede Dialogzeile soll sozial gelesen werden: Was tut Valerie mit diesem Satz?
|
|
||||||
- Nicht nur relevant, was sie sagt, sondern zu wem sie es sagt.
|
|
||||||
- Verbindliche Dialogchoice-Form: `[__%Verb% %Figur%__: „%gesprochene Zeile%“]`, sofern die Choice eine tatsächlich gesprochene Zeile darstellt.
|
|
||||||
- Reaktion muss Wirkung zeigen: Nähe, Distanz, Kontrolle, Irritation, Vertrauen, Öffentlichkeit.
|
|
||||||
- Valeries gesprochene Zeilen sollen nicht automatisch im Fließtext stehen, wenn der Spieler an dieser Stelle wählen soll.
|
|
||||||
|
|
||||||
Noch auszubauen:
|
|
||||||
|
|
||||||
- Verb-Lexikon pro Route.
|
|
||||||
- Beispielchoices pro Route.
|
|
||||||
- Beispiel einer schlechten neutralen Dialogchoice und ihrer Überarbeitung.
|
|
||||||
- Reaktionsmuster wichtiger Figuren auf jede Route.
|
|
||||||
|
|
||||||
## 0.7 Materialinventar: Valerie-Routen
|
|
||||||
|
|
||||||
Sechs Routen sind gesetzt:
|
|
||||||
|
|
||||||
1. Fassung / `route_composure`
|
|
||||||
2. Detective / `route_detective`
|
|
||||||
3. Lover / `route_lover`
|
|
||||||
4. Sapphic / `route_sapphic`
|
|
||||||
5. Careless / `route_careless`
|
|
||||||
6. Eccentric / `route_eccentric`
|
|
||||||
|
|
||||||
Wichtige Korrektur: Fassung ist nicht Default, sondern gleichberechtigte Route.
|
|
||||||
|
|
||||||
Jede Route braucht im endgültigen Dokument:
|
|
||||||
|
|
||||||
- Definition,
|
|
||||||
- psychologische Grundlage,
|
|
||||||
- soziale Funktion,
|
|
||||||
- typische Verben,
|
|
||||||
- typische Sätze,
|
|
||||||
- typische Risiken,
|
|
||||||
- mögliche Wirkung auf Viktor,
|
|
||||||
- mögliche Wirkung auf Amalia,
|
|
||||||
- mögliche Wirkung auf Haus, Dorf und Dienerschaft,
|
|
||||||
- typische Relationship-Updates,
|
|
||||||
- gute und schlechte Beispiele.
|
|
||||||
|
|
||||||
## 0.8 Materialinventar: Figuren
|
|
||||||
|
|
||||||
### Valerie
|
|
||||||
|
|
||||||
Gesetzt:
|
|
||||||
|
|
||||||
- junge Frau aus Wien / Wien-naher Sphäre,
|
|
||||||
- offizieller diskreter Auftrag,
|
|
||||||
- Ruf als Medium oder spiritistisch sensible Frau,
|
|
||||||
- Masken als Überlebens- und Arbeitsstrategie,
|
|
||||||
- nicht einfach frei,
|
|
||||||
- Herkunft und Erscheinung werden im Intro definiert,
|
|
||||||
- kanonischer Beispielname: Valerie Eleonore Josepha Freiin von Rauhenfels.
|
|
||||||
|
|
||||||
Noch ausführlicher zu schreiben:
|
|
||||||
|
|
||||||
- ihre Angst,
|
|
||||||
- ihr Wunsch,
|
|
||||||
- ihre Beziehung zu Wahrheit und Wirkung,
|
|
||||||
- ihr Verhältnis zu Weiblichkeit,
|
|
||||||
- ihre Beziehung zur Kabinettskanzlei Seiner Majestät,
|
|
||||||
- ihr innerer Widerspruch zwischen Kontrolle und Sehnsucht nach Angenommensein.
|
|
||||||
|
|
||||||
### Amalia
|
|
||||||
|
|
||||||
Gesetzt:
|
|
||||||
|
|
||||||
- Tochter der gräflichen Familie,
|
|
||||||
- Erwachsene sprechen für sie,
|
|
||||||
- erste private Szene ist kein Krimi-Interview,
|
|
||||||
- sie bewundert Valerie als junge Frau mit scheinbarer Freiheit, nicht wegen Eisenbahnfaszination,
|
|
||||||
- sie ist Kern des Beziehungsdramas.
|
|
||||||
|
|
||||||
Noch auszubauen:
|
|
||||||
|
|
||||||
- konkretes Alter,
|
|
||||||
- Stimme,
|
|
||||||
- erste Erscheinung,
|
|
||||||
- Verhältnis zu Vater/Mutter/Tante,
|
|
||||||
- Verhältnis zur alten Macht,
|
|
||||||
- was sie weiß,
|
|
||||||
- was sie will,
|
|
||||||
- was sie an Valerie missversteht,
|
|
||||||
- wie sie auf jede Route reagiert.
|
|
||||||
|
|
||||||
### Viktor
|
|
||||||
|
|
||||||
Gesetzt:
|
|
||||||
|
|
||||||
- voller Name und Dienstgrad: **Rittmeister Viktor Alois Nowak**,
|
|
||||||
- offizieller Sekretär / Begleiter, faktisch auch Aufsicht und Sicherheitsmensch,
|
|
||||||
- zivil gekleidet, aber militärisch/amtlich geprägt,
|
|
||||||
- kontrollierend, aber nicht platt feindselig,
|
|
||||||
- Startwerte: `viktor_closed = 1`,
|
|
||||||
- mögliche Entwicklung zu Respekt, Sorge, Irritation, Kontrollverlust, Verbündetenrolle.
|
|
||||||
|
|
||||||
Noch auszubauen:
|
|
||||||
|
|
||||||
- konkrete Biographie,
|
|
||||||
- Aussehen in fünf möglichen Varianten oder finale Auswahl,
|
|
||||||
- Sprachstil,
|
|
||||||
- Grenzen seiner Loyalität,
|
|
||||||
- Reaktionsmatrix auf Valeries Routen.
|
|
||||||
|
|
||||||
### Hohenreith-Familie
|
|
||||||
|
|
||||||
Gesetzt:
|
|
||||||
|
|
||||||
- gräflich,
|
|
||||||
- Name noch offen,
|
|
||||||
- „von Schönberg“ verworfen,
|
|
||||||
- Jagdhaus ist nicht Hauptsitz.
|
|
||||||
|
|
||||||
Noch offen:
|
|
||||||
|
|
||||||
- Familienname,
|
|
||||||
- Graf,
|
|
||||||
- Gräfin/Mutterstatus,
|
|
||||||
- Tante/Hausdame,
|
|
||||||
- Geschwister,
|
|
||||||
- wer über Amalia spricht,
|
|
||||||
- wer schweigt,
|
|
||||||
- wer das Haus kontrolliert.
|
|
||||||
|
|
||||||
### Dorf, Dienerschaft, Butler
|
|
||||||
|
|
||||||
Gesetzt:
|
|
||||||
|
|
||||||
- nicht Kulisse,
|
|
||||||
- soziale Beobachtungs- und Erinnerungssysteme,
|
|
||||||
- Butler spricht am Intro-Ende immer: „Willkommen in Hohenreith.“
|
|
||||||
|
|
||||||
Noch auszubauen:
|
|
||||||
|
|
||||||
- Butlerprofil,
|
|
||||||
- Kutscherprofil,
|
|
||||||
- wichtige Diener,
|
|
||||||
- Pfarrer,
|
|
||||||
- Dorfrollen,
|
|
||||||
- soziale Netzwerke.
|
|
||||||
|
|
||||||
## 0.9 Materialinventar: Systeme aus Referenztalks
|
|
||||||
|
|
||||||
Zu bewahren:
|
|
||||||
|
|
||||||
- John-Ingold-/Inkle-Denken: Encounters, State Machines, dynamische Autorensysteme.
|
|
||||||
- Heaven’s Vault: vier Buckets, vorconditioned Content, der dynamisch aufsteigen kann.
|
|
||||||
- Wichtig: Episode- und Game-Buckets müssen breit sein dürfen. Lokale Vorfilterung darf diesen Kern nicht zerstören.
|
|
||||||
- Engine filtert Präsentation, Ink authorisiert Verfügbarkeit.
|
|
||||||
- Ink kann `*`-Choices nach Wahl ausblenden, aber nicht „offered but not chosen“ tracken.
|
|
||||||
- Choice-Tags müssen für Engine vor Auswahl lesbar sein.
|
|
||||||
- Keine Side Effects beim Choice-Sammeln.
|
|
||||||
- State Trees mit Ink-`LIST`s, nicht externe Engine-States.
|
|
||||||
- Relationship-Matrix bleibt in Ink, trotz vieler Variablen.
|
|
||||||
|
|
||||||
Noch auszubauen:
|
|
||||||
|
|
||||||
- konkrete Episode-Templates,
|
|
||||||
- Bucket-Provider-Templates,
|
|
||||||
- Engine-Auswahlalgorithmus,
|
|
||||||
- Debug-Anforderungen,
|
|
||||||
- State-Tree-Beispiele,
|
|
||||||
- Relationship-Beispiele.
|
|
||||||
|
|
||||||
## 0.10 Materialinventar: Ink-Architektur
|
|
||||||
|
|
||||||
Gesetzt:
|
|
||||||
|
|
||||||
- `Eibenreith.ink` ist Root/Index/Referenzdatei.
|
|
||||||
- Hauptdatei enthält LISTs, Includes, Index, Entry Divert; keine Prosa, keine normalen VARs, keine Funktionen.
|
|
||||||
- `eibenreith_00_helpers.ink` enthält globale Variablen und alle Helper-Funktionen, inklusive `rel_*`.
|
|
||||||
- `eibenreith_00_character_relationships.ink` enthält nur Relationship-Variablen und Startwerte.
|
|
||||||
- Kapiteldateien enthalten Szene, Prosa, Choices, lokale Struktur.
|
|
||||||
- `LIST`s modellieren semantische States oder kontrollierte Vokabulare, keine redundanten Knot-Besuche.
|
|
||||||
- Character-Generation-Variablen liegen in Helpers, nicht Kapitel 01.
|
|
||||||
- Relationship-Helper nutzen `ref`, keine String-IDs.
|
|
||||||
- Ink hat keine optionalen Parameter; daher `_by`-Varianten.
|
|
||||||
- `#score` als ein Tag mit Gesamttext.
|
|
||||||
- gültiges Else-Format in Ink beachten.
|
|
||||||
|
|
||||||
Noch auszubauen:
|
|
||||||
|
|
||||||
- vollständige Dateistruktur,
|
|
||||||
- Naming-Conventions,
|
|
||||||
- Beispielcode für Episode, Traversal, Bucket, Score,
|
|
||||||
- Syntax-Fallen,
|
|
||||||
- Tests.
|
|
||||||
|
|
||||||
## 0.11 Materialinventar: Intro / Anreise
|
|
||||||
|
|
||||||
Gesetzt:
|
|
||||||
|
|
||||||
- Intro ist Anwendung der Tagesstruktur.
|
|
||||||
- Vormittag: Zug.
|
|
||||||
- Mittag: Bahnhof / Umstieg / Mahlzeit.
|
|
||||||
- Nachmittag: Kutsche / Wegzeichen / Dorfentscheidung.
|
|
||||||
- Abend: Ankunft Hohenreith / Butler / Score / Übergang Dinner.
|
|
||||||
|
|
||||||
Zug:
|
|
||||||
|
|
||||||
- erstes begehbares Ambiente,
|
|
||||||
- Traversal-Tutorial,
|
|
||||||
- Charaktergenerierung an Orten,
|
|
||||||
- Stand / Herkunft als erste Pflichtentscheidung im Abteil,
|
|
||||||
- Name / Titel / Anrede durch amtliche Erfassung, Schaffner- oder Viktor-Formularmoment,
|
|
||||||
- Missionslage durch Hohenreith-Hilfsgesuch und Viktors Memorandum,
|
|
||||||
- Geistlichenabteil = Religiosität,
|
|
||||||
- Tunnelspiegel = Körper/Kleidung,
|
|
||||||
- Waschraum = Gesicht/Frisur,
|
|
||||||
- Viktor-Gespräch = Auftrag, Memorandum, Routen-/Reaktionstutorial,
|
|
||||||
- Speisewagen/Bahnhof/Imbiss = Mittagsplanung.
|
|
||||||
|
|
||||||
Bahnhof:
|
|
||||||
|
|
||||||
- Noon-Slot,
|
|
||||||
- Mahlzeitenauswertung,
|
|
||||||
- Gepäck als soziale Autobiographie,
|
|
||||||
- Manierenpuzzle mit Gepäckträger/Kutscher/Viktor.
|
|
||||||
|
|
||||||
Kutsche:
|
|
||||||
|
|
||||||
- Übergang in lokale Ordnung,
|
|
||||||
- Wegzeichen/Statue = übernatürliche Begabung,
|
|
||||||
- Entscheidung direkt Hohenreith oder Dorf.
|
|
||||||
|
|
||||||
Dorf/Hohenreith:
|
|
||||||
|
|
||||||
- drei Anreisevarianten:
|
|
||||||
1. direkt mit Kutsche,
|
|
||||||
2. Dorfdetour + Kutsche,
|
|
||||||
3. Dorfdetour + Kutsche voraus + zu Fuß.
|
|
||||||
- Alle enden mit Butler-Satz.
|
|
||||||
- Bei Kutschenankunft erneute Ausstiegsszene.
|
|
||||||
- Score beschreibt erschaffene Figur.
|
|
||||||
|
|
||||||
Noch auszubauen:
|
|
||||||
|
|
||||||
- Beat Sheet des Intros,
|
|
||||||
- Player Objectives je Abschnitt,
|
|
||||||
- mögliche spätere Callbacks der drei Anreisevarianten,
|
|
||||||
- Dinner-Folgen.
|
|
||||||
|
|
||||||
## 0.12 Materialinventar: UI, Tutorial, Tags
|
|
||||||
|
|
||||||
Gesetzt:
|
|
||||||
|
|
||||||
- Entscheidungen links.
|
|
||||||
- Auswahl per Maus oder angezeigter Taste.
|
|
||||||
- Spiel erfolgt über Entscheidungen.
|
|
||||||
- Buchstabenkürzel können Textadventure-Konventionen folgen.
|
|
||||||
- Begriff „Entscheidungen“ ist Standard; „Wahlen“ verworfen.
|
|
||||||
- Bei Dialog ist relevant, was gesagt wird und zu wem.
|
|
||||||
- `#gloss` am Absatzende.
|
|
||||||
- `#alert` für Tutorial/UI-Hinweise.
|
|
||||||
- `#score` für Endauswertung.
|
|
||||||
- Asset-Tags unterstützen, aber tragen nicht die Szene.
|
|
||||||
|
|
||||||
Noch auszubauen:
|
|
||||||
|
|
||||||
- UI-Writing-Styleguide,
|
|
||||||
- Tag-Spec für Engine,
|
|
||||||
- Score-Rendering,
|
|
||||||
- Glossar-Parsing,
|
|
||||||
- Choice-Grouping.
|
|
||||||
|
|
||||||
## 0.13 Materialinventar: Offene Entscheidungen
|
|
||||||
|
|
||||||
Aktuell offen:
|
|
||||||
|
|
||||||
- exaktes Datum,
|
|
||||||
- Jahreszeit endgültig,
|
|
||||||
- Sisi/Kaiserjubiläum,
|
|
||||||
- Name der gräflichen Familie,
|
|
||||||
- Hohenreith-Familienstruktur,
|
|
||||||
- Amalias Kernkonflikt,
|
|
||||||
- Natur von Eibenbrunn/Frauenquelle,
|
|
||||||
- objektiver Status des Übernatürlichen,
|
|
||||||
- Sprachstil der Hohenreith-Familie,
|
|
||||||
- Kapitel 05 Dinner,
|
|
||||||
- Engine-Details für Choice-Tags, Offered-History, Glossar, Score,
|
|
||||||
- zusätzlicher State für Fußankunft,
|
|
||||||
- Umbenennung Kapitel-03-Datei.
|
|
||||||
|
|
||||||
## 0.14 Materialinventar: Verworfenes
|
|
||||||
|
|
||||||
Nicht wieder aufnehmen:
|
|
||||||
|
|
||||||
- 1889 als aktuelles Spieljahr,
|
|
||||||
- Charaktergenerierung als lineares Weave,
|
|
||||||
- Zug als bloß statisches Abteil,
|
|
||||||
- Übernatürliche Begabung im Dorf als Pflichtauslöser,
|
|
||||||
- Fassung als unsichtbarer Default,
|
|
||||||
- Krimi als Hauptstruktur,
|
|
||||||
- Amalias Faszination primär über Eisenbahn/Technik,
|
|
||||||
- Relationship-Matrix als engine-seitiges String-Dictionary,
|
|
||||||
- State-Listen für Knot-Besuche,
|
|
||||||
- Glossare in Choice-Texten,
|
|
||||||
- alte `{gloss:term|entry}`-Syntax,
|
|
||||||
- HTML-Glossartags,
|
|
||||||
- mehrere `#score`-Tags am Intro-Ende.
|
|
||||||
|
|
||||||
## 0.15 Durchgang 2: Widerspruchsauflösung
|
|
||||||
|
|
||||||
Dieser Durchgang entscheidet, welche Fassungen als gültig gelten, welche älteren Fassungen überschrieben sind und welche Punkte weiterhin offen bleiben. Er dient als Sperrschicht gegen Rückfälle in ältere Entwürfe.
|
|
||||||
|
|
||||||
### 0.15.1 Grundregel
|
|
||||||
|
|
||||||
Wenn mehrere Fassungen existieren, gilt folgende Priorität:
|
|
||||||
|
|
||||||
1. spätere ausdrückliche Autorentscheidung,
|
|
||||||
2. spätere gemeinsam akzeptierte Arbeitsfassung,
|
|
||||||
3. funktionierende aktuelle Ink-Architektur,
|
|
||||||
4. frühere Recherche oder Assistentenvorschlag nur, wenn nicht widersprochen.
|
|
||||||
|
|
||||||
Assistentenvorschläge gelten nie automatisch als endgültig. Wenn der Autor einen Vorschlag korrigiert hat, ist die Korrektur verbindlich.
|
|
||||||
|
|
||||||
### 0.15.2 Spieljahr
|
|
||||||
|
|
||||||
**Gültig:** 1898.
|
|
||||||
**Verworfen:** 1889 als aktuelles Spieljahr.
|
|
||||||
|
|
||||||
1889 bleibt nur für historische Vergleichsfragen relevant. Alle konkreten Ereignisse, Zeitungen, Bahnverbindungen, gesellschaftlichen Details und Wiener Bezugspunkte müssen auf 1898 geprüft werden, bevor sie als Storyinhalt verwendet werden.
|
|
||||||
|
|
||||||
**Offen:** Exaktes Datum, Jahreszeit, Verhältnis zu Sisis Ermordung und Kaiserjubiläum.
|
|
||||||
|
|
||||||
### 0.15.3 Genregewichtung
|
|
||||||
|
|
||||||
**Gültig:** psychologisches Beziehungsdrama mit Horror- und Mystery-Elementen.
|
|
||||||
**Verworfen:** Krimi als Hauptstruktur.
|
|
||||||
|
|
||||||
Ermittlungslogik darf vorkommen, aber sie darf das Spiel nicht dominieren. Wenn eine Szene wahlweise als Spurensuche oder als Beziehungs-/Rollenszene verstanden werden kann, ist die zweite Lesart vorrangig. Amalia ist kein Fall. Das Dorf ist nicht nur Informationsquelle. Viktor ist nicht nur Ermittlungsassistent oder Hindernis.
|
|
||||||
|
|
||||||
### 0.15.4 Valerie
|
|
||||||
|
|
||||||
**Gültig:** Valerie ist eine junge Frau, die Masken beherrscht, weil sie in ihrer Welt ohne Masken weniger Handlungsspielraum hätte.
|
|
||||||
**Verworfen:** Valerie als moderne, offen emanzipierte Ermittlerin ohne historische Verkörperung.
|
|
||||||
|
|
||||||
Valerie darf scharf, berechnend, manipulativ und witzig sein, aber diese Eigenschaften müssen aus ihrer historischen Lage entstehen. Sie soll nicht einfach die Stimme einer modernen Autorposition tragen.
|
|
||||||
|
|
||||||
**Gültig als Default-/Beispielname:** Valerie Eleonore Josepha Freiin von Rauhenfels.
|
|
||||||
**Gültig als Spielerstruktur:** Name, Herkunft und Erscheinung sind im Intro wählbar.
|
|
||||||
|
|
||||||
### 0.15.5 Charaktergenerierung
|
|
||||||
|
|
||||||
**Gültig:** Charaktergenerierung entsteht durch Orte und Handlungen im Zug und auf der Anreise.
|
|
||||||
**Verworfen:** vordefiniertes lineares Charaktergenerierungs-Weave.
|
|
||||||
|
|
||||||
Gültige Zuordnung:
|
|
||||||
|
|
||||||
- Abteil / amtliche Erfassung: Stand, Herkunft, Name, Titel, Anrede.
|
|
||||||
- Hohenreith-Hilfsgesuch und Viktors Memorandum: Auftrag, Informationsgefälle, Glaube ans Übernatürliche.
|
|
||||||
- Geistlichenabteil: Religiosität.
|
|
||||||
- Tunnelspiegel: Körper, Statur, Kleidung.
|
|
||||||
- Waschraum: Gesicht, Haar, Frisur.
|
|
||||||
- Wegzeichen / Statue vor der Nachmittagsgabelung: übernatürliche Begabung.
|
|
||||||
- Viktor-Gespräch oder Ausrede zum Waschraum: erste Präsentation der sechs Routen als Reaktionssystem, nicht als Charaktergenerierung.
|
|
||||||
|
|
||||||
**Verworfen:** übernatürliche Begabung im Dorf als Pflichtauslöser, weil die direkte Hohenreith-Route das Dorf überspringen kann.
|
|
||||||
|
|
||||||
**Verworfen:** Schreiben im Ridikül als feste Quelle für Name / Herkunft / Auftrag. Wenn Valerie ein Ridikül oder Notizbuch trägt, ist es persönlicher Besitz, nicht die Hauptakte. Viktor kontrolliert als Sekretär / Begleiter die offiziellen Papiere.
|
|
||||||
|
|
||||||
### 0.15.6 Valeries sechs Routen
|
|
||||||
|
|
||||||
**Gültig:** sechs gleichberechtigte Routen:
|
|
||||||
|
|
||||||
- Fassung,
|
|
||||||
- Detective,
|
|
||||||
- Lover,
|
|
||||||
- Sapphic,
|
|
||||||
- Careless,
|
|
||||||
- Eccentric.
|
|
||||||
|
|
||||||
**Verworfen:** Fassung als unsichtbarer Default oder Ideal, von dem andere Routen nur Abweichungen sind.
|
|
||||||
|
|
||||||
Fassung ist eine eigene Wahlstrategie mit eigenen Kosten. Sie darf nicht automatisch als „richtige“ Valerie behandelt werden.
|
|
||||||
|
|
||||||
### 0.15.7 Amalia
|
|
||||||
|
|
||||||
**Gültig:** Amalia ist Zentrum des Beziehungsdramas.
|
|
||||||
**Verworfen:** Amalia als bloßer Fall, Hinweisgeberin, Opferobjekt oder Krimi-NPC.
|
|
||||||
|
|
||||||
Amalia bewundert Valerie nicht primär wegen Eisenbahn oder technischer Moderne. Diese Deutung ist verworfen, weil Frauen ihres Standes die Eisenbahn wahrscheinlich bereits kennen. Amalias Faszination liegt in Valeries scheinbarer weiblicher Freiheit, Verantwortung, Hofnähe, Mobilität und mystischem Ruf.
|
|
||||||
|
|
||||||
Die erste private Amalia-Szene muss aus dieser Beziehungsspannung entstehen, nicht aus einem Ermittlungsinterview.
|
|
||||||
|
|
||||||
### 0.15.8 Viktor
|
|
||||||
|
|
||||||
**Gültig:** Viktor ist offizieller Sekretär/Begleiter nach außen und Aufsicht/Sicherheitsmensch nach innen. Er ist kontrollierend, aber nicht eindimensional feindlich.
|
|
||||||
**Verworfen:** Viktor als bloßer Bodyguard, Schurke oder expositorischer Stichwortgeber.
|
|
||||||
|
|
||||||
Seine Funktion ist Spannung zwischen Schutz, Kontrolle, Professionalität und möglicher Loyalität. Er reagiert besonders stark auf Valeries Formbrüche, Kontrollverluste und Kompetenzbeweise.
|
|
||||||
|
|
||||||
### 0.15.9 Hohenreith-Familie
|
|
||||||
|
|
||||||
**Gültig:** gräfliche Familie, Jagdhaus Hohenreith als Jagdresidenz, nicht Hauptsitz.
|
|
||||||
**Verworfen:** der frühere Name „von Schönberg“.
|
|
||||||
|
|
||||||
**Offen:** endgültiger Familienname, Familienstruktur, Mutter/Tante/Frauenrolle im Haus, Verhältnis Amalias zu Vater und Haushalt.
|
|
||||||
|
|
||||||
### 0.15.10 Eibenreith / Hohenreith / alte Namen
|
|
||||||
|
|
||||||
**Gültig:**
|
|
||||||
|
|
||||||
- Eibenreith: Dorf.
|
|
||||||
- Eibenreither Graben: Seitental.
|
|
||||||
- Jagdhaus Hohenreith: moderner Landsitzname.
|
|
||||||
- Eibenklause: älterer lokaler Name.
|
|
||||||
- Eibenbrunn / Frauenquelle: älterer Kern, Quelle oder verbotener Ort.
|
|
||||||
|
|
||||||
**Gültig:** Eibenreith und Hohenreith werden im Intro nicht glossiert, weil ihre Bedeutung im Spiel offenbart werden soll.
|
|
||||||
|
|
||||||
### 0.15.11 Anreise und Intro
|
|
||||||
|
|
||||||
**Gültig:** Die Anreise ist bereits nach Tagesstruktur gegliedert.
|
|
||||||
|
|
||||||
- Vormittag: Zug.
|
|
||||||
- Mittag: Bahnhof / Umstieg / Mahlzeit.
|
|
||||||
- Nachmittag: Kutschenfahrt / Wegzeichen / Dorfentscheidung.
|
|
||||||
- Abend: Hohenreith-Empfang / Score / Übergang zum Dinner.
|
|
||||||
|
|
||||||
**Gültig:** drei finale Anreisevarianten:
|
|
||||||
|
|
||||||
1. direkt mit Kutsche nach Hohenreith,
|
|
||||||
2. Dorfdetour und dann mit Kutsche weiter,
|
|
||||||
3. Dorfdetour, Kutsche mit Gepäck und Nachricht voraus, Ankunft zu Fuß.
|
|
||||||
|
|
||||||
Alle enden mit dem Butler-Satz:
|
|
||||||
|
|
||||||
> Willkommen in Hohenreith.
|
|
||||||
|
|
||||||
**Gültig:** Bei Ankunft mit Kutsche gibt es eine Ausstiegsszene. Bei Fußankunft nicht; dort ist die soziale Wirkung der fehlenden Kutsche entscheidend.
|
|
||||||
|
|
||||||
### 0.15.12 Glossarsystem
|
|
||||||
|
|
||||||
**Gültig:**
|
|
||||||
|
|
||||||
```ink
|
|
||||||
#gloss[Term](Erklärung)
|
|
||||||
```
|
|
||||||
|
|
||||||
am Ende des Absatzes.
|
|
||||||
|
|
||||||
**Verworfen:**
|
|
||||||
|
|
||||||
- `{gloss:term|entry}`,
|
|
||||||
- HTML-artige `<gloss>`-Tags,
|
|
||||||
- Glossar-Tags in Choice-Texten.
|
|
||||||
|
|
||||||
Glossare dienen historischer und linguistischer Aufklärung in Valeries bissigem Lexikonstil. Sie dürfen optional ausführlicher sein. Sie dürfen soziale Puzzle-Lösungen nicht verraten.
|
|
||||||
|
|
||||||
### 0.15.13 Score
|
|
||||||
|
|
||||||
**Gültig:** Ein einziges `#score[...]`-Tag gibt den vollständigen Endtext aus.
|
|
||||||
**Verworfen:** mehrere einzelne `#score`-Tags.
|
|
||||||
|
|
||||||
Der Score soll literarisch die erschaffene Valerie zusammenfassen, nicht bloß technische Werte ausgeben.
|
|
||||||
|
|
||||||
### 0.15.14 Ink-Architektur
|
|
||||||
|
|
||||||
**Gültig:**
|
|
||||||
|
|
||||||
- `Eibenreith.ink` ist Root/Index/Referenzdatei.
|
|
||||||
- `eibenreith_00_helpers.ink` enthält globale Variablen und Helper-Funktionen.
|
|
||||||
- `eibenreith_00_character_relationships.ink` enthält nur Relationship-Variablen und Startwerte.
|
|
||||||
- Kapiteldateien enthalten Prosa, Knots, Choices und lokale Struktur.
|
|
||||||
|
|
||||||
**Gültig:** Relationship-Helfer gehören in die Helper-Datei, nicht in die Character-Relationships-Datei.
|
|
||||||
|
|
||||||
**Gültig:** Charaktergenerierungsvariablen sind global in der Helper-Datei, weil spätere Kapitel sie brauchen.
|
|
||||||
|
|
||||||
### 0.15.15 Ink-State-Modell
|
|
||||||
|
|
||||||
**Gültig:** `LIST`s modellieren semantische States und kontrollierte Vokabulare.
|
|
||||||
**Verworfen:** `LIST`s, die nur Knot- oder Choice-Besuche duplizieren.
|
|
||||||
|
|
||||||
Für reine Besuchsfragen werden Ink-Readcounts / Knotnamen verwendet. `TURNS_SINCE()` ist nur für Recency oder aktuellen Chunk sinnvoll, nicht als Standardprüfung für „gesehen“.
|
|
||||||
|
|
||||||
### 0.15.16 Relationship-Matrix
|
|
||||||
|
|
||||||
**Gültig:** Relationship-Matrix bleibt Ink-intern.
|
|
||||||
**Verworfen:** engine-seitiges dynamisches String-Dictionary als Hauptlösung.
|
|
||||||
|
|
||||||
Jede wichtige Figur erhält explizite Variablen. Das ist nicht elegant, aber transparent und ink-nah. Helper-Funktionen arbeiten mit `ref`-Parametern.
|
|
||||||
|
|
||||||
### 0.15.17 Bucket-System
|
|
||||||
|
|
||||||
**Gültig:** Episode- und Game-Buckets dürfen breit sein.
|
|
||||||
**Verworfen:** Architektur, die Episode/Game-Content zu stark lokal vorfiltert und damit das Heaven’s-Vault-Prinzip zerstört.
|
|
||||||
|
|
||||||
Ink stellt gültige Choices bereit. Die Engine filtert sichtbare Choices nach Tags, Priorität, Bucket, Wiederholungsregeln und Offered-History.
|
|
||||||
|
|
||||||
### 0.15.18 Tutorial- und UI-Sprache
|
|
||||||
|
|
||||||
**Gültig:** Standardbegriff ist „Entscheidungen“.
|
|
||||||
**Verworfen:** „Wahlen“ als Standardbegriff für Choices.
|
|
||||||
|
|
||||||
Tutorialtexte müssen präzise, stilistisch sauber und nicht umgangssprachlich sein. Sie müssen linke Choice-Seite, Maus-/Tastenwahl, Entscheidung als Grundmechanik und Textadventure-Kürzel erklären.
|
|
||||||
|
|
||||||
## 0.16 Durchgang 2: endgültige Zielstruktur des Dokuments
|
|
||||||
|
|
||||||
Die finale Arbeitsbibel wird in folgende Großteile gegliedert. Diese Struktur überschreibt die bisherige kürzere Gliederung und dient als Ausbauplan für die nächsten Durchgänge.
|
|
||||||
|
|
||||||
### Teil I: Executive Reference
|
|
||||||
|
|
||||||
Kurzreferenz für schnelle Orientierung:
|
|
||||||
|
|
||||||
- Projektformel,
|
|
||||||
- Creative Pillars,
|
|
||||||
- zentrale Negativregeln,
|
|
||||||
- wichtigste offene Entscheidungen,
|
|
||||||
- Dateistruktur,
|
|
||||||
- Kernsysteme.
|
|
||||||
|
|
||||||
Dieser Teil darf kurz sein, aber er ersetzt nicht die Detailteile.
|
|
||||||
|
|
||||||
### Teil II: Creative Bible
|
|
||||||
|
|
||||||
Ausführliche Vision:
|
|
||||||
|
|
||||||
- Genre,
|
|
||||||
- Ton,
|
|
||||||
- Themen,
|
|
||||||
- zentrale emotionale Fragen,
|
|
||||||
- Verhältnis von Historie, Horror und Beziehungsdrama,
|
|
||||||
- Was das Spiel nicht sein darf.
|
|
||||||
|
|
||||||
### Teil III: Literary Style Guide
|
|
||||||
|
|
||||||
Ausführlicher Schreibstil:
|
|
||||||
|
|
||||||
- Prosa-Rhythmus,
|
|
||||||
- Verkörperung,
|
|
||||||
- Valeries Wahrnehmungsfilter,
|
|
||||||
- Gesellschaftskritik,
|
|
||||||
- Horrorprinzipien,
|
|
||||||
- Satzzeichen,
|
|
||||||
- gute und schlechte Beispiele,
|
|
||||||
- Glossar vs. Haupttext,
|
|
||||||
- Beispielrevisionen.
|
|
||||||
|
|
||||||
Dieser Teil ist derzeit am wichtigsten auszubauen, weil er die größte Gefahr für Informationsverlust enthält.
|
|
||||||
|
|
||||||
### Teil IV: Choice & Dialogue Style Guide
|
|
||||||
|
|
||||||
Konventionen für Spielerentscheidungen:
|
|
||||||
|
|
||||||
- Absichtsverben,
|
|
||||||
- verbotene neutrale Verben,
|
|
||||||
- Routenverben,
|
|
||||||
- Dialogreaktionen,
|
|
||||||
- Beispiele pro Route,
|
|
||||||
- NPC-Reaktionsprinzipien.
|
|
||||||
|
|
||||||
### Teil V: Character Bible
|
|
||||||
|
|
||||||
Ausführliche Figurenreferenz:
|
|
||||||
|
|
||||||
- Valerie,
|
|
||||||
- Amalia,
|
|
||||||
- Viktor,
|
|
||||||
- Hohenreith-Familie,
|
|
||||||
- Butler,
|
|
||||||
- Dienerschaft,
|
|
||||||
- Dorfrollen,
|
|
||||||
- Kirche/Pfarrer,
|
|
||||||
- spätere NPCs.
|
|
||||||
|
|
||||||
Jede wichtige Figur braucht: Funktion, Erscheinung, Stimme, Motivation, Angst, Machtmittel, Beziehung zu Valerie, mögliche Relationship-Reaktionen.
|
|
||||||
|
|
||||||
### Teil VI: World & Setting Bible
|
|
||||||
|
|
||||||
Historische und räumliche Welt:
|
|
||||||
|
|
||||||
- 1898,
|
|
||||||
- Wien,
|
|
||||||
- Obersteiermark,
|
|
||||||
- Eibenreith,
|
|
||||||
- Hohenreith,
|
|
||||||
- Eibenklause / Eibenbrunn / Frauenquelle,
|
|
||||||
- Dorf, Kirche, Haus, Bahnhof, Kutsche,
|
|
||||||
- soziale Institutionen,
|
|
||||||
- offene historische Datumsfragen.
|
|
||||||
|
|
||||||
### Teil VII: Narrative Systems Design
|
|
||||||
|
|
||||||
Systeme aus den Referenztalks:
|
|
||||||
|
|
||||||
- Encounters,
|
|
||||||
- State Trees,
|
|
||||||
- Bucket Content,
|
|
||||||
- Cascades/Callbacks/Heuristics,
|
|
||||||
- Relationship Matrix,
|
|
||||||
- Tagesstruktur,
|
|
||||||
- Episodenmodell,
|
|
||||||
- Traversal.
|
|
||||||
|
|
||||||
### Teil VIII: Ink Implementation Guide
|
|
||||||
|
|
||||||
Code-nahe Referenz:
|
|
||||||
|
|
||||||
- Dateistruktur,
|
|
||||||
- Include-Reihenfolge,
|
|
||||||
- Naming-Conventions,
|
|
||||||
- LIST-Konventionen,
|
|
||||||
- VAR-Konventionen,
|
|
||||||
- Helper-Funktionen,
|
|
||||||
- State-Templates,
|
|
||||||
- Relationship-Templates,
|
|
||||||
- Bucket-Templates,
|
|
||||||
- Episode-Templates,
|
|
||||||
- Traversal-Templates,
|
|
||||||
- Score-Template,
|
|
||||||
- Syntax-Fallen,
|
|
||||||
- Teststrategie.
|
|
||||||
|
|
||||||
### Teil IX: UI, Tags, Glossary & Engine Integration
|
|
||||||
|
|
||||||
Engine-naher Designteil:
|
|
||||||
|
|
||||||
- Choice-Tags,
|
|
||||||
- Bucket-Filterung,
|
|
||||||
- Offered-History,
|
|
||||||
- `#alert`,
|
|
||||||
- `#score`,
|
|
||||||
- `#gloss`,
|
|
||||||
- `#music`, `#image`, `#sfx`,
|
|
||||||
- TTS-Verhalten,
|
|
||||||
- Debugging.
|
|
||||||
|
|
||||||
### Teil X: Current Content Specification
|
|
||||||
|
|
||||||
Aktueller Content-Plan:
|
|
||||||
|
|
||||||
- Intro Beat Sheet,
|
|
||||||
- Kapitel 01–04 als Referenzmuster,
|
|
||||||
- Kapitel 05 Dinner als nächster Zielblock,
|
|
||||||
- erste private Amalia-Szene,
|
|
||||||
- geplante Tages- und Episodenstruktur.
|
|
||||||
|
|
||||||
Dieser Teil darf konkrete Kapitel enthalten, aber nicht als Änderungsprotokoll. Er beschreibt Zielzustände und Designfunktionen.
|
|
||||||
|
|
||||||
### Teil XI: Open Decisions, Deprecated Ideas, Decision Log
|
|
||||||
|
|
||||||
Kontrollteil:
|
|
||||||
|
|
||||||
- offene Entscheidungen,
|
|
||||||
- verworfene Ansätze,
|
|
||||||
- echte unaufgelöste Widersprüche,
|
|
||||||
- spätere Designentscheidungen mit Datum/Arbeitsstand.
|
|
||||||
|
|
||||||
## 0.17 Durchgang 2: Qualitätskriterien für die finale Arbeitsbibel
|
|
||||||
|
|
||||||
Die fertige GDD-Fassung ist erst brauchbar, wenn folgende Tests bestanden werden:
|
|
||||||
|
|
||||||
### 0.17.1 Writing-Test
|
|
||||||
|
|
||||||
Eine neue Szene kann aus dem Dokument heraus geschrieben werden, ohne erneut fragen zu müssen:
|
|
||||||
|
|
||||||
- wie Valerie denkt,
|
|
||||||
- wie körperlich der Text sein soll,
|
|
||||||
- wie Choices formuliert werden,
|
|
||||||
- wie Glossare klingen,
|
|
||||||
- wie Horror konkret wird,
|
|
||||||
- wie gesellschaftlicher Subtext funktioniert.
|
|
||||||
|
|
||||||
### 0.17.2 Ink-Test
|
|
||||||
|
|
||||||
Ein neuer Encounter kann aus dem Dokument heraus implementiert werden, ohne erneut klären zu müssen:
|
|
||||||
|
|
||||||
- wohin LISTs gehören,
|
|
||||||
- wohin VARs gehören,
|
|
||||||
- wie State-Reach funktioniert,
|
|
||||||
- wie Relationships upgedatet werden,
|
|
||||||
- wie Choice-Tags aussehen,
|
|
||||||
- wie Bucket-Content geschrieben wird,
|
|
||||||
- wie Traversal und Episode-Start/Ende funktionieren.
|
|
||||||
|
|
||||||
### 0.17.3 Character-Test
|
|
||||||
|
|
||||||
Eine neue Szene mit Valerie, Viktor oder Amalia kann geschrieben werden, ohne die Figuren auf Funktionen zu reduzieren. Das Dokument muss genug über Motivation, Angst, Stimme, soziale Rolle und Reaktionsmuster enthalten.
|
|
||||||
|
|
||||||
### 0.17.4 Continuity-Test
|
|
||||||
|
|
||||||
Das Dokument verhindert Rückfälle in verworfene Fassungen:
|
|
||||||
|
|
||||||
- kein 1889,
|
|
||||||
- keine lineare Charaktererstellung,
|
|
||||||
- keine Krimi-Dominanz,
|
|
||||||
- keine Glossare in Choices,
|
|
||||||
- keine Fassung als Default,
|
|
||||||
- keine redundanten State-Listen,
|
|
||||||
- keine mehrfachen Score-Tags.
|
|
||||||
|
|
||||||
### 0.17.5 Production-Test
|
|
||||||
|
|
||||||
Das Dokument ist lang genug, um keine wichtigen Entscheidungen zu verlieren, aber strukturiert genug, um benutzt zu werden. Es braucht Inhaltslogik, Überschriften, Beispiele, Templates und klare Negativregeln.
|
|
||||||
|
|
||||||
# 1. Projektvision
|
# 1. Projektvision
|
||||||
|
|
||||||
## 1.1 Projektformel
|
## 1.1 Projektformel
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Vendored
+5
@@ -243,6 +243,11 @@ class InkEngine {
|
|||||||
remainingTags.push(tag);
|
remainingTags.push(tag);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!currentText) {
|
||||||
|
previous.tags.push(tag);
|
||||||
|
remainingTags.push(tag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const matchesCurrent = currentText.includes(term);
|
const matchesCurrent = currentText.includes(term);
|
||||||
const matchesPrevious = previousText.includes(term);
|
const matchesPrevious = previousText.includes(term);
|
||||||
if (!matchesCurrent && matchesPrevious) {
|
if (!matchesCurrent && matchesPrevious) {
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -8,11 +8,13 @@ class ChoiceDisplayModule extends BaseModule {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super('choice-display', 'Choice Display');
|
super('choice-display', 'Choice Display');
|
||||||
|
|
||||||
this.dependencies = ['socket-client', 'markup-parser'];
|
this.dependencies = ['socket-client', 'markup-parser', 'layout-renderer'];
|
||||||
this.socketClient = null;
|
this.socketClient = null;
|
||||||
this.markupParser = null;
|
this.markupParser = null;
|
||||||
|
this.layoutRenderer = null;
|
||||||
this.container = null;
|
this.container = null;
|
||||||
this.choices = [];
|
this.choices = [];
|
||||||
|
this.currentGlossaryEntries = [];
|
||||||
this.inputMode = 'none';
|
this.inputMode = 'none';
|
||||||
this.processState = document.documentElement.dataset.processState || 'loading';
|
this.processState = document.documentElement.dataset.processState || 'loading';
|
||||||
this.currentTurnId = 0;
|
this.currentTurnId = 0;
|
||||||
@@ -52,13 +54,15 @@ class ChoiceDisplayModule extends BaseModule {
|
|||||||
'getTagValue',
|
'getTagValue',
|
||||||
'getTag',
|
'getTag',
|
||||||
'getTemplateCell',
|
'getTemplateCell',
|
||||||
'renderChoiceText'
|
'renderChoiceText',
|
||||||
|
'applyChoiceGlossary'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async initialize() {
|
async initialize() {
|
||||||
this.socketClient = this.getModule('socket-client');
|
this.socketClient = this.getModule('socket-client');
|
||||||
this.markupParser = this.getModule('markup-parser');
|
this.markupParser = this.getModule('markup-parser');
|
||||||
|
this.layoutRenderer = this.getModule('layout-renderer');
|
||||||
this.setupContainer();
|
this.setupContainer();
|
||||||
|
|
||||||
this.addEventListener(document, 'story:choices', (event) => {
|
this.addEventListener(document, 'story:choices', (event) => {
|
||||||
@@ -124,7 +128,14 @@ class ChoiceDisplayModule extends BaseModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleChoices(choices) {
|
handleChoices(choices) {
|
||||||
this.choices = this.normalizeChoices(Array.isArray(choices) ? choices : []);
|
const detail = Array.isArray(choices)
|
||||||
|
? { choices, glossaryEntries: [] }
|
||||||
|
: {
|
||||||
|
choices: Array.isArray(choices?.choices) ? choices.choices : [],
|
||||||
|
glossaryEntries: Array.isArray(choices?.glossaryEntries) ? choices.glossaryEntries : []
|
||||||
|
};
|
||||||
|
this.currentGlossaryEntries = detail.glossaryEntries;
|
||||||
|
this.choices = this.normalizeChoices(detail.choices);
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,6 +355,7 @@ class ChoiceDisplayModule extends BaseModule {
|
|||||||
const renderedText = this.renderChoiceText(choice.text);
|
const renderedText = this.renderChoiceText(choice.text);
|
||||||
const displayKey = this.formatChoiceKey(choice.letter);
|
const displayKey = this.formatChoiceKey(choice.letter);
|
||||||
button.innerHTML = `<kbd>${this.escapeHtml(displayKey)}</kbd><span>${choice.optional ? `<em>${renderedText}</em>` : renderedText}</span>`;
|
button.innerHTML = `<kbd>${this.escapeHtml(displayKey)}</kbd><span>${choice.optional ? `<em>${renderedText}</em>` : renderedText}</span>`;
|
||||||
|
this.applyChoiceGlossary(button.querySelector('span'), choice);
|
||||||
button.addEventListener('click', () => this.selectChoice(choice.index));
|
button.addEventListener('click', () => this.selectChoice(choice.index));
|
||||||
item.appendChild(button);
|
item.appendChild(button);
|
||||||
list.appendChild(item);
|
list.appendChild(item);
|
||||||
@@ -470,6 +482,27 @@ class ChoiceDisplayModule extends BaseModule {
|
|||||||
.replace(/_([^_\s][^_]*?)_/g, '<em>$1</em>');
|
.replace(/_([^_\s][^_]*?)_/g, '<em>$1</em>');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applyChoiceGlossary(label, choice = null) {
|
||||||
|
if (!label) return;
|
||||||
|
if (!this.layoutRenderer) {
|
||||||
|
this.layoutRenderer = this.getModule('layout-renderer');
|
||||||
|
}
|
||||||
|
if (!this.markupParser) {
|
||||||
|
this.markupParser = this.getModule('markup-parser');
|
||||||
|
}
|
||||||
|
const choiceEntries = this.markupParser && typeof this.markupParser.extractGlossaryTags === 'function'
|
||||||
|
? this.markupParser.extractGlossaryTags(choice?.tags || [])
|
||||||
|
: [];
|
||||||
|
const entries = [
|
||||||
|
...(Array.isArray(this.currentGlossaryEntries) ? this.currentGlossaryEntries : []),
|
||||||
|
...choiceEntries
|
||||||
|
];
|
||||||
|
if (entries.length === 0) return;
|
||||||
|
if (this.layoutRenderer && typeof this.layoutRenderer.applyGlossaryEntriesToInline === 'function') {
|
||||||
|
this.layoutRenderer.applyGlossaryEntriesToInline(label, entries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
formatChoiceKey(key) {
|
formatChoiceKey(key) {
|
||||||
const value = String(key || '').trim().charAt(0);
|
const value = String(key || '').trim().charAt(0);
|
||||||
return /^[A-Z]$/.test(value) ? value.toLowerCase() : value;
|
return /^[A-Z]$/.test(value) ? value.toLowerCase() : value;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class LayoutRendererModule extends BaseModule {
|
|||||||
'adjustJustification',
|
'adjustJustification',
|
||||||
'decorateInlineWord',
|
'decorateInlineWord',
|
||||||
'applyGlossaryEntries',
|
'applyGlossaryEntries',
|
||||||
|
'applyGlossaryEntriesToInline',
|
||||||
'normalizeGlossaryText',
|
'normalizeGlossaryText',
|
||||||
'normalizeGlossaryToken',
|
'normalizeGlossaryToken',
|
||||||
'normalizeGlossaryCompact',
|
'normalizeGlossaryCompact',
|
||||||
@@ -34,6 +35,7 @@ class LayoutRendererModule extends BaseModule {
|
|||||||
'decorateGlossarySegment',
|
'decorateGlossarySegment',
|
||||||
'decorateGlossaryRange',
|
'decorateGlossaryRange',
|
||||||
'decorateGlossaryWord',
|
'decorateGlossaryWord',
|
||||||
|
'renderGlossaryHtml',
|
||||||
'ensureGlossaryTooltip',
|
'ensureGlossaryTooltip',
|
||||||
'showGlossaryTooltip',
|
'showGlossaryTooltip',
|
||||||
'hideGlossaryTooltip',
|
'hideGlossaryTooltip',
|
||||||
@@ -390,6 +392,81 @@ class LayoutRendererModule extends BaseModule {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applyGlossaryEntriesToInline(container, entries = []) {
|
||||||
|
if (!container || !Array.isArray(entries) || entries.length === 0) return;
|
||||||
|
|
||||||
|
const filter = window.NodeFilter || NodeFilter;
|
||||||
|
entries
|
||||||
|
.filter(entry => entry && entry.term && entry.definition)
|
||||||
|
.forEach(entry => {
|
||||||
|
const walker = document.createTreeWalker(container, filter.SHOW_TEXT, {
|
||||||
|
acceptNode: (node) => {
|
||||||
|
if (!node?.nodeValue?.trim()) return filter.FILTER_REJECT;
|
||||||
|
if (node.parentElement?.closest?.('.story-glossary-word')) return filter.FILTER_REJECT;
|
||||||
|
return filter.FILTER_ACCEPT;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const textNodes = [];
|
||||||
|
let node;
|
||||||
|
while ((node = walker.nextNode())) {
|
||||||
|
textNodes.push(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
textNodes.forEach((textNode) => {
|
||||||
|
if (!textNode.parentNode) return;
|
||||||
|
const original = textNode.nodeValue || '';
|
||||||
|
if (!this.normalizeGlossaryText(original)) return;
|
||||||
|
|
||||||
|
const matches = [];
|
||||||
|
this.buildGlossaryTermPatterns(entry.term).forEach((pattern) => {
|
||||||
|
const matcher = new RegExp(`(^|\\s)(${pattern})(?=\\s|$|[.,;:!?])`, 'giu');
|
||||||
|
let match;
|
||||||
|
while ((match = matcher.exec(original)) !== null) {
|
||||||
|
const matchStart = match.index + match[1].length;
|
||||||
|
const matchEnd = matchStart + match[2].length;
|
||||||
|
if (!matches.some(existing => existing.start === matchStart && existing.end === matchEnd)) {
|
||||||
|
matches.push({ start: matchStart, end: matchEnd });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.buildCompactGlossaryTermPatterns(entry.term).forEach((pattern) => {
|
||||||
|
const compactOriginal = this.normalizeGlossaryCompact(original);
|
||||||
|
if (!compactOriginal) return;
|
||||||
|
const matcher = new RegExp(pattern, 'giu');
|
||||||
|
let match;
|
||||||
|
while ((match = matcher.exec(compactOriginal)) !== null) {
|
||||||
|
if (match.index !== 0 || match[0].length !== compactOriginal.length) continue;
|
||||||
|
if (!matches.some(existing => existing.start === 0 && existing.end === original.length)) {
|
||||||
|
matches.push({ start: 0, end: original.length });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
matches.sort((a, b) => a.start - b.start);
|
||||||
|
if (matches.length === 0) return;
|
||||||
|
|
||||||
|
const parent = textNode.parentNode;
|
||||||
|
let cursor = 0;
|
||||||
|
matches.forEach((match) => {
|
||||||
|
if (match.start < cursor) return;
|
||||||
|
const before = original.slice(cursor, match.start);
|
||||||
|
const matched = original.slice(match.start, match.end);
|
||||||
|
if (before) parent.insertBefore(document.createTextNode(before), textNode);
|
||||||
|
if (matched) {
|
||||||
|
const gloss = document.createElement('span');
|
||||||
|
gloss.textContent = matched;
|
||||||
|
this.decorateGlossaryWord(gloss, entry);
|
||||||
|
parent.insertBefore(gloss, textNode);
|
||||||
|
}
|
||||||
|
cursor = match.end;
|
||||||
|
});
|
||||||
|
|
||||||
|
const after = original.slice(cursor);
|
||||||
|
if (after) parent.insertBefore(document.createTextNode(after), textNode);
|
||||||
|
parent.removeChild(textNode);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
normalizeGlossaryText(text) {
|
normalizeGlossaryText(text) {
|
||||||
return String(text || '')
|
return String(text || '')
|
||||||
.normalize('NFC')
|
.normalize('NFC')
|
||||||
@@ -568,6 +645,17 @@ class LayoutRendererModule extends BaseModule {
|
|||||||
word.addEventListener('blur', this.hideGlossaryTooltip);
|
word.addEventListener('blur', this.hideGlossaryTooltip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderGlossaryHtml(text) {
|
||||||
|
const markupParser = this.getModule('markup-parser');
|
||||||
|
if (markupParser && typeof markupParser.markdownToHtml === 'function') {
|
||||||
|
return markupParser.markdownToHtml(String(text || ''));
|
||||||
|
}
|
||||||
|
return String(text || '')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>');
|
||||||
|
}
|
||||||
|
|
||||||
ensureGlossaryTooltip() {
|
ensureGlossaryTooltip() {
|
||||||
let tooltip = document.getElementById('story_glossary_tooltip');
|
let tooltip = document.getElementById('story_glossary_tooltip');
|
||||||
if (tooltip) return tooltip;
|
if (tooltip) return tooltip;
|
||||||
@@ -598,8 +686,8 @@ class LayoutRendererModule extends BaseModule {
|
|||||||
const tooltip = this.ensureGlossaryTooltip();
|
const tooltip = this.ensureGlossaryTooltip();
|
||||||
const title = tooltip.querySelector('.story-glossary-tooltip-title');
|
const title = tooltip.querySelector('.story-glossary-tooltip-title');
|
||||||
const body = tooltip.querySelector('.story-glossary-tooltip-body');
|
const body = tooltip.querySelector('.story-glossary-tooltip-body');
|
||||||
if (title) title.textContent = word.dataset.glossaryTerm || this.normalizeGlossaryText(word.textContent || '');
|
if (title) title.innerHTML = this.renderGlossaryHtml(word.dataset.glossaryTerm || this.normalizeGlossaryText(word.textContent || ''));
|
||||||
if (body) body.textContent = word.dataset.glossary || '';
|
if (body) body.innerHTML = this.renderGlossaryHtml(word.dataset.glossary || '');
|
||||||
tooltip.dataset.anchorId = word.id || '';
|
tooltip.dataset.anchorId = word.id || '';
|
||||||
tooltip.__anchorElement = word;
|
tooltip.__anchorElement = word;
|
||||||
tooltip.classList.add('visible');
|
tooltip.classList.add('visible');
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ class SocketClientModule extends BaseModule {
|
|||||||
'isTimedCueTag',
|
'isTimedCueTag',
|
||||||
'isRenderMetadataTag',
|
'isRenderMetadataTag',
|
||||||
'cueMarkersFromTags',
|
'cueMarkersFromTags',
|
||||||
|
'collectGlossaryEntriesForTurn',
|
||||||
|
'applyTurnGlossaryEntries',
|
||||||
|
'mergeGlossaryEntries',
|
||||||
'dispatchChoices',
|
'dispatchChoices',
|
||||||
'dispatchInputMode',
|
'dispatchInputMode',
|
||||||
'handleServerError',
|
'handleServerError',
|
||||||
@@ -302,9 +305,11 @@ class SocketClientModule extends BaseModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const turnGlossaryEntries = this.collectGlossaryEntriesForTurn(turnBlocks, globalTags);
|
||||||
|
this.applyTurnGlossaryEntries(turnBlocks, turnGlossaryEntries);
|
||||||
const choices = Array.isArray(data.choices) ? data.choices : [];
|
const choices = Array.isArray(data.choices) ? data.choices : [];
|
||||||
const inputMode = data.inputMode || (choices.length > 0 ? 'choice' : 'none');
|
const inputMode = data.inputMode || (choices.length > 0 ? 'choice' : 'none');
|
||||||
this.dispatchChoices(choices);
|
this.dispatchChoices(choices, turnGlossaryEntries);
|
||||||
this.dispatchInputMode(inputMode);
|
this.dispatchInputMode(inputMode);
|
||||||
|
|
||||||
await this.storeAndQueueBlocks(turnBlocks);
|
await this.storeAndQueueBlocks(turnBlocks);
|
||||||
@@ -336,9 +341,49 @@ class SocketClientModule extends BaseModule {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatchChoices(choices) {
|
collectGlossaryEntriesForTurn(blocks = [], globalTags = []) {
|
||||||
|
const markupParser = this.getModule('markup-parser');
|
||||||
|
const fromGlobal = markupParser && typeof markupParser.extractGlossaryTags === 'function'
|
||||||
|
? markupParser.extractGlossaryTags(globalTags)
|
||||||
|
: [];
|
||||||
|
const entries = [
|
||||||
|
...fromGlobal,
|
||||||
|
...blocks.flatMap(block => Array.isArray(block?.glossaryEntries) ? block.glossaryEntries : [])
|
||||||
|
];
|
||||||
|
const seen = new Set();
|
||||||
|
return entries.filter((entry) => {
|
||||||
|
const key = `${entry?.term || ''}\u0000${entry?.definition || ''}`;
|
||||||
|
if (!entry?.term || !entry?.definition || seen.has(key)) return false;
|
||||||
|
seen.add(key);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
applyTurnGlossaryEntries(blocks = [], entries = []) {
|
||||||
|
if (!Array.isArray(blocks) || !Array.isArray(entries) || entries.length === 0) return;
|
||||||
|
|
||||||
|
blocks
|
||||||
|
.filter(block => block?.type === 'paragraph' || block?.type === 'heading')
|
||||||
|
.forEach((block) => {
|
||||||
|
block.glossaryEntries = this.mergeGlossaryEntries(block.glossaryEntries, entries);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeGlossaryEntries(...entryLists) {
|
||||||
|
const seen = new Set();
|
||||||
|
return entryLists
|
||||||
|
.flatMap(list => Array.isArray(list) ? list : [])
|
||||||
|
.filter((entry) => {
|
||||||
|
const key = `${entry?.term || ''}\u0000${entry?.definition || ''}`;
|
||||||
|
if (!entry?.term || !entry?.definition || seen.has(key)) return false;
|
||||||
|
seen.add(key);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchChoices(choices, glossaryEntries = []) {
|
||||||
document.dispatchEvent(new CustomEvent('story:choices', {
|
document.dispatchEvent(new CustomEvent('story:choices', {
|
||||||
detail: choices
|
detail: { choices, glossaryEntries }
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class UIDisplayHandlerModule extends BaseModule {
|
|||||||
super('ui-display-handler', 'UI Display Handler');
|
super('ui-display-handler', 'UI Display Handler');
|
||||||
|
|
||||||
// Module dependencies
|
// Module dependencies
|
||||||
this.dependencies = ['layout-renderer', 'playback-coordinator', 'game-config', 'localization', 'story-history', 'sentence-queue', 'persistence-manager'];
|
this.dependencies = ['layout-renderer', 'playback-coordinator', 'game-config', 'localization', 'story-history', 'sentence-queue', 'persistence-manager', 'markup-parser'];
|
||||||
|
|
||||||
// DOM elements
|
// DOM elements
|
||||||
this.container = null;
|
this.container = null;
|
||||||
@@ -146,7 +146,8 @@ class UIDisplayHandlerModule extends BaseModule {
|
|||||||
'displayNextNotification',
|
'displayNextNotification',
|
||||||
'queueTerminalNotification',
|
'queueTerminalNotification',
|
||||||
'flushTerminalNotifications',
|
'flushTerminalNotifications',
|
||||||
'closeNotification'
|
'closeNotification',
|
||||||
|
'renderInlineMarkup'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
console.log('UIDisplayHandler: Constructor initialized');
|
console.log('UIDisplayHandler: Constructor initialized');
|
||||||
@@ -906,8 +907,8 @@ class UIDisplayHandlerModule extends BaseModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
modal.dataset.kind = next.kind;
|
modal.dataset.kind = next.kind;
|
||||||
title.textContent = next.title;
|
title.innerHTML = this.renderInlineMarkup(next.title);
|
||||||
message.textContent = next.message;
|
message.innerHTML = this.renderInlineMarkup(next.message);
|
||||||
if (okButton) {
|
if (okButton) {
|
||||||
okButton.textContent = this.t('popup.ok');
|
okButton.textContent = this.t('popup.ok');
|
||||||
setTimeout(() => okButton.focus(), 0);
|
setTimeout(() => okButton.focus(), 0);
|
||||||
@@ -929,6 +930,17 @@ class UIDisplayHandlerModule extends BaseModule {
|
|||||||
setTimeout(() => this.displayNextNotification(), 0);
|
setTimeout(() => this.displayNextNotification(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderInlineMarkup(text) {
|
||||||
|
const markupParser = this.getModule('markup-parser');
|
||||||
|
if (markupParser && typeof markupParser.markdownToHtml === 'function') {
|
||||||
|
return markupParser.markdownToHtml(String(text || ''));
|
||||||
|
}
|
||||||
|
return String(text || '')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Measure text width using canvas
|
* Measure text width using canvas
|
||||||
* @param {string} text - Text to measure
|
* @param {string} text - Text to measure
|
||||||
@@ -1470,6 +1482,7 @@ class UIDisplayHandlerModule extends BaseModule {
|
|||||||
|
|
||||||
handleHistoryWheel(event) {
|
handleHistoryWheel(event) {
|
||||||
if (!event.target?.closest?.('#page_right') || !this.pageRight) return;
|
if (!event.target?.closest?.('#page_right') || !this.pageRight) return;
|
||||||
|
if (event.target?.closest?.('.story-choices')) return;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
this.handleManualScrollStart('wheel');
|
this.handleManualScrollStart('wheel');
|
||||||
|
|||||||
@@ -289,6 +289,12 @@ export class InkEngine {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!currentText) {
|
||||||
|
previous.tags.push(tag);
|
||||||
|
remainingTags.push(tag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const matchesCurrent = currentText.includes(term);
|
const matchesCurrent = currentText.includes(term);
|
||||||
const matchesPrevious = previousText.includes(term);
|
const matchesPrevious = previousText.includes(term);
|
||||||
if (!matchesCurrent && matchesPrevious) {
|
if (!matchesCurrent && matchesPrevious) {
|
||||||
|
|||||||
Reference in New Issue
Block a user