NPCs in Raumbeschreibungen

In diesem Workshop möchte ich beschreiben, wie man andere Personen im Spiel (NPCs, Non Player Characters) elegant in die Raumbeschreibung einbinden kann, ohne dass die Mechanik der Auflistung allzu stark in den Vordergrund tritt. Es geht weder darum, wie man lebendige NPCs implementiert noch um den Raumbeschreibungstext selbst, es geht nur darum, beide zu verknüpfen.

In diesem Artikel wird die Vorgehensweise anhand von Inform mit der deform-Library erklärt. Es liegen drei kleine Beispiele mit Inform-Quelltext vor. Eine T.A.G.-Version ist ebenfalls verfügbar.

* * *

Zunächst einmal allgemeinere Betrachtungen über den Aufbau der Raumbeschreibung, die noch nichts mit NPCs zu tun haben. Eine typische Beschreibung sieht so aus:

Pfad durch Weideland
Dieser schmale Pfad führt von Norden nach Süden durch Weideland; er ist auf der Westseite von einem Zaun gesäumt.

Neben einem der Zaunpfähle liegt ein einzelner roter Wollhandschuh.

Der erste Absatz ist die eigentliche Raumbeschreibung, die mit description (oder Besch) angegeben wird. Ihr wird der Name des Raums vorangestellt. Im knappen Modus sieht man nur diesen Namen.

Alles, was danach kommt, hängt von den Gegenständen in diesem Raum ab. In diesem Fall liegt hier ein Wollhandschuh, der noch nicht bewegt worden ist. Der initial-Satz (oder Erst-Text bei T.A.G.) bringt den Handschuh in seinen Anfangskontext: Offensichtlich wurde der Handschuh verloren. Hätte der Handschuh kein initial, oder wäre er bereits einmal aufgehoben worden, stünde in der Beschreibung:

Du siehst hier einen roten Wollhandschuh.

Der Vorteil dieses Satzes ist, dass er in jedem Raum gültig ist. Als Beschreibung beim ersten Antreffen des Handschuhs finde ich ihn eher fad. Ein initial-Text fügt das Objekt nicht nur besser in die Beschreibung seines Anfangsorts ein, sie hilft dem Spieler auch, zwischen bereits bewegten und neuen Objekten zu unterscheiden: Neue werden in einem eigenen Satz beschrieben; alte, die der Spieler vielleicht nur aus Platzmangel hier abgelegt hat, in einem generischen Satz ganz am Ende.

Meiner Meinung nach sollten auch statische Objekte, die bereits im Haupttext auftauchen, nicht noch einmal in der Auflistung erwähnt werden. Man kann sich überlegen, ob man einzelne Objekte aus dem Text herauszieht und mit einem eigenen initial später erwähnt. Sie einfach mit allen anderen Gegenständen in eine Liste zu packen, ist aber hässlich. Diese an die frühen Scott-Adams-Spiele erinnernden Auflistungen lassen sofort die Objektstruktur erkennen und zerstören die Illusion der Spielwelt:

Pfad durch Weideland
Dieser schmale Pfad führt von Norden nach Süden durch Weideland.

Hier sind ein Weidezaun und ein Handschuh.

In Inform werden auch Personen in die generische Liste der Gegenstände mit aufgenommen, wenn es nicht anders programmiert wird. (In T.A.G. bekommen Personen automatisch eine eigene Zeile, obwohl das auch nicht immer wünschenswert ist, wie wir sehen werden.) Und obwohl in modernen Adventures gerne die Erzählung in den Vordergrund gestellt wird, sieht man gelegentlich Beschreibungen wie:

Eine Tasche auf Rädern steht direkt vor der Alten.

Ein Mädchen ist außerdem hier.

Du siehst hier auch eine Rentnerin, eine orangefarbene Stange, einen Hund, einen Vater und einen Sohn.

Dieses extreme Beispiel, in dem sogar die Bezüge der Objekte aufeinander schief sind, ist aus Linear. (Man muss allerdings sagen, dass das Spiel nur in zwei Straßenbahnwagen spielt und nicht darauf angelegt ist, dass man oft oder überhaupt die Raumbeschreibungen liest.)

* * *

Wenn man in der Raumbeschreibung Personen getrennt von den anderen Objekten im Raum nennen will, sollte man ihnen in Inform ein initial geben. In T.A.G. wird bei Personen automatisch ein eigener Absatz erstellt, der aber generisch ist. Auch hier ist es schöner, einen lebendigeren Satz mit Erst zu definieren.

Eine beliebte Technik, um NPCs nicht statisch wirken zu lassen, ist ihnen mit einem daemon oder each_turn zufällige oder nach einem bestimmten Schema gesteuerte Handlungen ausführen zu lassen. Diese werden dann nach jedem Zug erwähnt.

Beispiel 1: Dieses Beispiel implementiert einen Schmied, der einen fest vorgegebenen Arbeitsablauf hat. Er schmiedet Hufeisen und in jedem Zug führt er einen Schritt durch, der in each_turn beschrieben wird.

Dabei kommt es manchmal zu unschönen Mehrfachnennungen:

Der Schmied ist hier.

Der Schmied holt mit einer Zange einen der glühenden Rohlinge aus dem Ofen.

> u Schmied
Der Schmied ist ein kräftiger, ruhiger Mann.

Der Schmied hämmert mit schweren Schlägen auf dem Rohling herum und biegt ihn so um einen Dorn auf dem Amboss in die Form eines Hufeisens.

In diesem Beispiel werden die Beschreibungen des Schmieds und der Schmiede mit den Arbeitsschritten in jedem Zug zu einem Text zusammengefasst. Dazu wird einfach die Beschreibung des Schmiedens aus initial und description (bzw. Erst und Besch, zu denen man in T.A.G. mit Ausf auch Aktionen anstatt Texten angeben kann) heraus aufgerufen. Eine Flagge unterdrückt dann die Ausgabe in each_turn (bzw. Aktion *):

Der Schmied ist hier. Er holt gerade mit einer Zange einen der glühenden Rohlinge aus dem Ofen.

> u Schmied
Der Schmied ist ein kräftiger, ruhiger Mann. Er hämmert im Augenblick mit schweren Schlägen auf dem Rohling herum und biegt ihn so um einen Dorn auf dem Amboss in die Form eines Hufeisens.

Das ist etwas Arbeit, lässt aber die Sätze natürlicher erscheinen und kaschiert ein wenig die formelle Struktur des Adventures, in der die Aktion und die Dämonen getrennt sind.

* * *

Personen, die zusammen in einem Raum sind, haben oft eine Beziehung zueinander: Sie interagieren und bilden Gruppen. Auch in Textadventures haben die NPCs nicht nur eine Beziehung zum Spieler, sondern auch untereinander. Wie diese NPCs sich untereinander verhalten, sollte auch in der Raumbeschreibung berücksichtigt werden.

Beispiel 2: Eine Mutter steht am Strand und sieht ihrem Kind beim Spielen zu. Das Kind ist manchmal mit der Mutter im selben Raum, manchmal nicht. Wenn beide zusammen sind, sollen sie in einem Block beschrieben werden.

Es ist natürlich nicht schwer, die Texte zu initial oder Erst mit einer Routine bzw. einem Ausf-Block anzupassen, je nachdem, ob beide in einem Raum sind oder nicht.

Das Problem ist hier, die Beschreibung des Kindes zu unterdrücken, wenn es bereits bei der Mutter erwähnt wurde. In T.A.G. erreicht man das, indem man in Erst nichts ausgibt.

In Inform kann man dazu anstatt initial die Eigenschaft describe verwenden, die nicht unbedingt etwas ausgeben muss. Wenn man aus ihr true zurückgibt, ist für den Algorithmus zur Raumbeschreibung bereits alles getan worden, und er behandelt das Kind als bereits beschrieben. Achtung: Im Gegensatz zu initial müssen Umbrüche in describe manuell eingefügt werden.

Eine Flagge ist hier nicht nötig – wie die Beschreibungen ausgegeben werden, ist bereits durch den Aufenthaltsort der NPCs definiert.

* * *

Die Gruppierung von NPCs lässt sich noch weiter treiben:

Beispiel 3: Auf einer Gartenparty sind drei Gruppen von Leuten: Die Verwandten, die Arbeitskollegen und die Kumpels vom Fußballklub. Sie mischen sich nicht untereinander und suchen immer Anschluss an andere Leute ihrer Gruppe, wenn sie neu in einen Raum kommen.

Die Programmierung ist ähnlich wie im zweiten Beispiel. Hier muss allerdings zu jeder Gruppe ein Alpha-NPC bestimmt werden, der die Ausgabe in der Raumbeschreibung übernimmt.

Dieses Beispiel ist wohl eher experimentell. Es zeigt schön, dass viele NPCs auch kein Garant für ein gutes Spiel sind. Schnell verliert man den Überblick, selbst wenn – anders als in diesem Beispiel – die NPCs einige Tiefe haben. Auch die zufällige Bewegung mag für die Implementierung einer Gartenparty in Ordnung sein, in einem ernsthaften Spiel ist es wohl besser zu skripten. (Das ist gewiss mehr Arbeit, aber man spart sich einigen Ärger, da man das Skript so gestalten kann, dass manche NPCs sich nie begegnen.)

* * *

Die Beispiele können hier als Quelltexte für Inform (mit deform-Lib) und T.A.G. heruntergeladen werden: