MIDGARD · Multi User Dungeon Alpha 0.1

Kapitel 10: Vererbung & Mudlib-Design

In diesem Kapitel lernst du, wie du in LPC sauber auf Ereignisse reagierst, ohne Objekte fest miteinander zu verdrahten. Hooks und Events helfen dir dabei, Code wartbar, erweiterbar und „MUD-tauglich“ zu schreiben – besonders im Midgard MUD.

Weiter zu Kapitel 9

Objektkommunikation: call_other(), Rückgabewerte und Nachrichten.

8.1 Alles ist ein Objekt – der „Baukasten“ des MUD

In LPC ist „Objekt“ nicht nur ein Modewort. Ein Objekt ist die grundlegende Einheit, aus der das gesamte Spiel zusammengesetzt ist. Wenn du in einem Raum stehst, dann stehst du nicht in „Text“, sondern in einem Raum-Objekt. Wenn du ein Schwert siehst, dann siehst du ein Schwert-Objekt. Wenn du „nimm schwert“ tippst, werden Funktionen in mehreren Objekten aufgerufen: in dir (Spielerobjekt), im Schwert, im Raum und oft sogar in Standardsystemen der Mudlib.

Ein Objekt besteht grob aus zwei Teilen:

  • Zustand: Variablen/Properties, also gespeicherte Werte (z.B. „geladen?“, „verschlossen?“, „haltbar?“)
  • Verhalten: Funktionen, die etwas tun oder Werte liefern (z.B. short(), long(), move())

Anfängerfehler ist häufig: Man versucht alles „als Text“ zu denken. In LPC ist es besser, alles als „Dinge“ zu denken, die miteinander interagieren. Wenn du das verinnerlichst, wird dir vieles leichter fallen: Inventare, Räume, Ausgänge, Monsterlogik, Quest-Trigger, Effekte – alles sind Objektkontakte.

8.2 Dateien und Objekte – was hat die .c-Datei damit zu tun?

Jede .c-Datei beschreibt einen Objekttyp. Du kannst dir das wie einen Bauplan vorstellen: Die Datei enthält Code, der sagt, wie das Objekt aussieht und was es kann. Aber solange niemand diesen Bauplan benutzt, existiert das Objekt noch nicht „in der Welt“.

Erst wenn die Datei geladen oder daraus ein Objekt geklont wird, entsteht ein echtes Objekt im Speicher des Drivers.

Merksatz: Datei = Bauanleitung, Objekt = gebautes Ding.

8.3 Blueprint vs. Clone – Bauplan und Instanz (bitte wirklich merken)

Dieses Thema ist in LDMud/Midgard MUD zentral. Du wirst es in Debugging, in Tools, in Fehlermeldungen und beim Testen ständig sehen.


// Blueprint (Bauplan, einmal im Speicher)
 /obj/waffen/schwert

// Clone (Instanz, viele möglich)
 /obj/waffen/schwert#12345
      

Der Blueprint ist das geladene Objekt ohne #. Er existiert genau einmal. Ein Clone ist eine Instanz mit eindeutigem Suffix #....

Warum so kompliziert? Weil das effizient ist: Der Code muss nur einmal geladen sein, aber viele Instanzen können existieren. Außerdem kann jede Instanz ihren eigenen Zustand haben.

Praktisches Beispiel:

  • Du legst zwei Schwerter in einen Raum.
  • Beide Schwerter sind Clones derselben Datei.
  • Eines ist verflucht, das andere nicht – das ist Instanzzustand.

Im Mirdgard MUD sind Räume oft Blueprints (ein Raum wird geladen und bleibt), während Items, Monster, Werkzeuge und vieles andere Clones sind.

8.4 Laden und Klonen – wann passiert was?

Es gibt zwei typische „Entstehungsarten“:

  • Laden: Das Objekt wird als Blueprint in den Speicher geholt.
  • Klonen: Aus einem Blueprint wird eine Instanz erzeugt.

Als Anfänger musst du nicht sofort jedes Detail kennen, aber du solltest das Prinzip verstehen: Wenn du ein Monster erzeugst, wird es meistens geklont. Wenn du einen Raum betrittst, wird der Raum meistens geladen (wenn er noch nicht geladen ist).

Typische Efuns (nur zum Verständnis):


object bp = load_object("/obj/waffen/schwert");   // Blueprint laden
object ob = clone_object("/obj/waffen/schwert");  // Clone erzeugen
      

Im MG übernehmen Standardfunktionen und Library-Objekte das oft für dich, aber beim Debuggen ist es Gold wert, zu wissen, ob du gerade mit dem Blueprint oder einem Clone arbeitest.

8.5 create() – Startzustand setzen

create() ist die Initialisierung. Sie wird einmal beim Entstehen aufgerufen. Hier setzt du Properties, IDs und Startwerte.


inherit "/std/thing";
#include <properties.h>

void create() {
  ::create();

  SetProp(P_NAME, "ein Stein");
  SetProp(P_SHORT, "Ein kleiner Stein");
  SetProp(P_LONG, "Ein unscheinbarer grauer Stein.\n");

  AddId("stein");
  AddId("kleiner stein");
}
      

Zwei typische Anfängerfragen:

  • Warum ::create()? — Damit die Elternklasse ihren Teil initialisieren kann.
  • Warum AddId()? — Damit das Objekt im Spiel referenzierbar ist („nimm stein“).

Wenn dein Objekt im Spiel nicht „ansprechbar“ ist, liegt es sehr oft an fehlenden IDs.

8.6 Vererbung – warum du fast nie alles selbst schreibst

Vererbung ist in LPC nicht optionaler Luxus, sondern Standardpraxis. Die Mudlib stellt dir viele Basisklassen bereit, die schon alles Grundlegende können: Inventar verwalten, Properties speichern, Bewegungen prüfen, Standardausgaben erzeugen, und vieles mehr.


inherit "/std/room";
      

Damit bekommst du z.B. für Räume bereits fertige Mechaniken: Beschreibungen, Ausgänge, Reset-Logik, Inventarhandling. Du musst nur noch konfigurieren.

Übliche Basisklassen im Midgard MUD:

  • /std/thing: allgemeine Gegenstände
  • /std/room: Räume
  • /std/living: Lebewesen (Spieler/Monster)
  • /std/weapon, /std/armour: Kampfobjekte

Anfänger-Tipp: Lies in die Standardobjekte rein (mit Tools wie MGtool) – nicht um alles sofort zu verstehen, sondern um ein Gefühl dafür zu bekommen, was „schon da“ ist.

8.7 Funktionen überschreiben und :: benutzen

Wenn du vererbst, kannst du Funktionen überschreiben. Dann zählt deine Version. Das ist sinnvoll, wenn du Standardverhalten ändern willst.


string short() {
  return "Ein seltsam glühender Stein";
}
      

Oft willst du aber nur ergänzen. Dann rufst du die geerbte Version auf:


string short() {
  return ::short() + " (er ist warm)";
}
      

Diese Technik ist ein Kernmuster: „Basisklasse macht die Arbeit, du setzt die Details drauf.“

8.8 this_object(), this_player(), environment() – dein Orientierungssystem

Diese drei Funktionen helfen dir, in der Objektwelt nicht die Orientierung zu verlieren:

  • this_object(): Das Objekt, dessen Code gerade läuft („ich“)
  • this_player(): Der Spieler, der eine Aktion auslöst (z.B. durch einen Befehl)
  • environment(x): Wo x drin ist (Raum, Spieler, Container)

object pl = this_player();
object here = environment(pl);

if (here)
  write("Du bist in: " + object_name(here) + "\n");
      

Sehr wichtig: this_player() kann in Callouts, Hintergrundjobs oder autonomen Objektaktionen auch 0 sein. Deshalb: immer prüfen, wenn du nicht sicher bist.

8.9 Inventar – Objekte in Objekten (und warum das überall auftaucht)

Inventar ist nicht nur „Spielerinventar“. Inventar ist ein allgemeines Konzept: Ein Raum enthält Objekte. Ein Spieler enthält Objekte. Ein Beutel enthält Objekte. Ein Schrank enthält Objekte. Das ist eine verschachtelte Struktur wie ein Baum.


object *inv = all_inventory(this_player());
foreach (object ob : inv)
{
  write(object_name(ob) + "\n");
}
      

Typische Helfer:

  • all_inventory(ob) – alle Objekte darin
  • present("id", ob) – Objekt anhand einer ID finden
  • deep_inventory(ob) – rekursiv, also „auch in Untercontainern“

Anfänger-Tipp: Wenn du „wo ist das Objekt?“ suchst, geh über environment() nach oben, und über all_inventory() nach unten. Das ist dein Navigationssystem.

8.10 move(), remove() und destruct() – bewegen, aufräumen, löschen

Objekte bewegen sich ständig: vom Raum ins Inventar, aus dem Inventar in den Raum, aus dem Raum in einen Container. Diese Bewegung läuft meist über Standardfunktionen der Mudlib, aber du solltest wissen: „Nehmen“ und „Ablegen“ sind technisch Bewegungen.

Das andere Ende ist das Entfernen:

  • remove(): sauberes Aufräumen (Effekte stoppen, Callouts entfernen)
  • destruct(): endgültig aus dem Speicher löschen

void remove() {
  // Hier würdest du eigene Aufräumlogik einbauen.
  ::remove();
}
      

Anfängerfehler: Man zerstört Objekte hart, ohne aufzuräumen. Das führt zu „hängenden“ Effekten oder Callouts, die auf zerstörte Objekte zeigen. Sauberer Stil spart dir Debug-Zeit.

8.11 Vollständiges Beispiel: ein kleines Objekt mit Interaktion

Hier ist ein Gegenstand, den man untersuchen kann. Er zeigt: create(), IDs, Beschreibung und eine einfache Interaktion über add_action(). (Du lernst Commands/Actions je nach Aufbau deines Manuals ausführlicher, aber hier als praxisnahes Objektbeispiel.)


inherit "/std/thing";
#include <properties.h>

void create() {
  ::create();
  SetProp(P_NAME, "ein glühender Stein");
  SetProp(P_SHORT, "Ein glühender Stein");
  SetProp(P_LONG,
    "Der Stein glüht schwach. Vielleicht kannst du ihn genauer betrachten.\n");
  AddId("stein");
  AddId("gluehender stein");
}

void init() {
  ::init();
  add_action("cmd_betrachten", "betrachte");
}

int cmd_betrachten(string str) {
  if (!str || !id(str))
    return 0;

  write("Du hältst den Stein näher ans Gesicht. Er pulsiert warm.\n");
  say(this_player()->Name(WER) + " betrachtet einen glühenden Stein.\n");
  return 1;
}
      

Hinweis: Je nach Mudlib kann das Command-System leicht variieren. Im Midgard MUD ist add_action() Standard für eigene Aktionen an Objekten.

8.12 Typische Anfängerfehler (Checkliste)

  • Kein ::create() → Basisklasse initialisiert nicht korrekt
  • Keine IDs → Objekt ist im Spiel schwer ansprechbar
  • Blueprint/Clone verwechselt → falsches Objekt „bearbeitet“
  • this_player() blind → in Callouts/Autologik kann es 0 sein
  • remove() ignoriert → Effekte/Callouts bleiben „hängen“

Wenn du das als Liste neben dir liegen hast, sparst du dir beim Debuggen viele Stunden.

8.13 Zusammenfassung

Du hast jetzt das wichtigste Grundmodell für LPC-Objekte:

  • Die Welt besteht aus Objekten (nicht aus „Text“)
  • Eine Datei beschreibt einen Typ, der Blueprint ist das geladene Original
  • Ein Clone ist die konkrete Instanz mit eigenem Zustand
  • create() setzt Startwerte; ::create() ist fast immer Pflicht
  • Vererbung ist der Standardweg, um solide Objekte zu bauen
  • Inventar und environment() erklären „wo ist was?“

Im nächsten Kapitel geht es darum, wie Objekte miteinander sprechen und arbeiten: Funktionsaufrufe, Rückgabewerte und Nachrichten an Spieler/Räume. Das ist die Basis für Quests, Interaktion und praktisch jede Spielmechanik.

Ausblick

Bereit für den nächsten Schritt? In Kapitel 9 lernst du, wie du gezielt Funktionen in anderen Objekten aufrufst (und warum das die „Sprache“ der Mudlib ist).

Weiter zu Kapitel 9

call_other(), Rückgabewerte, say()/tell_object() und typische Muster.