MIDGARD · Multi User Dungeon Alpha 0.1

Kapitel 13: Großes Lern- & Praxisbeispiel

Ein umfangreiches, vollständiges Lernbeispiel: Eine Waldlichtung zur Wikingerzeit, in der Spieler Dinge untersuchen, einen Hebel entdecken, ein Versteck öffnen, Items finden (die ins Inventar kommen), mit einem NPC interagieren und über Hooks/Events Erweiterungen möglich sind – alles mit sehr ausführlichen Kommentaren.

Zurück zur Übersicht

Alle Kapitel & Ressourcen

13.1 Ziel dieses Kapitels

Du bekommst hier ein „Mini-Gebiet“ als Lernpaket: ein Raum plus mehrere Objekte. Du kannst die Dateien fast 1:1 in dein Arbeitsverzeichnis kopieren und dann iterativ erweitern. Dabei siehst du viele typische Mudlib-Mechaniken in Aktion:

  • Room-Setup (Beschreibung, Details, Gerüche, Geräusche, Exits)
  • Spieleraktionen: suchen, untersuche, ziehen, schieben, nehmen
  • Ein „geheimes“ Versteck, das erst durch Interaktion zugänglich wird
  • „Finden“-Mechanik: Item wird (sinnvoll) ins Inventar bewegt
  • Ein NPC (Wikinger-Wächter) mit Dialog / Reaktion
  • Ein Hebel-Objekt, das Ereignisse auslöst (und leicht erweiterbar ist)
  • Sauber kommentierter Code: du sollst verstehen, nicht nur kopieren

Hinweis: Die Mudlib im Midgard MUD bietet viele Komfortfunktionen. Da Setups je nach Version/Standardobjekten variieren können, ist das Beispiel bewusst so geschrieben, dass es als Anfänger-„Gerüst“ verständlich bleibt. Wenn deine Mudlib bestimmte Hilfsfunktionen anders nennt, passt du sie im Zweifel minimal an.

Anmerkung: Alle Code-Beispiele sind optimiert für den Desktop. Auf Mobilgeräten kann also die Ansicht verzerrt sein. Aber auf dem Handy programmiert man in der Regel auch nicht, oder doch neuerdings? ;-)

13.2 Dateistruktur (Vorschlag)

Lege dir z.B. folgendes Verzeichnis an:


/players/deinname/lernbereich/wikinger/
  clearing.c          (der Raum)
  lever.c             (der versteckte Hebel / Mechanik-Auslöser)
  stash.c             (das Versteck / Behälter)
  npc_guard.c         (NPC: Wikinger-Wächter)
  items/
    rune_stone.c      (Fundstück: Runenstein)
    bone_needle.c     (Fundstück: Knochennadel)
    torch_oil.c       (nützliches Item: Ölfläschchen)
      

Für Anfänger ist diese Struktur Gold wert: Du findest Dateien schnell wieder, und dein Raum bleibt übersichtlich.

13.3 Der Raum: Waldlichtung zur Wikingerzeit

Diese Datei ist das Herzstück. Sie lädt/platziert den NPC und sorgt dafür, dass Hebel & Versteck vorhanden sind. Außerdem definieren wir Details, die Spieler untersuchen können, und eine Suche-Aktion, die ein „verstecktes“ Interaktionsobjekt erst sichtbar macht.

Datei: clearing.c


// /players/deinname/lernbereich/wikinger/clearing.c
//
// Waldlichtung zur Wikingerzeit – Lernraum
// ------------------------------------------------------------
// Lernziele:
//  - Raum-Setup (Beschreibung, Details, Exits)
//  - Spieleraktionen (suchen, untersuche)
//  - „Entdecken“ eines Hebels
//  - Öffnen eines Verstecks, Items finden und ins Inventar bewegen
//  - NPC einbinden
//
// Hinweis: Einige Mudlib-Funktionen können je nach MG-Stand variieren.
// Das Beispiel ist absichtlich „klar“ und kommentiert – passe es bei Bedarf an.

#pragma strict_types
#pragma no_clone

inherit "/std/room";

#include <properties.h>

#define PATH "/players/deinname/lernbereich/wikinger/"
#define ITEMPATH (PATH+"items/")

// Wir merken uns, ob der Hebel bereits entdeckt wurde.
// Anfänger-Tipp: Solche Zustände gehören in den Raum, wenn sie raumweit sind.
private int lever_discovered;

// Optional: Wir speichern die Objekt-Referenzen, damit wir nicht ständig suchen müssen.
private object lever_ob;
private object stash_ob;

protected void create()
{
  ::create();

  lever_discovered = 0;

  // --- Kurze & lange Beschreibung -----------------------------------------
  SetProp(P_SHORT, "Eine Waldlichtung (Wikingerzeit)");
  SetProp(P_LONG,
    "Du stehst auf einer windgeschützten Waldlichtung. Hohe Fichten rahmen den Ort ein, "
    "und der Boden ist weich von Moos und Nadeln. In der Mitte liegt ein alter Feuerplatz, "
    "umgeben von flachen Steinen. Ein verwitterter Baumstumpf wirkt, als hätte hier oft jemand gesessen. "
    "Zwischen Wurzeln und Farnen sieht man Spuren – nicht nur von Tieren.\n"
    "\n"
    "Für Lernzwecke kannst du hier vieles ausprobieren: untersuchen, suchen, einen Mechanismus finden, "
    "ein Versteck öffnen und sogar ein paar Gegenstände entdecken.\n"
  );

  // --- Details: „untersuche <ding>“ ----------------------------------------
  // Viele Mudlibs bieten AddDetail() / AddReadMsg() / AddSmells() etc.
  // Wenn deine Variante anders heißt, passe nur diese Zeilen an.
  AddDetail(({"feuerplatz","feuerstelle","asche"}),
    "Ein alter Feuerplatz. Die Asche ist kalt, aber zwischen den Steinen steckt noch Ruß. "
    "Vielleicht wurde hier vor gar nicht allzu langer Zeit gekocht.\n");

  AddDetail(({"baumstumpf","stumpf"}),
    "Ein verwitterter Baumstumpf. Die Oberfläche ist glattgesessen. "
    "Am Rand siehst du Kerben – als hätte jemand etwas hinein geritzt.\n");

  AddDetail(({"kerben","ritzungen"}),
    "Die Kerben wirken wie ein grobes Zeichen… vielleicht eine Rune. "
    "Wenn du genauer suchst, könnte es einen Mechanismus geben.\n");

  AddDetail(({"moos","boden","nadeln"}),
    "Moos und Nadelstreu dämpfen jeden Schritt. Hier kann man leicht etwas verlieren… oder verstecken.\n");

  AddDetail(({"farn","farne","wurzeln","wurzel"}),
    "Zwischen Farn und Wurzeln sind Spuren. Manche wirken wie Schuhabdrücke.\n");

  // --- Sinneseindrücke -----------------------------------------------------
  AddSmells(SENSE_DEFAULT,
    "Du riechst Harz, feuchte Erde und einen Hauch von altem Rauch.\n");
  AddSounds(SENSE_DEFAULT,
    "Du hörst leises Rascheln, Vogelrufe und irgendwo das Knacken von Ästen.\n");

  // --- Exits ---------------------------------------------------------------
  // Passe Exits an dein Gebiet an. Der Raum funktioniert auch ohne Exits.
  AddExit("norden", "/players/deinname/lernbereich/wikinger/forest_path1");
  AddExit("sueden", "/players/deinname/lernbereich/wikinger/forest_path2");

  // --- Objekte im Raum -----------------------------------------------------
  // Wir stellen sicher, dass Hebel und Versteck existieren.
  // In vielen Mudlibs gibt es AddItem/ AddObject / AddMon etc.
  // Wir verwenden hier ein simples Muster: beim Reset nachladen, wenn fehlt.
  // Siehe reset() unten.

  // NPC platzieren: Wikinger-Wächter
  AddObject(PATH+"npc_guard", 1);

  // Spieleraktionen registrieren:
  // Diese Aktionen sind bewusst „Anfängerfreundlich“:
  AddCmd(({"suche","suchen"}), "cmd_suchen");
  AddCmd(({"untersuche","untersuchen","betrachte","betrachten"}), "cmd_untersuche");
}

// reset() wird (je nach Mudlib) regelmäßig aufgerufen.
// Hier sorgen wir dafür, dass Objekte da sind, auch wenn jemand sie zerstört hat.
protected void reset()
{
  ::reset();

  if (!lever_ob || !objectp(lever_ob) || environment(lever_ob) != this_object())
  {
    lever_ob = clone_object(PATH+"lever");
    lever_ob->move(this_object());
  }

  if (!stash_ob || !objectp(stash_ob) || environment(stash_ob) != this_object())
  {
    stash_ob = clone_object(PATH+"stash");
    stash_ob->move(this_object());
  }
}

// ------------------------------------------------------------
// Spieleraktion: suchen
// Idee: Spieler können in der Lichtung suchen. Beim ersten Mal entdecken sie,
// dass am Baumstumpf etwas „locker“ ist – der Hebel wird „entdeckt“.
// Danach weist die Suche auf das Versteck hin.
// ------------------------------------------------------------
int cmd_suchen(string str)
{
  object who = this_player();

  // Wenn kein Argument: allgemeine Suche.
  // In vielen MUDs ist „suche“ ohne Parameter okay.
  if (!str || str == "")
  {
    if (!lever_discovered)
    {
      lever_discovered = 1;

      tell_object(who,
        "Du gehst langsam über die Lichtung und untersuchst Baumstumpf, Steine und Wurzeln.\n"
        "Als du mit den Fingern über die Kerben am Baumstumpf fährst, spürst du: Ein Stück Holz sitzt locker.\n"
        "Es wirkt wie ein kleiner, versteckter Hebel.\n");

      tell_room(this_object(),
        who->Name(WER) + " sucht aufmerksam die Lichtung ab und bleibt am Baumstumpf stehen.\n",
        ({ who }));

      // Optional: Hebel-Objekt kann wissen, dass es entdeckt wurde (für Feedback).
      if (lever_ob) lever_ob->SetDiscovered(1);

      return 1;
    }

    tell_object(who,
      "Du suchst erneut, aber das Wichtigste hast du schon entdeckt: den versteckten Hebel am Baumstumpf.\n"
      "Vielleicht solltest du versuchen, ihn zu ziehen oder zu drücken.\n");
    return 1;
  }

  // Suche mit Argument: z.B. „suche im moos“, „suche wurzeln“, …
  str = lower_case(str);

  if (member(({"baumstumpf","stumpf"}), str) >= 0)
  {
    if (!lever_discovered)
    {
      lever_discovered = 1;
      tell_object(who,
        "Du tastest den Baumstumpf sorgfältig ab.\n"
        "Hinter einer Kerbe findest du einen kleinen, getarnten Hebel.\n");
      if (lever_ob) lever_ob->SetDiscovered(1);
      return 1;
    }

    tell_object(who,
      "Du kennst den Trick bereits: Der Hebel ist da.\n");
    return 1;
  }

  if (member(({"moos","boden","nadeln","farn","farne","wurzeln","wurzel"}), str) >= 0)
  {
    tell_object(who,
      "Du wühlst vorsichtig im " + str + ".\n"
      "Du findest nichts Offensichtliches – aber du hast das Gefühl, dass hier etwas versteckt sein könnte,\n"
      "wenn man den richtigen Mechanismus auslöst.\n");
    return 1;
  }

  tell_object(who, "Du suchst danach, aber findest nichts Besonderes.\n");
  return 1;
}

// ------------------------------------------------------------
// Spieleraktion: untersuche
// Für Anfänger: Wir behandeln „untersuche“ so, dass es auch ohne AddDetail()
// verständlich Feedback gibt. AddDetail bleibt trotzdem nützlich.
// ------------------------------------------------------------
int cmd_untersuche(string str)
{
  object who = this_player();

  if (!str || str == "")
  {
    tell_object(who,
      "Was möchtest du untersuchen?\n"
      "Beispiele: untersuche feuerplatz, untersuche baumstumpf, untersuche wurzeln\n");
    return 1;
  }

  str = lower_case(str);

  if (member(({"hebel","versteckter hebel","mechanismus"}), str) >= 0)
  {
    if (!lever_discovered)
    {
      tell_object(who,
        "Du siehst keinen Hebel. Vielleicht musst du erst gründlicher suchen.\n");
      return 1;
    }

    tell_object(who,
      "Der Hebel ist gut getarnt und in die Kerben am Baumstumpf eingelassen.\n"
      "Er wirkt alt, aber funktionstüchtig. Du könntest versuchen, ihn zu ziehen oder zu drücken.\n");
    return 1;
  }

  // Fallthrough: Viele Dinge werden bereits über AddDetail abgefangen.
  // Wenn nicht, geben wir generisches Feedback.
  tell_object(who,
    "Du betrachtest " + str + " genauer, aber findest nichts Weiteres.\n"
    "Tipp: Manche Dinge entdeckt man eher durch 'suche'.\n");
  return 1;
}

// ------------------------------------------------------------
// Hilfsfunktion: Wird vom Hebel aufgerufen, um das Versteck zu öffnen.
// So bleibt der Hebel dumm: Er löst aus, aber der Raum entscheidet, was passiert.
// ------------------------------------------------------------
public void OnLeverTriggered(object who, string mode)
{
  if (!objectp(who)) return;

  if (!stash_ob || !objectp(stash_ob) || environment(stash_ob) != this_object())
  {
    tell_object(who, "Du hörst ein Knacken – aber irgendetwas scheint zu fehlen.\n");
    return;
  }

  // Mode könnte "pull" / "push" sein (siehe lever.c).
  tell_room(this_object(),
    "Im Baumstumpf klickt etwas. Zwischen Wurzeln verschiebt sich Moos, und ein kleines Versteck wird sichtbar.\n");

  stash_ob->OpenStash(who);
}

// Optional: Ein Getter, damit Objekte (oder Debug) sehen können, ob entdeckt.
public int QueryLeverDiscovered() { return lever_discovered; }
      

Wichtiges Lernprinzip hier: Der Raum hält die „Weltlogik“ zusammen. Der Hebel ist nur ein Auslöser, und das Versteck ist nur ein Container. Dadurch wird alles leicht erweiterbar.

13.4 Der Hebel: versteckt, entdeckt, auslösen

Der Hebel ist ein eigenes Objekt. Er ist zunächst „unauffällig“, wird aber durch suche im Raum als entdeckt markiert. Danach kann man ihn benutzen. Bei Benutzung informiert er den Raum: environment()->OnLeverTriggered(...)

Datei: lever.c


// /players/deinname/lernbereich/wikinger/lever.c
//
// Ein versteckter Hebel am Baumstumpf.
// ------------------------------------------------------------
// Idee:
//  - Erst sichtbar/„nutzbar“, wenn entdeckt
//  - Befehle: ziehen, druecken (auch schieben)
//  - Hebel kennt NICHT die konkrete Folge – er informiert den Raum

#pragma strict_types
inherit "/std/thing";

#include <properties.h>

private int discovered;

protected void create()
{
  ::create();

  discovered = 0;

  SetProp(P_SHORT, "Ein unauffälliger Holzhebel");
  SetProp(P_LONG,
    "Ein kleiner, gut getarnter Holzhebel. Er sitzt in einer Kerbe am Baumstumpf.\n"
    "Er wirkt wie Teil eines Mechanismus. Vielleicht kann man ihn ziehen oder drücken.\n");

  AddId(({"hebel","holzhebel","mechanismus"}));

  // Der Hebel soll nicht „herumliegen“ wie ein Loot-Item.
  // Er gehört zum Raum: daher schwer/kein Nehmen.
  SetProp(P_WEIGHT, 999999);
  SetProp(P_NODROP, "Das gehört zur Lichtung, nicht in dein Gepäck.\n");
  SetProp(P_NOGET, "Der Hebel ist fest im Baumstumpf eingelassen.\n");

  // Spielerkommandos:
  AddCmd(({"zieh","ziehen"}), "cmd_ziehen");
  AddCmd(({"drueck","druecken","schieb","schieben"}), "cmd_druecken");
}

public void SetDiscovered(int x)
{
  discovered = x ? 1 : 0;
}

public int QueryDiscovered() { return discovered; }

// Hilfsfunktion: wenn noch nicht entdeckt, reagieren wir passend.
private int deny_if_undiscovered()
{
  if (discovered) return 0;
  tell_object(this_player(),
    "Du siehst hier keinen Hebel. Vielleicht musst du erst genauer suchen.\n");
  return 1;
}

int cmd_ziehen(string str)
{
  if (deny_if_undiscovered()) return 1;

  // Optional: Wenn Spieler keinen Parameter angibt, akzeptieren wir trotzdem.
  // Wenn str vorhanden ist und nicht zum Hebel passt: abbrechen.
  if (str && str != "" && !id(str)) return 0;

  tell_object(this_player(), "Du ziehst vorsichtig an dem versteckten Hebel.\n");
  tell_room(environment(this_object()),
    this_player()->Name(WER) + " zieht an einem versteckten Hebel am Baumstumpf.\n",
    ({ this_player() }));

  // Wichtig: Der Hebel ruft NICHT direkt das Versteck auf!
  // Er informiert den Raum, der entscheidet, was das auslöst.
  if (environment(this_object()))
    environment(this_object())->OnLeverTriggered(this_player(), "pull");

  return 1;
}

int cmd_druecken(string str)
{
  if (deny_if_undiscovered()) return 1;
  if (str && str != "" && !id(str)) return 0;

  tell_object(this_player(), "Du drückst den Hebel hinein. Holz knarzt leise.\n");
  tell_room(environment(this_object()),
    this_player()->Name(WER) + " drückt einen versteckten Hebel am Baumstumpf.\n",
    ({ this_player() }));

  if (environment(this_object()))
    environment(this_object())->OnLeverTriggered(this_player(), "push");

  return 1;
}
      

Anfänger-Regel: Der Hebel kennt keine Quest und keine Tür und kein Versteck. Er ist ein Input-Gerät – und damit bleibt er klein und sauber.

13.5 Das Versteck: Container, der Items ausgibt

Das Versteck ist ein Behälter-Objekt, das (einmalig oder begrenzt) Gegenstände bereitstellt. Wir implementieren:

  • OpenStash(who) – wird vom Raum aufgerufen
  • einmaliges Finden pro Reset (oder pro Zeit)
  • Items werden ins Inventar des Spielers bewegt
  • sauberes Feedback, wenn Inventar voll oder Move scheitert

Datei: stash.c


// /players/deinname/lernbereich/wikinger/stash.c
//
// Versteck zwischen Wurzeln – gibt Lern-Items aus.
// ------------------------------------------------------------
// Lernziele:
//  - Begrenzte Loot-Ausgabe (nicht unendlich farmbar)
//  - Items klonen und ins Inventar bewegen
//  - Fehlerfälle behandeln (Inventar zu voll, Move-Fehler)

#pragma strict_types
inherit "/std/thing";

#include <properties.h>
#include <moving.h>

#define PATH "/players/deinname/lernbereich/wikinger/"
#define ITEMPATH (PATH+"items/")

private int opened;  // wurde das Versteck seit letztem Reset geöffnet?
private int looted;  // wurden Items bereits entnommen?

protected void create()
{
  ::create();

  opened = 0;
  looted = 0;

  SetProp(P_SHORT, "Ein verborgenes Versteck");
  SetProp(P_LONG,
    "Zwischen Moos und Wurzeln ist eine kleine, versteckte Mulde.\n"
    "Sie wirkt wie ein altes Versteck: trocken, sorgfältig abgedeckt.\n");

  AddId(({"versteck","mulde","versteckmulde"}));

  // Auch das Versteck soll nicht als normales Item mitgenommen werden.
  SetProp(P_NOGET, "Das Versteck ist Teil der Lichtung.\n");
  SetProp(P_WEIGHT, 999999);
}

protected void reset()
{
  ::reset();
  opened = 0;
  looted = 0;
}

// Wird vom Raum aufgerufen, wenn der Hebel ausgelöst wurde.
public void OpenStash(object who)
{
  if (!objectp(who)) return;

  if (!opened)
  {
    opened = 1;
    tell_object(who,
      "Das Moos weicht zur Seite. Du entdeckst ein kleines Versteck zwischen den Wurzeln.\n");

    tell_room(environment(this_object()),
      who->Name(WER) + " entdeckt ein Versteck zwischen den Wurzeln.\n",
      ({ who }));
  }

  // Wenn schon geplündert:
  if (looted)
  {
    tell_object(who,
      "Du schaust erneut in das Versteck – es ist leer.\n");
    return;
  }

  // Jetzt geben wir Items aus.
  // Anfänger-Tipp: Nicht zu viel auf einmal. Besser wenige, aber interessante Lernobjekte.
  looted = 1;

  give_item(who, ITEMPATH+"rune_stone");
  give_item(who, ITEMPATH+"bone_needle");
  give_item(who, ITEMPATH+"torch_oil");

  tell_object(who,
    "Du sicherst die gefundenen Dinge und deckst das Versteck wieder zu.\n");
}

// Hilfsfunktion: Item klonen & bewegen, Move-Fehler sauber behandeln.
private void give_item(object who, string file)
{
  object ob;
  int res;

  if (!objectp(who)) return;

  ob = clone_object(file);
  if (!ob)
  {
    tell_object(who, "Irgendetwas stimmt mit einem Fundstück nicht (konnte nicht erzeugt werden).\n");
    return;
  }

  // Move ins Inventar. M_NOCHECK ist manchmal zu aggressiv; wir nutzen normalen Move.
  res = ob->move(who, M_NOCHECK);

  // Viele Mudlibs geben Move-Fehlercodes zurück (ME_*). Wir reagieren freundlich:
  if (res != ME_OK)
  {
    // Wenn nicht ins Inventar, dann in den Raum legen (damit es nicht verschwindet).
    ob->move(environment(this_object()), M_NOCHECK);

    tell_object(who,
      "Du findest " + ob->name(WEN) + ", kannst es aber nicht tragen. Es fällt auf den Boden.\n");
    tell_room(environment(this_object()),
      who->Name(WER) + " lässt etwas zu Boden fallen.\n",
      ({ who }));
  }
  else
  {
    tell_object(who,
      "Du findest: " + ob->name(WEN) + "\n");
  }
}
      

Das ist ein sehr typisches Muster im MG: Wenn ein Spieler etwas findet, versucht man zuerst, es ins Inventar zu bewegen. Wenn das nicht geht, wird es in den Raum gelegt (damit es nicht „weg“ ist).

13.6 Der NPC: Wikinger-Wächter mit Reaktionen

Jetzt kommt Leben in den Raum: Ein NPC, der thematisch passt und zugleich eine Lernfläche bietet: Wie reagiert ein NPC auf bestimmte Spieleraktionen?

Wir machen es absichtlich einfach:

  • Der NPC steht auf der Lichtung
  • Er hat eine kurze Dialogreaktion (z.B. auf „frage“ oder „sage“)
  • Er kommentiert, wenn jemand den Hebel betätigt (über Raum-Events könntest du das später koppeln)

Datei: npc_guard.c


// /players/deinname/lernbereich/wikinger/npc_guard.c
//
// NPC: Wikinger-Wächter (freundlich, aber wachsam)
// ------------------------------------------------------------
// Lernziele:
//  - NPC erstellen
//  - kurze Beschreibung, long, ids
//  - einfache Interaktion über AddCmd / init (je nach Mudlib)

#pragma strict_types
inherit "/std/npc";

#include <properties.h>

protected void create()
{
  ::create();

  SetProp(P_NAME, "Hrafn");
  SetProp(P_SHORT, "Hrafn, ein Wikinger-Wächter");
  SetProp(P_LONG,
    "Hrafn ist ein stämmiger Mann mit wettergegerbtem Gesicht. Er trägt ein altes Kettenhemd und\n"
    "einen Mantel aus grobem Wollstoff. Sein Blick ist ruhig, aber aufmerksam – als würde er alles\n"
    "auf dieser Lichtung zählen: Schritte, Schatten, Atemzüge.\n");

  AddId(({"hrafn","waechter","wikinger","mann"}));

  // Ein bisschen Flavor:
  SetProp(P_RACE, "Mensch");
  SetProp(P_GENDER, MALE);

  // Optional: Level/Stats setzen – je nach Mudlib anders.
  // Als Lern-NPC reicht oft ein harmloser, nicht-aggressiver Charakter.
  SetProp(P_AGGRESSIVE, 0);

  // Chat – wenn deine Mudlib Chat-Mechaniken unterstützt.
  // Sonst kannst du das weglassen.
  SetProp(P_CHAT_CHANCE, 8);
  SetProp(P_CHATS, ({
    "Hrafn schaut über die Lichtung, als würde er etwas erwarten.",
    "Hrafn reibt sich die Hände und zieht den Mantel fester.",
    "Hrafn murmelt: \"Die Götter prüfen uns in kleinen Dingen…\"",
  }));
}

void init()
{
  ::init();

  // Spielerinteraktion: "frage hrafn nach ..."
  // Manche Mudlibs nutzen AddCmd im NPC, andere AddAction in init.
  // Wir zeigen das AddAction-Muster, weil es sehr verbreitet ist.
  add_action("cmd_frage", "frage");
  add_action("cmd_sage", "sage");
}

int cmd_frage(string str)
{
  if (!str || str=="") {
    write("Wen oder was willst du fragen?\n");
    return 1;
  }

  // Einfaches Parsing: "frage hrafn nach hebel"
  // Anfänger-Tipp: Parsing muss nicht perfekt sein – aber klar genug.
  if (strstr(lower_case(str), "hrafn") == -1 && strstr(lower_case(str), "waechter") == -1)
    return 0;

  if (strstr(lower_case(str), "hebel") != -1 || strstr(lower_case(str), "baumstumpf") != -1)
  {
    write("Hrafn sagt: \"Manche Dinge sind besser verborgen. Doch wer Augen hat, findet sie.\" \n");
    return 1;
  }

  if (strstr(lower_case(str), "versteck") != -1 || strstr(lower_case(str), "wurzeln") != -1)
  {
    write("Hrafn sagt: \"Nicht alles, was verborgen ist, ist für dich bestimmt. Aber lernen darf jeder.\" \n");
    return 1;
  }

  write("Hrafn sagt: \"Frag nach dem, was du wirklich verstehen willst.\" \n");
  return 1;
}

int cmd_sage(string str)
{
  // Minimale Reaktion auf "sage hrafn ..."
  if (!str || str=="") return 0;

  if (strstr(lower_case(str), "hrafn") == -1 && strstr(lower_case(str), "waechter") == -1)
    return 0;

  write("Hrafn nickt langsam. \"Worte sind Wind. Taten bleiben.\" \n");
  return 1;
}
      

Der NPC ist bewusst „harmlos“: Er soll nicht töten, sondern Atmosphäre geben. Später kannst du ihn erweitern: Patrouille, Quest, Training, Händlerkontakt usw.

13.7 Items: Runenstein, Knochennadel, Ölfläschchen

Jetzt kommen die Fundstücke. Sie sind bewusst klein, aber zeigen typische Patterns:

  • ein Item mit „lesen“ (Runen)
  • ein Item mit „benutzen“ (Knochennadel)
  • ein Item, das später in anderen Rätseln nützlich sein kann (Öl)

Datei: items/rune_stone.c


// /players/deinname/lernbereich/wikinger/items/rune_stone.c
//
// Runenstein – kann gelesen/untersucht werden.

#pragma strict_types
inherit "/std/thing";

#include <properties.h>

protected void create()
{
  ::create();

  SetProp(P_SHORT, "ein kleiner Runenstein");
  SetProp(P_LONG,
    "Ein handgroßer Stein mit eingeritzten Runen. Die Zeichen sind alt, aber klar.\n"
    "Vielleicht kannst du ihn lesen.\n");

  AddId(({"runenstein","stein","rune"}));
  SetProp(P_WEIGHT, 120);
  SetProp(P_VALUE, 50);

  AddCmd(({"lies","lesen"}), "cmd_lesen");
}

int cmd_lesen(string str)
{
  if (str && str != "" && !id(str)) return 0;

  write(
    "Du entzifferst die Runen langsam:\n"
    "\"Nicht Stärke öffnet jedes Tor, sondern Aufmerksamkeit.\"\n"
    "Vielleicht ist das ein Hinweis für Mechanismen in diesem Lerngebiet.\n");
  return 1;
}
      

Datei: items/bone_needle.c


// /players/deinname/lernbereich/wikinger/items/bone_needle.c
//
// Knochennadel – kleines Tool, zeigt „benutzen“-Interaktion.

#pragma strict_types
inherit "/std/thing";

#include <properties.h>

protected void create()
{
  ::create();

  SetProp(P_SHORT, "eine Knochennadel");
  SetProp(P_LONG,
    "Eine feine Nadel aus Knochen, erstaunlich glatt. Vielleicht wurde sie zum Nähen benutzt.\n"
    "Man könnte damit etwas aus Moos oder Ritzen herauspulen.\n");

  AddId(({"knochennadel","nadel","werkzeug"}));
  SetProp(P_WEIGHT, 30);
  SetProp(P_VALUE, 20);

  AddCmd(({"benutze","nutze","pule"}), "cmd_benutze");
}

int cmd_benutze(string str)
{
  // Sehr einfaches Parsing: „benutze nadel an ...“
  if (!str || str=="") {
    write("Wobei willst du die Nadel benutzen?\n"
          "Beispiel: benutze nadel an moos\n");
    return 1;
  }

  if (strstr(lower_case(str), "nadel") == -1 && strstr(lower_case(str), "knochennadel") == -1)
    return 0;

  if (strstr(lower_case(str), "moos") != -1 || strstr(lower_case(str), "ritze") != -1 || strstr(lower_case(str), "kerbe") != -1)
  {
    write("Du pulst vorsichtig im Moos und in kleinen Ritzen. Vielleicht hilft das beim Suchen.\n");
    return 1;
  }

  write("Du stochert ein wenig herum, aber hier bringt es nichts.\n");
  return 1;
}
      

Datei: items/torch_oil.c


// /players/deinname/lernbereich/wikinger/items/torch_oil.c
//
// Ölfläschchen – kann man „ausgießen“ oder „einölen“ (Lernobjekt für spätere Rätsel).

#pragma strict_types
inherit "/std/thing";

#include <properties.h>

private int uses;

protected void create()
{
  ::create();

  uses = 3;

  SetProp(P_SHORT, "ein kleines Ölfläschchen");
  SetProp(P_LONG,
    "Ein kleines Fläschchen mit dickem Öl. Es riecht nach Harz und Kräutern.\n"
    "Du könntest damit etwas einölen oder Öl ausgießen.\n");

  AddId(({"oel","oelflaeschchen","flaeschchen","öl"}));
  SetProp(P_WEIGHT, 90);
  SetProp(P_VALUE, 35);

  AddCmd(({"giesse","gieße","ausgiessen","ausgießen","oele","öle","einoelen","einölen"}), "cmd_oel");
}

int cmd_oel(string str)
{
  if (uses <= 0) {
    write("Das Fläschchen ist leer.\n");
    return 1;
  }

  // Wenn kein Ziel: einfach etwas Öl auf den Boden tropfen (Flavor)
  if (!str || str=="") {
    uses--;
    write("Du lässt ein wenig Öl auf den Boden tropfen. Es glänzt kurz im Licht.\n");
    return 1;
  }

  // Grobe Zielerkennung: In echten Rätseln würdest du genauer parsen.
  if (strstr(lower_case(str), "hebel") != -1) {
    uses--;
    write("Du ölst den Mechanismus vorsichtig. Das Holz quietscht weniger.\n");
    return 1;
  }

  uses--;
  write("Du nutzt etwas Öl, aber hier hat es keine erkennbare Wirkung.\n");
  return 1;
}
      

13.8 Was kann der Spieler jetzt alles tun?

Mit diesen Dateien kann ein Spieler auf der Lichtung:

  • untersuche feuerplatz / untersuche baumstumpf
  • suche (Hebel entdecken)
  • ziehe hebel oder druecke hebel (Versteck öffnet sich)
  • Items werden gefunden und ins Inventar bewegt
  • lies runenstein
  • benutze nadel an moos
  • oele hebel oder giesse oel aus
  • frage hrafn nach hebel

Damit hast du bereits eine kleine „Gameplay-Schleife“: entdecken → auslösen → loot → interagieren. Genau das ist der Kern vieler MUD-Rätsel – nur später komplexer.

13.9 Erweiterungsideen (für deine nächsten Schritte)

Wenn du das Beispiel verstanden hast, kannst du es ausbauen. Ideen:

  • Hebel funktioniert nur, wenn man vorher den Runenstein gelesen hat (Wissen als Schlüssel)
  • Das Öl verbessert die Erfolgschance (z.B. randomisierte Mechanik: ohne Öl klemmt es manchmal)
  • NPC reagiert auf das Öffnen des Verstecks (z.B. warnt, hilft, stellt Fragen)
  • Versteck enthält manchmal unterschiedliche Items (Loot-Tabelle)
  • Ein zweiter Raum mit „verschlossener Kiste“, die man mit Nadel/Öl öffnen kann
  • Ein kleines Questflag: wer das Versteck einmal gefunden hat, bekommt eine Notiz im Questlog

Tipp: Baue immer nur eine Erweiterung auf einmal ein und teste sie gründlich. Im MUD entstehen Bugs oft durch viele kleine Änderungen gleichzeitig.

13.10 Debugging-Tipps für dieses Beispiel

Wenn etwas nicht klappt, prüfe systematisch:

  • Existieren die Dateien unter dem richtigen Pfad?
  • Wird der Raum korrekt geladen?
  • Wird reset() aufgerufen und klont Hebel/Versteck?
  • Wird der Hebel als entdeckt markiert? (Suche-Logik)
  • Gibt es Tippfehler in Funktionsnamen (OnLeverTriggered)?

Im Midgard MUD hast du dafür Werkzeuge wie die Lupe und das MGtool (wie in Kapitel 2 beschrieben). Nutze sie, um Objekte anzuschauen, Inventare zu prüfen, und Funktionen testweise aufzurufen.

Fertig – und jetzt?

Wenn du möchtest, kann ich als nächstes:

  • eine „Quest-Mini-Mechanik“ dazu schreiben (einfach & lehrreich)
  • eine Loot-Tabelle mit Wahrscheinlichkeiten bauen
  • das Beispiel auf Hooks/Event-System im MG „sauber“ umstellen
  • ein zweites Raum-Puzzle ergänzen (z.B. Runen-Tür oder Opferstein)
Zur Übersicht

Alle Kapitel & Ressourcen