Räume programmieren

In einem MUD sind Räume das A und O. In den Räumen kann der Spieler forschen, indem er Beschreibungen liest, Details untersucht und Aktionen ausführt. Viele Räume bilden zusammen ein Gebiet, in dem sich der Spieler bewegen kann. In diesem Tutorial erstellen wir einen einfachen Raum.

In LPC sind Räume (wie alles andere im Spiel) Objekte. Jedes Objekt kann beliebig oft repliziert und durch das Setzen von Eigenschaften, den sogenannten Properties, an die eigenen Bedürfnisse angepasst werden. Eines dieser Standardobjekte ist /std/room.c, welches Eigenschaften und Funktionen zum Erstellen von Räumen bereitstellt. Dieses Standardobjekt wird in diesem Dokument verwendet, um einfache Räume zu erstellen. Es gibt aber auch weitere Standardobjekte, die /std/room.c erben und um neue Funktionen erweitern. Dazu gehören zum Beispiel /std/kneipe.c, /std/laden.c und /std/post.c. Auch diese können analog zu den Beispielen verwendet werden.

Vorbereitung

Wenn du deinen ersten Raum erstellt hast, möchtest du ihn sicher testen. Deshalb treffen wir jetzt einige Vorbereitungen. Logge dich mit deinem Programmierer in das Spiel ein und gehe als erstes in deinen Arbeitsraum:

> home

Wechsle nun mit dem Befehl ‚ocd‘ in dein Hauptverzeichnis. Dieser sorgt dafür, dass du dich nun im Verzeichnis des Raumes befindest, in dem sich dein Programmierer befindet.

> ocd

Du kannst auch nachschauen, was sich in deinem Verzeichnis befindet:

> ls

In deinem Verzeichnis siehst du deinen Arbeitsraum (workroom.c), eine Define (def.h) und einige Unterverzeichnisse, in denen du deine Programme thematisch ablegen kannst. Um einen Raum zu erstellen, musst du in das Verzeichnis room/ wechseln:

> cd room

Die folgende Anleitung geht davon aus, dass sich dein Programmierer jetzt in room/ befindet. Mit ‚ls‘ kannst du überprüfen, was sich darin befindet.

Das Editieren von Dateien

Du benötigst zunächst einen Editor, wie zum Beispiel Visual Studio Code, den es für alle relevanten Betriebssysteme gibt. In diesem Editor erstellst du eine neue Datei, z.B. raum1.c und speicherst sie im Unterverzeichnis room/, das sich in deinem Arbeitsverzeichnis befindet.

Die Include-Dateien

In deinem Arbeitsverzeichnis befindet sich bereits eine Header-Datei namens def.h. In dieser Datei sind bereits alle relevanten Header aus /sys/ enthalten. Include-Dateinen und der Präprozessor sind ein eigenes Thema, deswegen wir hier darauf nicht eingegangen. Beginne deinen Raum also mit der Zeile:

#include "def.h"

Erbe die Raumklasse

Für Räume gibt es eine vorgefertigte Klasse (/std/room.c), die alle Funktionen bereitstellt, die ein Raum benötigt. Diese Klasse bindest du ein mit:

inherit "/std/room";

Eigentlich hast du jetzt schon einen fertigen Raum, den du mit ‚goto raum1‘ betreten kannst, wenn dein Programmierer sich im selben Verzeichnis wie der Raum befindet. Außer ein paar Standardeinstellungen enthält er aber noch nicht viel.

Beschreibe den Raum

In LPC heißt die Funktion, die automatisch aufgerufen wird, wenn dein Programm geladen (ausgeführt) wird, ‚create()‘. Wenn du die Programmiersprache C kennst: Dort würde die Einsprungfunktion ‚main()‘ heißen. Man spricht bei einer Funktion, die immer beim Erstellen eines Objekts aufgerufen wird, auch von einem ‚Konstruktor‘.

protected void create() {
  ::create();  // Hier wird das create() der geerbten Klasse room aufgerufen.
  // (hier kommt der weiter unten beschriebene Code rein)
 }

// void:           Die Funktion create() gibt keinen Wert zurück.
// protected: Die Funktion kann nicht von außerhalb des Programms aufgerufen werden.

Alternativ kann man auch schreiben:

protected void create() {
  room::create(); // Da aber nur eine Klasse geerbt wurde, reicht ::create()
                               // ohne die Angabe der Klasse davor.
}

Die Kurzbeschreibung – P_INT_SHORT

Jeder Raum benötigt eine Kurzbeschreibung, da ein Spieler in einen Modus (kurz) wechseln kann, in dem er nur die Kurzbeschreibungen der Räume sieht. Die Kurzbeschreibung wird gesetzt mit:

SetProp(P_INT_SHORT, "Im Schlafzimmer"); // Ohne nachfolgenden Punkt

Die Langbeschreibung – P_INT_LONG

Ein Spieler sieht normalerweise die lange Beschreibung eines Raumes, sobald er ihn betritt oder den Befehl ’schau‘ eingibt. Diese sollte so atmosphärisch wie möglich sein, aber auch nicht zu kurz oder zu lang. Eine Langbeschreibung von 4 bis 6 Zeilen à 78 Zeichen sollte ausreichen.

SetProp(P_INT_LONG,
  "Du bist in Merlins Schlafzimmer. Vor der hinteren Wand steht ein grosses Himmelbett. "
  "Neben dem Bett stehen zwei verzierte Nachttische. Im Westen ist die Tuer. Sie ist "
  "offen.");

Die Details – AddDetail()

Die Details sind bei der Beschreibung des Raums sehr wichtig, denn der Spieler kann sie mit ‚untersuche <detail>‘ erforschen. Sie sollten weder zu lang noch zu kurz sein. Auch sollten langweilige Details vermieden werden, wie zum Beispiel: boden -> Der Boden ist unter deinen Füßen, decke -> Die Decke ist über dir.
Langweiliger kann man es nicht beschreiben und sollte es daher auch nicht tun. Auch das direkte Ansprechen des Spielers in der Beschreibung zerstört die Atmosphäre. Z.B.: loch -> Ich hab dir doch gesagt, da ist kein Loch. (Sich selbst zu verewigen ist meist auch keine gute Idee, denn in 10 Jahren weiß niemand mehr, wer du warst).

AddDetail(({"boden","fussboden"}),
  "Auf dem Boden liegt ein orientalischer Teppich. Die Farben sind so praechtig, dass man "
  "aus dem Staunen nicht mehr herauskommt.");
 
// Alles, was in Großbuchstaben geschrieben ist, sollte beschrieben werden, auch das, was in den Beschreibungen
// steht, und zwar im Plural und Singular oder auch sinnverwandte Begriffe:

AddDetail(({"farben","farbe","teppichfarben","teppichfarbe"}),
"Du siehst alle Farben des Regenbogens!")

Um zu überprüfen, ob du in einem Raumm alles beschrieben hast, gibt es folgenden Befehl für Programmierer: ‚ottest‘. Dein Programmierer muss sich in dem Raum befinden, den du auf fehlende Details testen möchtest.

Gerüche und Geräusche – AddSound() und AddSmell()

Zu einem Raum gehören oft auch Gerüche und Geräusche. Diese kann der Spieler mit den Befehlen „rieche‘ und ‚lausche‘ erkunden.

// DEFAULT_SMELL, wenn bei 'rieche' nichts anderes angegeben wird.
AddSmell(({DEFAULT_SMELL}),
"Hier riecht es nach Pferdeäpfeln.");

// Dies erscheint, wenn der Spieler 'rieche pferdeapfel' eingibt.
AddSmell(({"pferdeaepfel","pferdeapfel"}),
"Du steckst deine Nase in den Pferdeapfel. Igitt! Was für ein Gestank!");

// DEFAULT_SOUND, wenn für 'lausche' nichts angegeben wird.
AddSound(({DEFAULT_SOUND}),
"Ein Vogel zwitschert.");

AddSound(({"vogel","voegel","zwitschern"}),
"Tschiep!");

Das Licht – P_LIGHT

Wenn das Licht aus ist, kann der Spieler ohne Fackel nichts sehen. Natürlich kann man das Licht in Höhlen etc. auch absichtlich ausschalten.

SetProp(P_LIGHT,1); // Licht an: 1, Licht aus: 0

Räume draußen oder im Freien – P_INDOORS

Wenn du auf einer Wiese stehst, bist du meistens draußen, in einem Haus bist du meistens drinnen (es sei denn, das Haus hat kein Dach).

SetProp(P_INDOORS,1); // Drinnen: 1, Draußen: 0

Das Terrain – P_TERRAIN

Die Terrain muss genau definiert werden. Liegt der Raum im Wald? Ist es ein Weg oder eine befestigte Straße? Dafür gibt es die Eigenschaft P_TERRAIN. Diese sollte IMMER gesetzt werden, da die Rassen dies benötigen. Zum Beispiel heilt ein Elf schneller in einem Wald und ein Zwerg besser unter der Erde. Die verschiedenen Terrains können auch kombiniert werden. Die Terrains stehen in /sys/room/terrain.h. Alle Terrains kannst du dir im Spiel mit deinem Programmierer mit folgendem Befehl anschauen:

> xmore /sys/room/terrain.h

Hier ein paar Beispiele:

SetProp(P_TERRAIN, T_FOREST | T_TRACK); // Ein Weg im Wald

SetProp(P_TERRAIN, T_PLAIN | T_ROAD | T_FLOODPLAIN); // Eine Straße durch eine Ebene in einer Flussaue

SetProp(P_TERRAIN, T_BUILDING); // In einem Gebäude

Eine Besonderheit bietet T_AT_WATER. Hier gibt es zwei zusätzliche Eigenschaften (Properties), die das Angeln ermöglichen:

SetProp(P_TERRAIN, T_FOREST | T_AT_WATER);  // Ein Weg an einem Gewässer

// Nun müssen noch die Eigenschaften des Gewässers gesetzt werden, damit das Angeln funktioniert.
// Abhängig von dem Wassertyp und der Temperatur können dann verschiedene Fische
// geangelt werden:

// Die Wasser-Temperatur:
SetProp(P_T_WATERTEMP, TW_COLD);

// TW_COLD:   Kaltes Wasser
// WT_WARM: Warmes Wasser

// Der Wassertyp::
SetProp(P_T_WATERTYPE, TW_LAKE);

// TW_CREEK:  Baeche
// TW_LAKE:     Seen
// TW_RIVER:    Fluesse
// TW_SEA:        Meer / Salzwasser
// TW_POND:    Tuempel

Mit einer Angel (z.B. /obj/angeln/angel.c) wäre jetzt das Angeln in diesem Raum möglich.

Die Eigenschaften des Raumes – P_ENVIRONMENT

Oft ist es wichtig, die Eigenschaften eines Raumes zu kennen. Wie hoch ist die Temperatur? Wie stark ist der Wind? Gib diese Informationen IMMER an, auch wenn DU sie nicht brauchst. Denke daran, dass du an einem Projekt mit vielen Programmierern arbeitest. Je mehr sie über fremde Räume wissen, desto besser können sie NPCs, Objekte usw. schreiben, die für diese Bedingungen geeignet sind.

SetProp(P_ENVIRONMENT, ([
  ENV_TEMPERATURE : 3, // ziemlich kalt…
  ENV_ENVSPEED : 6,        // und sehr windig (Windstaerke 6).
  ENV_ALTITUDE : 2500,  // 2500m hoch…
  ENV_HUMIDITY : 20      // 20% Luftfeuchtigkeit (relativ trocken).
]));

Die Ausgänge – AddExit()

Ein Raum benötigt auch Ausgänge, um in den nächsten Raum zu gelangen. Normalerweise sind dies die Himmelsrichtungen oder bei Treppen und ähnlichem ‚oben‘ und ‚unten‘. In diesem Beispiel würde ein Ausgang nach Osten im Raum erscheinen. Wenn der Spieler nun ‚osten‘ oder ‚o‘ eingibt, wechselt er in den nächsten Raum, in diesem Fall raum2.c. Logischerweise hätte raum2.c einen Ausgang nach Westen, um wieder in den Ausgangsraum zu gelangen.

AddExit("osten", ROOM "raum2");

Fazit

Das war’s. Jetzt speicherst du deinen Raum ab und versuchst ihn zu laden (dabei muss sich dein Programmierer der Einfachheit halber im selben Verzeichnis wie der Raum befinden):

> xload raum1

Wenn alles klappt und keine Fehler auf der ‚work‘-Ebene auftreten, kannst du den Raum betreten mit:

> goto raum1

Das ist natürlich nicht alles, was du mit einem Raum machen kannst. Man kann z.B. mit AddCmd neue Befehle erstellen, automatische Raumnachrichten einfügen usw., aber das würde den Rahmen dieser Anleitung sprengen. Wir raten dir, einen Blick in die Dateien in /d/gebiete/ zu werfen, um mehr über Räume zu lernen.