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“ für Objekte im MUD — und beide sind
für unterschiedliche Zwecke gedacht. Wer sie verwechselt, baut entweder zu viel
Speicher voll oder bekommt einen einzigen geteilten Zustand, wo eigentlich viele
Instanzen sein sollten.
- Laden (
load_object): Das Objekt wird als
Blueprint in den Speicher geholt — also genau einmal. Beim zweiten Aufruf
bekommst du dasselbe Blueprint zurück, ohne dass etwas Neues erzeugt wird.
- Klonen (
clone_object): Aus einem Blueprint wird eine
neue Instanz erzeugt. Jeder Aufruf liefert ein neues Objekt mit eigenem
Zustand. Mehrfaches Klonen erzeugt mehrere unabhängige Exemplare.
Als Anfänger musst du nicht sofort jedes Detail kennen, aber du solltest das
Prinzip verstehen — und vor allem die typischen Anwendungsfälle:
- Wenn du ein Monster erzeugst, wird es meistens geklont.
Du willst zehn verschiedene Orks haben, jeden mit eigenen Lebenspunkten.
- Wenn du einen Raum betrittst, wird der Raum meistens
geladen (wenn er noch nicht geladen ist). Es gibt nur einen einzigen
„Marktplatz“ — alle Spieler sehen denselben Raum.
- Wenn du einen Daemon brauchst (z.B. Wetterdaemon), wird er
geladen. Es soll nur einen geben.
- Wenn ein Spieler ein Item bekommt (Schwert, Trank), wird es
geklont. Der Spieler hat sein eigenes Schwert mit seinem eigenen Verschleiß.
Typische Efuns (zum Verständnis):
object bp = load_object("/obj/waffen/schwert"); // Blueprint laden
object ob = clone_object("/obj/waffen/schwert"); // Clone erzeugen
// Du erkennst den Unterschied am Namen:
write(object_name(bp)); // "/obj/waffen/schwert"
write(object_name(ob)); // "/obj/waffen/schwert#12345"
// Test-Funktionen:
clonep(ob); // 1 — ist ein Clone
clonep(bp); // 0 — ist Blueprint
Implizites Laden
Du musst load_object oft gar nicht selbst schreiben. Der Driver lädt
Objekte automatisch in folgenden Fällen:
- Bei einem
call_other auf einen Pfad, der noch nicht geladen ist.
- Bei
clone_object, wenn der Blueprint nicht da ist (er wird zuerst geladen,
dann geklont).
- Bei einem
inherit "/std/room"; in deiner Datei — die Eltern-Datei wird
geladen.
Wo passiert das im MG-Alltag?
Im Midgard MUD übernehmen Standardfunktionen und Library-Objekte das oft für dich.
Beispielsweise lädt ein Raum mit AddItem("/obj/foo", REFRESH_DESTRUCT)
beim ersten Reset automatisch das Objekt und klont es. Du selbst rufst
clone_object nur dann, wenn du außerhalb der Standardmechanik
arbeitest.
Beim Debuggen ist es Gold wert, zu wissen, ob du gerade mit dem Blueprint oder
einem Clone arbeitest — daran erkennst du, ob deine Änderungen am richtigen Objekt
ankommen. Eine Faustregel: Wenn der Objektname kein # enthält, ist es
ein Blueprint. Sonst ein Clone.
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
(z.B. [ 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:
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.11a Die wichtigsten Standardobjekte im Midgard MUD
Du programmierst praktisch nie „auf der gruenen Wiese“. Stattdessen erbst du von
einer der Mudlib-Klassen unter /std/. Hier die Kandidaten, die du
wahrscheinlich am häufigsten sehen wirst:
/std/thing – Basisobjekt für alle Dinge (Items, Steine, Bücher, …)
/std/container – Dinge mit Inventar (Beutel, Truhen)
/std/room – Räume (mit Exits, Details, Items)
/std/pub – Kneipenräume mit Speise-/Getraenkesystem
/std/transport – bewegliche Räume (Schiffe, Karren)
/std/living/ – Modul-Verzeichnis: life, combat, attributes, skills, … (NPCs und Spieler erben direkt aus diesen Modulen, es gibt KEIN /std/living.c)
/std/npc – NPCs (mit Chats, Combat, Items)
/std/inpc – intelligenter NPC: Tagesablauf, Routen, Flucht, KI
/std/mnpc – mobile NPCs, die zwischen Räumen wandern
/std/snpc – skalierender NPC: passt sich an Spielerlevel an
/std/weapon – Waffen (P_WC, P_DAM_TYPE, HitFunc)
/std/armour – Ruestungen (P_AC, P_ARMOUR_TYPE, DefendFunc)
/std/clothing – Kleidung ohne Kampfwerte
/std/unit – Units (Geld, Pfeile – ein Objekt = mehrere Stück)
/std/corpse – Leichen mit Zerfall
Konkrete Vererbungsbaeume und Beschreibungen findest du unter
/doc/std/<name>, z.B. /doc/std/thing,
/doc/std/room, /doc/std/snpc.
Beispiel: einfache, aber komplette Datei
// ein Stein, den man untersuchen kann
inherit "/std/thing";
#include <properties.h>
#include <language.h>
protected void create() {
::create();
SetProp(P_NAME, "Stein");
SetProp(P_SHORT, "Ein kleiner Stein");
SetProp(P_LONG, "Ein unscheinbarer grauer Stein.\n");
SetProp(P_GENDER, MALE);
SetProp(P_WEIGHT, 200); // Gramm
AddId(({ "stein", "kleiner stein" }));
AddAdjective("klein");
}
8.11b P_AUTOLOADOBJ – Items überleben den Logout
Im Midgard MUD müssen Items, die ein Spieler über Logout/Reboot behalten soll,
die Property P_AUTOLOADOBJ setzen. Sonst sind sie nach dem nächsten
Login weg.
protected void create() {
::create();
SetProp(P_NAME, "Schwert");
// ... weitere Properties
SetProp(P_AUTOLOADOBJ, 1); // einfachste Variante: einfach erhalten
}
Wenn dein Item interne Variablen merken muss (Charge, Verfluchungsdauer,
Ladezustand), kannst du P_AUTOLOADOBJ mit Set-/Querymethoden
an ein Mapping/Array koppeln, in dem du die Variablen serialisierst.
Siehe /doc/props/P_AUTOLOADOBJ.
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.